From e8ce1b33754917bc2aaaa97bade33867c3672271 Mon Sep 17 00:00:00 2001 From: lzwisbadbad Date: Mon, 29 Jun 2020 21:07:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BA=93=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...61\345\205\245\347\220\206\350\247\243.md" | 2559 +++++++++-------- .../01-\345\272\223.md" | 283 ++ 2 files changed, 1713 insertions(+), 1129 deletions(-) create mode 100755 "zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/\345\274\200\345\217\221\345\272\223/01-\345\272\223.md" diff --git "a/zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/03-\346\267\261\345\205\245\347\220\206\350\247\243.md" "b/zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/03-\346\267\261\345\205\245\347\220\206\350\247\243.md" index 9d045bd..fd805a6 100644 --- "a/zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/03-\346\267\261\345\205\245\347\220\206\350\247\243.md" +++ "b/zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/03-\346\267\261\345\205\245\347\220\206\350\247\243.md" @@ -1,1129 +1,1430 @@ -# 深入理解智能合约 - -## 1. 组织机构 - -为BCBChain主链开发的智能合约按照组织的架构进行组织,为同一个组织机构开发的所有智能合约都将被集成在一个合约进程中供用户进行调用。 - -在一个智能合约中可以调用其它智能合约提供的接口,在那个接口对应的智能合约代码中又可以调用另一个智能合约的接口,BCBChain最多支持8级嵌套的跨合约调用,嵌套调用不允许包含环形调用结构。 - -BCBChain设定了一个基础组织,为该基础组织开发的智能合约将被集成到所有组织的合约进程中供用户调用,这个组织的设定主要是为了提供一些可以随时被任意组织跨合约调用执行的基础合约,例如BCBChain主链的基础通证以及代币模板合约。 - - - -## 2. BNF范式 - -本章对合约规范的描述采用巴科斯(BNF)范式。 - -巴科斯范式的英文缩写为BNF,它是以美国人巴科斯(Backus)和丹麦人诺尔(Naur)的名字命名的一种形式化的语法表示方法,用来描述语法的一种形式体系,是一种典型的元语言。又称巴科斯-诺尔形式(Backus-Naur form)。它不仅能严格地表示语法规则,而且所描述的语法是与上下文无关的。它具有语法简单,表示明确,便于语法分析和编译的特点。 - -BNF范式表示语法规则的方式为: - -- 非终结符用尖括号括起。 -- 每条规则的左部是一个非终结符,右部是由非终结符和终结符组成的一个符号串,中间一般以::=分开。 -- 具有相同左部的规则可以共用一个左部,各右部之间以竖线“|”隔开。 - -BNF范式中常用的元字符及其表示的意义如下: - -``` -1. 在双引号中的字符(例如"word")代表着这些字符本身。而double_quote用来代表双引号本身。 -2. 在双引号外的字(有可能有下划线)代表着语法部分。 -3. 尖括号 < > 内包含的为必选项。 -4. 方括号 [ ] 内包含的为可选项。 -5. 大括号 { } 内包含的为可重复0至无数次的项。 -6. 圆括号 ( ) 内包含的所有项为一组,用来控制表达式的优先级。 -7. 竖线 | 表示在其左右两边任选一项,相当于"OR"的意思。 -8. ::= 是“被定义为”的意思。 -9. 空白字符 BNF范式定义中出现的空白字符间隔仅为排版需要,不作为规范的一部分。 -``` - - - -## 3. 合约标记 - -BCBChain智能合约代码采用标记法在合约代码的注释中对合约的元数据进行描述。 - -下面为BCBChain智能合约所使用的各种标记的详细语法描述。 - -### 3.1 contract - -标记```contract```用于标识合约名称,同一组织之下的合约名称必须唯一,请智能合约的开发者进行合理的规划。 - -标记```contract```为必须标记,但在整个合约代码中只能出现一次。 - -标记```contract```的BNF范式定义如下: - -``` - ::= "//@:contract:" <合约名称> -<合约名称> ::= <字母> | <合约名称> <字母数字串> -<字母数字串> ::= <字母> | <十进制数字> | <字母数字串> <字母> | <字母数字串> <十进制数字> -<字母> ::= "_" | "-" | "." | <小写字母> | <大写字母> -<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | - "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | - "y" | "z" -<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | - "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | - "Y" | "Z" -<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" -``` - -标记```contract```之后的有效代码必须紧跟一个包含```sdk.ISmartContract```成员的合约类定义,这个合约类为智能合约的上下文环境,每次针对智能合约的消息调用都会自动创建这个合约类的一个实例,在这个实例上调用智能合约的方法。智能合约对外提供的方法必须属于这个类的成员函数。类名首字母必须大写。 - -示例如下: - -``` -//@:contract:mycoin -type Mycoin struct { - sdk sdk.ISmartContract - - //@:public:store:cache - totalSupply bn.Number - - //@:public:store - balanceOf map[types.Address]bn.Number -} -``` - - - -### 3.2 version - -标记```version```用于标识合约的版本,在整个合约代码中只能出现一次。 - -标记```version```为必须标记,但在整个合约代码中只能出现一次。 - -标记```version```的BNF范式定义如下: - -``` - ::= "//@:version:" <合约版本> -<合约版本> ::= <十进制数> | - <十进制数> "." <十进制数> | - <十进制数> "." <十进制数> "." <十进制数> | - <十进制数> "." <十进制数> "." <十进制数> "." <十进制数> -<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> -<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" -``` - -示例如下: - -``` -//@:version:1.0 -``` - -注:BCBChain链不要求智能合约版本的具体规则,即合约版本既可以采用一段式(例如1、2、3),也可以采用两段式(1.0、1.1、1.2)、三段式(1.0.1、1.0.2、1.0.3)、四段式(1.0.1.102、1.0.1.103),但是同一智能合约的不同版本需要保持版本格式段数的一致性。 - - - -### 3.3 organization - -标记 ```organization``` 用于标识合约所属组织机构的ID。 - -标记 ```organization``` 为必须标记,但在整个合约代码中只能出现一次。 - -标记 ```organization``` 的BNF范式定义如下: - -``` - ::= "//@:organization:" <组织ID> -<组织ID> ::= <前缀码> -<前缀码> ::= "org" - ::= | - ::= <十进制数字> | <大写字母> | <小写字母> -<十进制数字> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" -<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "J" | "K" | "L" | "M" | - "N" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" -<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "m" | - "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | - "z" -``` - -示例如下: - -``` -//@:organization:orgBtjfCSPCAJ84uQWcpNr74NLMWYm5SXzer -``` - - - -### 3.4 author - -标记```author```用于标识合约作者的账户公钥。 - -标记```author```为必须标记,但在整个合约代码中只能出现一次。 - -标记```author```的BNF范式定义如下: - -``` - ::= "//@:author:" <账户公钥> -<账户公钥> ::= <十六进制字符串> -<十六进制字符串> ::= <十六进制数字> | <十六进制字符串> <十六进制数字> -<十六进制数字> ::= "0" | 1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" | - "A" | "B" | "C" | "D" | "E" | "F" | - "a" | "b" | "c" | "d" | "e" | "f" -``` - -示例如下: - -``` -//@:author:b37e7627431feb18123b81bcf1f41ffd37efdb90513d48ff2c7f8a0c27a9d06c -``` - -注:十六进制公钥字符串长度必须等于64。 - - - -### 3.5 constructor - -标记 ```constructor``` 用于标识合约的上链初始化函数。 - -标记 ```constructor``` 之后的有效代码必须紧跟一个名为```InitChain``` 或 ```UpdateChain``` 的无参数函数,该函数将在BCBChain链上第一次部署(```InitChain```)或者升级(```UpdateChain```)这个合约的时候自动进行唯一一次调用,以完成智能合约在区块链上的初始化工作,例如初始化某些全局状态数据的初始值。 - -标记```constructor```为可选标记,但在整个合约代码中最多只能出现两次,一次标识 ```InitChain``` 函数,一次标识 ```UpdateChain```函数。 - -标记```constructor```的BNF范式定义如下: - -``` - ::= "//@:constructor" -``` - -示例如下: - -``` -//@:constructor -func (mc *Mycoin) InitChain() { - ... -} - -//@:constructor -func (mc *Mycoin) UpdateChain() { - ... -} -``` - - - -### 3.6 public:store - -标记 ```public:store``` 用于标识一个状态变量,状态变量必须是通过标记```contract```标注的合约类的成员变量。 - -标记 ```public:store``` 之后的有效代码必须紧跟一个成员变量的定义。 - -标记 ```public:store``` 为可选标记,在合约类定义中可以出现多次,每次标识一个状态变量。 - -标记 ```public:store``` 的BNF范式定义如下: - -``` - ::= "//@:public:store" -``` - -示例如下: - -``` -//@:contract:mycoin -type Mycoin struct { - sdk sdk.ISmartContract - ... - //@:public:store - balanceOf map[types.Address]bn.Number -} -``` - -标记```public:store```标识的状态变量在合约代码中不能直接访问,根据BCBChain合约规范,BCBChain链提供的SDK配套工具会自动生成访问该状态变量的读写函数,示例如下: - -``` -//读取状态变量balanceOf的函数 -func (mc *Mycoin) _balanceOf(k types.Address) bn.Number { - return *mc.sdk.Helper().StateHelper().GetEx( - fmt.Sprintf("/balanceOf/%v", k), &bn.Number{V: big.NewInt(0)}).(*bn.Number) -} - -//检测状态数据balanceOf是否存在的函数 -func (mc *Mycoin) _chkBalanceOf(k types.Address) bool { - return mc.sdk.Helper().StateHelper().Check(fmt.Sprintf("/balanceOf/%v", k)) -} - -//设置状态变量balanceOf的函数 -func (mc *Mycoin) _setBalanceOf(k types.Address, v bn.Number) { - mc.sdk.Helper().StateHelper().Set(fmt.Sprintf("/balanceOf/%v", k), &v) -} - -//从状态数据库中删除状态变量balanceOf的键值的函数 -func (mc *Mycoin) _delBalanceOf(k types.Address) { - mc.sdk.Helper().StateHelper().Delete(fmt.Sprintf("/balanceOf/%v", k)) -} -``` - - - -### 3.7 public:store:cache - -标记```public:store:cache```用于标识一个可缓存在内存的状态变量,状态变量必须是通过标记```contract```标注的合约类的成员变量。 - -标记```public:store:cache```之后的有效代码必须紧跟一个成员变量的定义。 - -标记```public:store:cache```为可选标记,在合约类定义中可以出现多次,每次标识一个状态变量。 - -标记```public:store:cache```的BNF范式定义如下: - -``` - ::= "//@:public:store:cache" -``` - -示例如下: - -``` -//@:contract:mycoin -type Mycoin struct { - sdk sdk.ISmartContract - ... - //@:public:store:cache - totalSupply bn.Number -} -``` - - -可缓存的状态变量在合约代码中不能直接访问,根据BCBChain合约规范,BCBChain提供的SDK配套工具会自动生成访问该状态变量的读写函数,示例如下: - -``` -//读取状态变量totalSupply的函数 -func (mc *Mycoin) _totalSupply() bn.Number { - return *mc.sdk.Helper().StateHelper().McGetEx( - "/totalSupply", &bn.Number{V: big.NewInt(0)}).(*bn.Number) -} - -//检测状态数据totalSupply是否存在的函数 -func (mc *Mycoin) _chkTotalSupply() bool { - return mc.sdk.Helper().StateHelper().McCheck("/totalSupply") -} - -//设置状态变量totalSupply的函数 -func (mc *Mycoin) _setTotalSupply(v bn.Number) { - mc.sdk.Helper().StateHelper().McSet("/totalSupply", &v) -} - -//清除状态变量totalSupply的内存缓存的函数 -func (mc *Mycoin) _clrTotalSupply() { - mc.sdk.Helper().StateHelper().McClear("/totalSupply") -} - -//从状态数据库中删除状态变量totalSupply -func (m *Mycoin) _delTotalSupply() { - m.sdk.Helper().StateHelper().McDelete("/totalSupply") -} -``` - - - -### 3.8 public:receipt - -标记 ```public:receipt``` 用于标识合约中所有收据的定义。 - -标记 ```public:receipt``` 之后的有效代码必须紧跟一个名为 ```receipt``` 的接口定义。 - -标记 ```public:receipt``` 为可选标记,在整个合约代码中最多只能出现一次。 - -标记 ```public:receipt``` 的BNF范式定义如下: - -``` - ::= "//@:public:receipt" -``` - -示例如下: - -``` -//@:public:receipt -type receipt interface { - emitTransferMyCoin(token, from, to types.Address, value bn.Number) -} -``` - -接口```receipt```之中定义的每一个发送收据的方法必须以```emit```开头。```emit```之后的第一个单词首字母转为小写后就是这个收据的名称,可以根据收据名称到BCBChain链上进行检索。根据BCBChain合约规范,BCBChain提供的SDK配套工具会自动生成发送收据函数的实现代码,示例如下: - -``` -//下面的函数是由配套工具自动生成的 -func (mc *Mycoin) emitTransferMyCoin(token, from, to types.Address, value bn.Number) { - type transferMyCoin struct { - Token types.Address `json:"token"` - From types.Address `json:"from"` - To types.Address `json:"to"` - Value bn.Number `json:"value"` - } - mc.sdk.Helper().ReceiptHelper().Emit( - transferMycoin{ - Token: token, - From: from, - To: to, - Value: value, - }) -} - -//下面是发送收据的示例代码,位于Transfer函数的实现当中 -func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { - - //实现转账的业务逻辑代码 - ... - - //发送转账收据 - mc.emitTransferMyCoin( - mc.sdk.Message().Contract().Address(), - sender, - to, - value) -} -``` - - - -### 3.9 public:method - -标记```public:method```用于标识合约的公开方法。 - -标记```public:method```之后的有效代码必须紧跟一个针对通过标记```contract```标注的合约类的成员函数定义(函数名称必须由大写字母开头),这个合约的成员函数可以通过BCBChain链的广播交易来执行。 - -标记```public:method```为可选标记,在整个合约代码中可以出现零次、一次或多次。 - -标记```public:method```的BNF范式定义如下: - -``` - ::= "//@:public:method:gas[" <燃料数量> "]" -<燃料数量> ::= ["-"] <十进制数> -<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> -<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" -``` - -注: - -* 燃料数量为正整数表示该方法调用消耗的燃料费用由交易最初的发起者支付; -* 燃料数量为0表示不需要支付手续费; -* 燃料数量为负整数表示该方法调用消耗的燃料费用由当前智能合约的账户进行支付。 - -示例如下: - -``` -//@:public:method:gas[500] -func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { - ... -} -``` - - - -### 3.10 public:interface - -标记```public:interface```用于标识合约的公开接口。 - -标记```public:interface```之后的有效代码必须紧跟一个针对通过标记```contract```标注的合约类的成员函数定义(函数名称必须由大写字母开头),这个合约的成员函数可以从别的合约通过跨合约调用机制来执行。 - -标记```public:interface```为可选标记,在整个合约代码中可以出现零次、一次或多次。 - -标记```public:interface```的BNF范式定义如下: - -``` - ::= "//@:public:interface:gas[" <燃料数量> "]" -<燃料数量> ::= <十进制数> -<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> -<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" -``` - -示例如下: - -``` -//@:public:interface:gas[450] -func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { - ... -} -``` - -注: - -- 燃料数量大于等于0。 - -标记```public:interface```标记```public:method```可以同时标注合约类的同一个成员函数,上面的例子实际上应该定义如下: - -``` -//@:public:method:gas[500] -//@:public:interface:gas[450] -func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { - ... -} -``` - - - -### 3.11 public:mine - -标记```public:mine```用于标识合约公开的挖矿接口。 - -标记```public:mine```之后的有效代码必须紧跟一个针对通过标记```contract```标注的合约类的成员函数定义(挖矿函数原型必须为:```func Mine() int64```),这个挖矿函数将在 BCBChain 的区块达成共识后自动执行。 - -标记```public:mine```为可选标记,在整个合约代码中可以出现零次或一次。只有 BCBChain 的基础组织的智能合约才允许包含挖矿接口。 - -标记```public:mine```的BNF范式定义如下: - -``` - ::= "//@:public:mine" -``` - -示例如下: - -``` -//@:public:mine -func (mc *Mycoin) Mine() int64 { - ... -} -``` - -注: - -- 挖矿函数 ```Mine()``` 只能与标记 ```public:mine``` 进行配合。 - - - - -### 3.12 import - -标记```import```用于导入一个外部合约的接口原型,方便在当前合约中调用外部合约。 - -标记```import```为可选标记,在整个合约代码中可以出现多次,每次导入一个外部合约的跨合约调用接口,每一个外部合约只能导入一次。 - -标记```import```的BNF范式定义如下: - -``` - ::= "//@:import:" <合约名称> -<合约名称> ::= <字母> | <合约名称> <字母数字串> -<字母数字串> ::= <字母> | <十进制数字> | <字母数字串> <字母> | <字母数字串> <十进制数字> -<字母> ::= "_" | "-" | "." | <小写字母> | <大写字母> -<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | - "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | - "y" | "z" -<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | - "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | - "Y" | "Z" -<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" -``` - -以前面的```mycoin```代币合约为例,如果在其它合约中需要调用该合约,需要编写如下代码: - -``` -//本段代码在合约mycontract中 - -//@:import:mycoin -type mycoin interface { - Transfer(to types.Address, value bn.Number) -} -``` - -外部合约的接口名称自定义,只要遵循 ```golang``` 语法规范即可。 - -外部合约的接口在合约代码中不能直接访问,根据BCBChain合约规范,BCBChain提供的 ```SDK``` 配套工具会自动生成访问该外部合约接口的实现函数,自动生成的代码示例如下: - -``` -//本段代码是由SDK配套工具自动生成的 - -//mycoin This is method of MyContract -func (m *MyContract) mycoin() *InterfacemycoinStub { - return &InterfacemycoinStub{} -} - -//Transfer This is a method of InterfacemycoinStub -func (is *InterfacemycoinStub) Transfer(to types.Address, value bn.Number) { - return -} - -//run Transfer some receipts to destination contract -func (is *InterfacemycoinStub) run(f func()) (*InterfacemycoinStub) { - ... - f() - ... - return is -} - -//contract Wrap the destination contract information -func (is *InterfacemycoinStub) contract() IContract { - ... - return contract -} -``` - -下面是在合约代码中调用外部合约的示例代码: - -``` -//本段代码在合约mycontract中 - -func (m *MyContract)TransferTest(to types.Address, value bn.Number) { - m.mycoin().Transfer(to, value) -} -``` - -跨合约调用还支持向被调用合约传递收据,以下为示例代码(从合约 ```contract2``` 中调用合约 ```contract1``` 的接口): - -> 合约1,提供跨合约接口服务: -> -> ``` -> //@:contract:contract1 -> type C1 struct { -> sdk sdk.ISmartContract -> ... -> } -> -> //@:public:interface:gas[450] -> func (c *C1) Register() { -> -> //I need receipt from caller -> transfers := dw.sdk.Message().GetTransferToMe() -> sdk.Require(transfers!=nil && len(transfers)==1, -> types.ErrInvalidParameter, "Please transfer to me first") -> -> token := transfers[0].Token -> value := transfers[0].Value -> -> ... -> } -> ``` -> -> 合约2,跨合约调用者: -> -> ``` -> //@:contract:contract2 -> type C2 struct { -> sdk sdk.ISmartContract -> ... -> } -> -> //@:import:contract1 -> type mycoin interface { -> Register() -> } -> -> //@:public:method:gas[450] -> func (c *C2) Register() { -> ... -> c.contract1().run(func(){ -> c.sdk.Message().Contract().Account.TransferByName( -> "bcb", -> c.contract1().contract().Account().Address(), -> bn.N(1000000000), -> ) -> }).Register() -> ... -> } -> ``` - - - -## 4. 合约规范 - -### 4.1 BNF范式定义 - -综合上面对合约标记的描述,下面定义合约规范的 ```BNF``` 范式: - -``` -<智能合约> ::= <合约定义代码文件> {<合约实现代码文件>} {<合约测试代码文件>} - -<合约定义代码文件> ::= <代码包定义> <合约定义代码> -<合约实现代码文件> ::= <代码包定义> <合约实现代码> -<合约测试代码文件> ::= <代码包定义> <合约测试代码> -<代码包定义> ::= "package" <合约包名> -<合约包名> ::= 遵循glang语法规范,但不能为 std -<合约定义代码> ::= "import (" - <合约SDK包根路径> - {[包别名] <合约支撑代码包路径>} - ")" - <合约类定义> - [<合约上链初始化函数定义>] - [<挖矿定义>] - [<合约收据定义>] - {<跨合约调用接口定义>} - {<合约公开函数定义>} - {<合约实现代码>} -<合约SDK包根路径> ::= double_quote "blockchain/smcsdk/sdk" double_quote -<合约支撑代码包路径> ::= 遵循golang代码包路径规范,遵循BCBChain合约规范的白名单与灰名单规范 -<包别名> ::= 遵循golang代码规范的代码包别名,不允许使用 '.' -<合约实现代码> ::= 遵循golang代码规范的合约实现代码(不需要BCBChain合约标记的代码),包括类型定义、 - 常量定义、函数定义(注:不能包含全局变量定义,不允许使用 for 关键字,不允许递归 - 调用) -<合约测试代码> ::= 遵循golang单元测试规范的测试代码 - -<合约类定义> ::= "//@:contract:" <合约名称> - "//@:version:" <合约版本> - "//@:organization:" <组织ID> - "//@:author:" <账户公钥> - "type " <合约类名> " struct {" - " sdk sdk.ISmartContract" - {<状态变量定义>} - {} - "}" -<合约名称> ::= 参见<合约标记:contract> -<合约版本> ::= 参见<合约标记:version> -<组织ID> ::= 参见<合约标记:organization> -<账户公钥> ::= 参见<合约标记:author> -<合约类名> ::= <大写字母开头的标识符> - ::= 遵循golang代码规范的标准变量定义代码 -<状态变量定义> ::= <基本状态变量定义> | <带缓存的状态变量定义> -<基本状态变量定义> ::= "//@:public:store" - <变量名称> ["*"] <变量类型> -<带缓存的状态变量定义> ::= "//@:public:store:cache" - <变量名称> ["*"] <变量类型> -<变量名称> ::= <标识符> -<变量类型> ::= <元数据类型> | <数组类型> | <映射表类型> - -<合约上链初始化函数定义> ::= <部署函数> | <升级函数> -<部署函数> ::= "//@:constructor" - "func (" <合约对象定义> ") InitChain() {" - <上链代码> - "}" -<升级函数> ::= "//@:constructor" - "func (" <合约对象定义> ") UpdateChain() {" - <上链代码> - "}" -<挖矿定义> ::= "//@:public:mine" - "func (" <合约对象定义> ") Mine() int64 {" - <挖矿代码> - "}" -<合约对象定义> ::= <变量名称> "*" <合约类名> -<上链代码> ::= - 注1:只允许访问状态变量 - 注2:sdk中不允许访问Message()和Tx() -<挖矿代码> ::= - 注1:sdk中不允许访问Message()和Tx() - ::= 遵循golang代码规范的函数体实现代码,参见 <合约实现代码> 的定义 - -<合约收据定义> ::= "//@:public:receipt" - "type receipt interface {" - <收据函数名称> <函数入口参数定义> - "}" -<收据函数名称> ::= "emit" <大写字母开头的标识符> -<函数入口参数定义> ::= "(" <参数表> ")" -<参数表> ::= <参数定义> | <参数表> "," <参数定义> -<参数定义> ::= <变量名称> ["*"] <变量类型> - -<跨合约调用接口定义> ::= "//@:import:" <合约名称> - "type "<接口类名称>" interface {" - <接口函数名称> <函数入口参数定义> <函数返回定义> - "}" -<接口类名称> ::= <大写字母开头的标识符> -<接口函数名称> ::= <大写字母开头的标识符> -<函数返回定义> ::= <空> | ["*"] <变量类型> | "(" <返回表> ")" -<空> ::= 空白 -<返回表> ::= <返回定义> | <返回表> "," <返回定义> -<返回定义> ::= [<变量名称>] ["*"] <变量类型> - -<合约公开函数定义> ::=[<合约公开方法标记>] - [<合约公开接口标记>] - "func (" <合约对象定义> ")" <公开函数名称> <函数入口参数定义> <函数返回定义> "{" - - "}" -<合约公开方法标记> ::= "//@:public:method:gas[ "<燃料数量> "]" -<合约公开接口标记> ::= "//@:public:interface:gas[ "<燃料数量> "]" -<燃料数量> ::= 参见<合约标记:public:method> -<公开函数名称> ::= <大写字母开头的标识符> - -<标识符> ::= <字母> | <标识符> <字母数字串> -<大写字母开头的标识符> ::= <大写字母> | <大写字母开头的标识符> <字母数字串> -<字母数字串> ::= <字母> | <十进制数字> | <字母数字串> <字母> | <字母数字串> <十进制数字> -<字母> ::= "_" | <小写字母> | <大写字母> -<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | - "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | - "y" | "z" -<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | - "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | - "Y" | "Z" -<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" -<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> - -<元数据类型> := | | <自定义数据结构> - ::= "int" | "int8" | "int16" | "int32" | "int64" | - "uint" | "uint8" | "uint16" | "uint32" | "uint64" | - "bool" | "string" | "byte" - ::=
| | | | -
::= "types.Address" - ::= "types.HexBytes" - ::= "types.Hash" - ::= "types.PubKey" - ::= "bn.Number" -<自定义数据结构> ::= "type " <标识符> " struct {" - {<变量名称> ["*"] <变量类型>} - "}" -<数组类型> ::= <数组维度定义> [<数组维度定义>] <元数据类型> -<数组维度定义> ::= "[" [<十进制数>] "]" -<映射表类型> ::= <映射表类型1> | <映射表类型2> -<映射表类型1> ::= <映射表定义> [<映射表定义>] <元数据类型> -<映射表类型2> ::= <映射表定义> [<映射表定义>] <数组类型> -<映射表定义> ::= "map[" <元数据类型> "]" -``` - - - -### 4.2 包结构规范 - -下面定义合约包结构的 ```BNF``` 范式: - -``` -<智能合约包结构> ::= <合约定义代码文件> {<合约实现代码文件>} {<合约测试代码文件>} -<合约定义代码文件> ::= <合约代码目录> "/" -<合约实现代码文件> ::= <合约代码目录> "/" -<合约测试代码文件> ::= <合约代码目录> "/" -<合约代码目录> ::= "/" <合约根目录> "/" <合约名称> "/v" <合约版本> "/" <合约名称> - ::= golang标准环境变量GOPATH指向的目录 -<合约根目录> ::= "src/contract/" <组织ID> "/code" -<组织ID> ::= 参见<合约标记:organization> - ::= <合法文件名> ".go" - ::= <合法文件名> "_test.go" -<任意文件名> ::= <合法文件名>|<非法文件名> -<非法文件名> ::= [<合法文件名>] "autogen" [<合法文件名>] | <任意文件名> "_test" -<合法文件名> ::= 排除<非法文件名>以后所有操作系统认可作为文件名的字符串 -``` - -注:规范定义中出现的空白符间隔仅为排版需要,不作为规范的一部分。 - - - -### 4.3 白名单列表 - -出于安全考虑,BCBChain智能合约限制为只允许导入行为明确且不会导致不同节点运行结果不一致的代码支撑包。这样的包包含在白名单列表中。 - -白名单列表中支持的代码包在智能合约中可以放心的使用。 - -白名单与灰名单的具体内容随智能合约 ```SDK``` 及配套的BCBChain插件版本升级将会有所不同,详细信息请参见智能合约 ```SDK``` 及配套的BCBChain插件的相关文件。 - -下面是第一版白名单列表: - -``` -//以下为可用的 golang 标识包 -bytes -container/heap -container/list -container/ring -crypto -crypto/aes -crypto/cipher -crypto/des -crypto/hmac -crypto/md5 -crypto/rc4 -crypto/sha1 -crypto/sha256 -crypto/sha512 -encoding -encoding/ascii85 -encoding/asn1 -encoding/base32 -encoding/base64 -encoding/binary -encoding/csv -encoding/gob -encoding/hex -encoding/json -encoding/pem -encoding/xml -errors -fmt -hash -hash/adler32 -hash/crc32 -hash/crc64 -hash/fnv -index/suffixarray -math -math/big -math/bits -math/cmplx -reflect -regexp -regexp/syntax -sort -strconv -strings -unicode -unicode/utf16 -unicode/utf8 - -//以下为 SDK 提供的标准包 -blockchain/smcsdk/sdk -``` - - - - -## 5. BRC20代币 - -本文档概述中发布的智能合约 “代币” 是一个演示性的代币合约,不是符合BCBChain标准的 ```BRC20``` 代币。 - -BCBChain的用户可以自己编写智能合约发行符合BRC20代币规范的代币。本节定义符合 ```BRC20``` 代币规范的智能合约。 - -规范描述如下: - -* 合约必须明确调用智能合约SDK提供的接口```ITokenHelper::RegisterToken(...)```向BCBChain注册一个新的代币(每个智能合约能且只能注册一个代币); - -* 合约注册代币会自动在BCBChain上记录一条标准的代币生成收据,收据定义如下: - - ``` - import ( - "blockchain/smcsdk/sdk/bn" - "blockchain/smcsdk/sdk/types" - ) - - // Name of receipt: std::newToken - type NewToken struct { - TokenAddress types.Address `json:"tokenAddr"` // 代币地址 - ContractAddress types.Address `json:"contractAddr"` // 代币的合约地址 - Owner types.Address `json:"owner"` // 代币拥有者的外部账户地址 - Name string `json:"name"` // 代币名称 - Symbol string `json:"symbol"` // 代币符号 - TotalSupply bn.Number `json:"totalSupply"` // 代币总供应量(单位:cong) - AddSupplyEnabled bool `json:"addSupplyEnabled"`// 代币是否支持增发 - BurnEnabled bool `json:"burnEnabled"` // 代币是否支持燃烧 - GasPrice int64 `json:"gasPrice"` // 代币燃料价格(单位:cong) - } - ``` - -* 合约必须实现本章定义的方法与接口。 - - - -### 5.1 Transfer - -**方法原型** - -``` -import ( - "blockchain/smcsdk/sdk/bn" - "blockchain/smcsdk/sdk/types" -) - -//@:public:method:gas[500] -//@:public:interface:gas[450] -Transfer(types.Address,bn.Number) -``` - -**功能说明** - -- 执行代币转移功能。 - -**实现说明** - -- 必须实现; -- 必须标记为```public:method```; -- 必须标记为```public:interface```。 - -**调用说明** - -- 允许任何人通过广播交易进行调用; -- 允许同组织的其它智能合约进行跨合约调用; -- 允许同组织的其它智能合约通过SDK提供的接口```IAccount::Transfer```直接调用(```SDK``` 底层自动进行跨合约调用)。 - -**输入参数** - -- _to types.Address 代币接收方地址(可以是外部账户地址、合约账户地址、合约地址) - - _value bn.Number 转移的代币数额(单位:```cong```) - - 注:如果接收方地址是合约地址,会自动将接收到的代币转入该合约的账户地址。 - -**输出收据** - -- BRC20代币 ```Transfer() ``` 方法会自动在BCBChain上记录一条标准的代币转移收据,收据定义如下: - - ``` - import ( - "blockchain/smcsdk/sdk/bn" - "blockchain/smcsdk/sdk/types" - ) - - // Name of receipt: std::transfer - type Transfer struct { - Token smc.Address `json:"token"` // 代币地址 - From smc.Address `json:"from"` // 转出方地址 - To smc.Address `json:"to"` // 代币接收方地址 - Value bn.Number `json:"value"` // 转移的代币数额((单位:cong) - } - ``` - -### 5.2 AddSupply - -**方法原型** - -``` -import ( - "blockchain/smcsdk/sdk/bn" -) - -//@:public:method:gas[2400] -AddSupply(bn.Number) -``` - -**功能说明** - -- 为代币增加供应量。 - -**实现说明** - -- 可选实现; -- 如果实现,必须标记为```public:method```。 - -**调用说明** - -- 只有代币拥有者才能通过广播交易进行调用。 - -**输入参数** - -- _value bn.Number 新增加的供应量(单位:```cong```)。 - -**输出收据** - -- BRC20代币 ```AddSupply() ``` 方法会自动在BCBChain上记录一条标准的代币增发收据,收据定义如下: - - ``` - import ( - "blockchain/smcsdk/sdk/bn" - "blockchain/smcsdk/sdk/types" - ) - - // Name of receipt: std::addSupply - type AddSupply struct { - Token types.Address `json:"token"` // 代币地址 - Value bn.Number `json:"value"` // 增发的供应量(单位:cong) - TotalSupply bn.Number `json:"totalSupply"` // 新的总供应量(单位:cong) - } - ``` -- BRC20代币 ```AddSupply()``` 方法还会自动在BCBChain上记录一条标准的```std::transfer```收据。 - - - -### 5.3 Burn - -**方法原型** - -``` -import ( - "blockchain/smcsdk/sdk/bn" -) - -//@:public:method:gas[2400] -Burn(bn.Number) -``` - -**功能说明** - -- 燃烧代币的供应量。 - -**实现说明** - -- 可选实现; -- 如果实现,必须标记为```public:method```。 - -**调用说明** - -- 只有代币拥有者才能通过广播交易进行调用。 - -**输入参数** - -- _value bn.Number 燃烧掉的供应量(单位:```cong```)。 - -**输出收据** - -- BRC20代币 ```Burn()``` 方法会自动在BCBChain上记录一条标准的代币燃烧收据,收据定义如下: - - ``` - import ( - "blockchain/smcsdk/sdk/bn" - "blockchain/smcsdk/sdk/types" - ) - - // Name of receipt: std::burn - type Burn struct { - Token types.Address `json:"token"` // 代币地址 - Value bn.Number `json:"value"` // 燃烧的供应量(单位:cong) - TotalSupply bn.Number `json:"totalSupply"` // 新的总供应量(单位:cong) - } - ``` -- BRC20代币 ```Burn()``` 方法还会自动在BCBChain上记录一条标准的```std::transfer```收据。 - - - -### 5.4 SetGasPrice - -**方法原型** - -``` -//@:public:method:gas[2400] -SetGasPrice(int64) -``` - -**功能说明** - -- 设置燃料价格。 - -**实现说明** - -- 可选实现; -- 如果实现,必须标记为```public:method```。 - -**调用说明** - -- 只有代币拥有者才能通过广播交易进行调用。 - -**输入参数** - -- _value int64 新的燃料价格(单位:```cong``` )。 - -**输出收据** - -- BRC20代币 ```SetGasPrice() ``` 方法会自动在BCBChain上记录一条标准的设置燃料价格收据,收据定义如下: - - ``` - import ( - "blockchain/smcsdk/sdk/types" - ) - - // Name of receipt: std::setGasPrice - type SetGasPrice struct { - Token types.Address `json:"token"` // 代币地址 - GasPrice int64 `json:"gasPrice"` // 燃料价格(单位:cong) - } - ``` - -### 5.5 SetOwner - -**方法原型** - -``` -import ( - "blockchain/smcsdk/sdk/types" -) - -//@:public:method:gas[2400] -SetOwner(types.Address) -``` - -**功能说明** - -- 转移智能合约及代币的所有权。 - -**实现说明** - -- 可选实现; -- 如果实现,必须标记为```public:method```。 - -**调用说明** - -- 只有代币拥有者才能通过广播交易进行调用。 - -**输入参数** - -- _newOwner types.Address 合约新的拥有者的外部账户地址。 - -**输出收据** - -- BRC20代币 ```SetOwner()``` 方法会自动在BCBChain上记录一条标准的转移所有权收据,收据定义如下: - - ``` - import ( - "blockchain/smcsdk/sdk/types" - ) - - // Name of receipt: std::setOwner - type SetOwner struct { - ContractAddr types.Address `json:"contractAddr"` // 智能合约地址 - NewOwner types.Address `json:"newOwner"` // 合约新的拥有者的外部账户地址 - } - ``` -- BRC20代币 ```SetOwner()``` 方法还会自动在BCBChain上记录一条标准的```std::transfer```收据。 +# 深入理解智能合约 + +## 1. 组织机构 + +为BCBChain主链开发的智能合约按照组织的架构进行组织,为同一个组织机构开发的所有智能合约都将被集成在一个合约进程中供用户进行调用。 + +在一个智能合约中可以调用其它智能合约提供的接口,在那个接口对应的智能合约代码中又可以调用另一个智能合约的接口,BCBChain最多支持8级嵌套的跨合约调用,嵌套调用不允许包含环形调用结构。 + +BCBChain设定了一个基础组织,为该基础组织开发的智能合约将被集成到所有组织的合约进程中供用户调用,这个组织的设定主要是为了提供一些可以随时被任意组织跨合约调用执行的基础合约,例如BCBChain主链的基础通证以及代币模板合约。 + + + +## 2. BNF范式 + +本章对合约规范的描述采用巴科斯(BNF)范式。 + +巴科斯范式的英文缩写为BNF,它是以美国人巴科斯(Backus)和丹麦人诺尔(Naur)的名字命名的一种形式化的语法表示方法,用来描述语法的一种形式体系,是一种典型的元语言。又称巴科斯-诺尔形式(Backus-Naur form)。它不仅能严格地表示语法规则,而且所描述的语法是与上下文无关的。它具有语法简单,表示明确,便于语法分析和编译的特点。 + +BNF范式表示语法规则的方式为: + +- 非终结符用尖括号括起。 +- 每条规则的左部是一个非终结符,右部是由非终结符和终结符组成的一个符号串,中间一般以::=分开。 +- 具有相同左部的规则可以共用一个左部,各右部之间以竖线“|”隔开。 + +BNF范式中常用的元字符及其表示的意义如下: + +``` +1. 在双引号中的字符(例如"word")代表着这些字符本身。而double_quote用来代表双引号本身。 +2. 在双引号外的字(有可能有下划线)代表着语法部分。 +3. 尖括号 < > 内包含的为必选项。 +4. 方括号 [ ] 内包含的为可选项。 +5. 大括号 { } 内包含的为可重复0至无数次的项。 +6. 圆括号 ( ) 内包含的所有项为一组,用来控制表达式的优先级。 +7. 竖线 | 表示在其左右两边任选一项,相当于"OR"的意思。 +8. ::= 是“被定义为”的意思。 +9. 空白字符 BNF范式定义中出现的空白字符间隔仅为排版需要,不作为规范的一部分。 +``` + + + +## 3. 合约标记 + +BCBChain智能合约代码采用标记法在合约代码的注释中对合约的元数据进行描述。 + +下面为BCBChain智能合约所使用的各种标记的详细语法描述。 + +### 3.1 contract + +标记```contract```用于声明合约类型为合约,以及标识合约名称,同一组织之下的合约名称必须唯一,请智能合约的开发者进行合理的规划。 + +标记```contract```与```library```为必须标记,在整个合约代码中只能出现一次,要么出现一次`contract`标记,要么出现一次`library`标记。 + +标记为`contract`的合约不允许使用`public:function`标签。 + +标记```contract```的BNF范式定义如下: + +``` + ::= "//@:contract:" <合约名称> +<合约名称> ::= <字母> | <合约名称> <字母数字串> +<字母数字串> ::= <字母> | <十进制数字> | <字母数字串> <字母> | <字母数字串> <十进制数字> +<字母> ::= "_" | "-" | "." | <小写字母> | <大写字母> +<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | + "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + "y" | "z" +<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | + "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | + "Y" | "Z" +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +``` + +标记```contract```之后的有效代码必须紧跟一个包含```sdk.ISmartContract```成员的合约类定义,这个合约类为智能合约的上下文环境,每次针对智能合约的消息调用都会自动创建这个合约类的一个实例,在这个实例上调用智能合约的方法。智能合约对外提供的方法必须属于这个类的成员函数。类名首字母必须大写。 + +示例如下: + +``` +//@:contract:mycoin +type Mycoin struct { + sdk sdk.ISmartContract + + //@:public:store:cache + totalSupply bn.Number + + //@:public:store + balanceOf map[types.Address]bn.Number +} +``` + + + +### 3.2 library + +标记```library```用于声明合约类型为库,以及标识库的名称,同一组织之下的库名称必须唯一,请库的开发者进行合理的规划。 + +标记```library```与```contract```为必须标记,在整个合约代码中只能出现一次,要么出现一次`contract`标记,要么出现一次`library`标记。 + +标记为`library`的合约不允许使用`public:method`和`public:interface`以及`public:mine`标签。 + +标记为`library`在合约不允许定义`InitChain`以及`UpdateChain`方法。 + +标记```library```的BNF范式定义如下: + +``` + ::= "//@:library:" <库名称> +<库名称> ::= <字母> | <库名称> <字母数字串> +<字母数字串> ::= <字母> | <十进制数字> | <字母数字串> <字母> | <字母数字串> <十进制数字> +<字母> ::= "_" | "-" | "." | <小写字母> | <大写字母> +<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | + "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + "y" | "z" +<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | + "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | + "Y" | "Z" +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +``` + +标记```library```之后的有效代码必须紧跟一个包含```sdk.ILibrary```成员的库类定义,这个库类为库的上下文环境,每次针对库的调用都会自动创建这个库类的一个实例,在这个实例上调用库的方法。库对外提供的方法必须属于这个类的成员函数。类名首字母必须大写。 + +示例如下: + +``` +//@:library:EEA_lib +type EEA struct { + sdk sdk.ILibrary + +} +``` + + + +### 3.3 version + +标记```version```用于标识合约的版本,在整个合约代码中只能出现一次。 + +标记```version```为必须标记,但在整个合约代码中只能出现一次。 + +标记```version```的BNF范式定义如下: + +``` + ::= "//@:version:" <合约版本> +<合约版本> ::= <十进制数> | + <十进制数> "." <十进制数> | + <十进制数> "." <十进制数> "." <十进制数> | + <十进制数> "." <十进制数> "." <十进制数> "." <十进制数> +<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +``` + +示例如下: + +``` +//@:version:1.0 +``` + +注:BCBChain链不要求智能合约版本的具体规则,即合约版本既可以采用一段式(例如1、2、3),也可以采用两段式(1.0、1.1、1.2)、三段式(1.0.1、1.0.2、1.0.3)、四段式(1.0.1.102、1.0.1.103),但是同一智能合约的不同版本需要保持版本格式段数的一致性。 + + + +### 3.4 organization + +标记 ```organization``` 用于标识合约所属组织机构的ID。 + +标记 ```organization``` 为必须标记,但在整个合约代码中只能出现一次。 + +标记 ```organization``` 的BNF范式定义如下: + +``` + ::= "//@:organization:" <组织ID> +<组织ID> ::= <前缀码> +<前缀码> ::= "org" + ::= | + ::= <十进制数字> | <大写字母> | <小写字母> +<十进制数字> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "J" | "K" | "L" | "M" | + "N" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" +<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "m" | + "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | + "z" +``` + +示例如下: + +``` +//@:organization:orgBtjfCSPCAJ84uQWcpNr74NLMWYm5SXzer +``` + + + +### 3.5 author + +标记```author```用于标识合约作者的账户公钥。 + +标记```author```为必须标记,但在整个合约代码中只能出现一次。 + +标记```author```的BNF范式定义如下: + +``` + ::= "//@:author:" <账户公钥> +<账户公钥> ::= <十六进制字符串> +<十六进制字符串> ::= <十六进制数字> | <十六进制字符串> <十六进制数字> +<十六进制数字> ::= "0" | 1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" | + "A" | "B" | "C" | "D" | "E" | "F" | + "a" | "b" | "c" | "d" | "e" | "f" +``` + +示例如下: + +``` +//@:author:b37e7627431feb18123b81bcf1f41ffd37efdb90513d48ff2c7f8a0c27a9d06c +``` + +注:十六进制公钥字符串长度必须等于64。 + + + +### 3.6 constructor + +标记 ```constructor``` 用于标识合约的上链初始化函数。 + +标记 ```constructor``` 之后的有效代码必须紧跟一个名为```InitChain``` 或 ```UpdateChain``` 的无参数函数,该函数将在BCBChain链上第一次部署(```InitChain```)或者升级(```UpdateChain```)这个合约的时候自动进行唯一一次调用,以完成智能合约在区块链上的初始化工作,例如初始化某些全局状态数据的初始值。 + +标记```constructor```为可选标记,但在整个合约代码中最多只能出现两次,一次标识 ```InitChain``` 函数,一次标识 ```UpdateChain```函数。 + +标记```constructor```的BNF范式定义如下: + +``` + ::= "//@:constructor" +``` + +示例如下: + +``` +//@:constructor +func (mc *Mycoin) InitChain() { + ... +} + +//@:constructor +func (mc *Mycoin) UpdateChain() { + ... +} +``` + +注:由于 ```InitChain``` 函数以及 ```UpdateChain```函数不能出现在库中,所以该标签不允许在库代码中使用。 + + + +### 3.7 public:store + +标记 ```public:store``` 用于标识一个状态变量,状态变量必须是通过标记```contract```标注的合约类或`library`标注的库类的成员变量。 + +标记 ```public:store``` 之后的有效代码必须紧跟一个成员变量的定义。 + +标记 ```public:store``` 为可选标记,在合约类定义中可以出现多次,每次标识一个状态变量。 + +用标记 ```public:store``` 标注的状态变量的存储空间在由调用者的上下文确定,如果合约是标记 `contract` 标注的合约,则状态变量的 `key` 的存储空间在这个合约对应的存储空间;如果合约是标记 `library` 标注的库,则状态变量的 `key` 的存储空间在运行时调用这个库的合约对应的存储空间。 + +标记 ```public:store``` 的BNF范式定义如下: + +``` + ::= "//@:public:store" +``` + +示例如下: + +``` +合约: +//@:contract:mycoin +type Mycoin struct { + sdk sdk.ISmartContract + ... + //@:public:store + balanceOf map[types.Address]bn.Number +} + +库: +//@:library:EEA_lib +type EEA struct { + sdk sdk.ILibrary + + //@:public:store + signer types.Pubkey + +} +``` + +标记```public:store```标识的状态变量在合约代码中不能直接访问,根据BCBChain合约规范,BCBChain链提供的SDK配套工具会自动生成访问该状态变量的读写函数,示例如下: + +``` +//读取状态变量balanceOf的函数 +func (mc *Mycoin) _balanceOf(k types.Address) bn.Number { + return *mc.sdk.Helper().StateHelper().GetEx( + fmt.Sprintf("/balanceOf/%v", k), &bn.Number{V: big.NewInt(0)}).(*bn.Number) +} + +//检测状态数据balanceOf是否存在的函数 +func (mc *Mycoin) _chkBalanceOf(k types.Address) bool { + return mc.sdk.Helper().StateHelper().Check(fmt.Sprintf("/balanceOf/%v", k)) +} + +//设置状态变量balanceOf的函数 +func (mc *Mycoin) _setBalanceOf(k types.Address, v bn.Number) { + mc.sdk.Helper().StateHelper().Set(fmt.Sprintf("/balanceOf/%v", k), &v) +} + +//从状态数据库中删除状态变量balanceOf的键值的函数 +func (mc *Mycoin) _delBalanceOf(k types.Address) { + mc.sdk.Helper().StateHelper().Delete(fmt.Sprintf("/balanceOf/%v", k)) +} + +====================================================================================== + +//_signer This is a function of EEA +func (eea *EEA) _signer() types.PubKey { + + return *eea.sdk.Helper().StateHelper().GetEx("/signer", new(types.PubKey)).(*types.PubKey) +} + +//_chkSigner This is a function of EEA +func (eea *EEA) _chkSigner() bool { + return eea.sdk.Helper().StateHelper().Check("/signer") +} + +//_setSigner This is a function of EEA +func (eea *EEA) _setSigner(v types.PubKey) { + eea.sdk.Helper().StateHelper().Set("/signer", &v) +} + +//_delSigner This is a function of EEA +func (eea*EEA) _delSigner() { + eea.sdk.Helper().StateHelper().Delete("/signer") +} +``` + + + +### 3.8 public:store:cache + +标记```public:store:cache```用于标识一个可缓存在内存的状态变量,状态变量必须是通过标记```contract```标注的合约类或`library`标注的库类的成员变量。 + +标记```public:store:cache```之后的有效代码必须紧跟一个成员变量的定义。 + +标记```public:store:cache```为可选标记,在合约类定义中可以出现多次,每次标识一个状态变量。 + +用标记 ```public:store:cache``` 标注的状态变量的存储空间在由调用者的上下文确定,如果合约是标记 `contract` 标注的合约,则状态变量的 `key` 的存储空间在这个合约对应的存储空间;如果合约是标记 `library` 标注的库,则状态变量的 `key` 的存储空间在运行时调用这个库的合约对应的存储空间。 + +标记```public:store:cache```的BNF范式定义如下: + +``` + ::= "//@:public:store:cache" +``` + +示例如下: + +``` +合约: +//@:contract:mycoin +type Mycoin struct { + sdk sdk.ISmartContract + ... + //@:public:store:cache + totalSupply bn.Number +} + +库: +//@:library:EEA_lib +type EEA struct { + sdk sdk.ILibrary + + ... + //@:public:store:cache + cipher []byte + +} +``` + + +可缓存的状态变量在合约代码中不能直接访问,根据BCBChain合约规范,BCBChain提供的SDK配套工具会自动生成访问该状态变量的读写函数,示例如下: + +``` +//读取状态变量totalSupply的函数 +func (mc *Mycoin) _totalSupply() bn.Number { + return *mc.sdk.Helper().StateHelper().McGetEx( + "/totalSupply", &bn.Number{V: big.NewInt(0)}).(*bn.Number) +} + +//检测状态数据totalSupply是否存在的函数 +func (mc *Mycoin) _chkTotalSupply() bool { + return mc.sdk.Helper().StateHelper().McCheck("/totalSupply") +} + +//设置状态变量totalSupply的函数 +func (mc *Mycoin) _setTotalSupply(v bn.Number) { + mc.sdk.Helper().StateHelper().McSet("/totalSupply", &v) +} + +//清除状态变量totalSupply的内存缓存的函数 +func (mc *Mycoin) _clrTotalSupply() { + mc.sdk.Helper().StateHelper().McClear("/totalSupply") +} + +//从状态数据库中删除状态变量totalSupply +func (m *Mycoin) _delTotalSupply() { + m.sdk.Helper().StateHelper().McDelete("/totalSupply") +} + +======================================================================================= +//读取状态变量 cipher 的函数 +func (eea *EEA) _cipher() []byte { + return *eea.sdk.Helper().StateHelper().McGetEx( + "/cipher", &[]byte).(*[]byte) +} + +//检测状态数据 cipher 是否存在的函数 +func (eea *EEA) _chkCipher() bool { + return eea.sdk.Helper().StateHelper().McCheck("/cipher") +} + +//设置状态变量 cipher 的函数 +func (eea *EEA) _setCipher(date string, v []byte) { + eea.sdk.Helper().StateHelper().McSet("/cipher", &v) +} + +//清除状态变量 cipher 的内存缓存的函数 +func (eea *EEA) _clrCipher() { + eea.sdk.Helper().StateHelper().McClear("/cipher") +} + +//从状态数据库中删除状态变量 cipher +func (eea *EEA) _delCipher() { + eea.sdk.Helper().StateHelper().McDelete("/cipher") +} + +``` + + + +### 3.9 public:receipt + +标记 ```public:receipt``` 用于标识合约中所有收据的定义。 + +标记 ```public:receipt``` 之后的有效代码必须紧跟一个名为 ```receipt``` 的接口定义。 + +标记 ```public:receipt``` 为可选标记,在整个合约代码中最多只能出现一次。 + +标记 ```public:receipt``` 的BNF范式定义如下: + +``` + ::= "//@:public:receipt" +``` + +示例如下: + +``` +//@:public:receipt +type receipt interface { + emitTransferMyCoin(token, from, to types.Address, value bn.Number) +} +``` + +接口```receipt```之中定义的每一个发送收据的方法必须以```emit```开头。```emit```之后的第一个单词首字母转为小写后就是这个收据的名称,可以根据收据名称到BCBChain链上进行检索。根据BCBChain合约规范,BCBChain提供的SDK配套工具会自动生成发送收据函数的实现代码,示例如下: + +``` +//下面的函数是由配套工具自动生成的 +func (mc *Mycoin) emitTransferMyCoin(token, from, to types.Address, value bn.Number) { + type transferMyCoin struct { + Token types.Address `json:"token"` + From types.Address `json:"from"` + To types.Address `json:"to"` + Value bn.Number `json:"value"` + } + mc.sdk.Helper().ReceiptHelper().Emit( + transferMycoin{ + Token: token, + From: from, + To: to, + Value: value, + }) +} + +//下面是发送收据的示例代码,位于Transfer函数的实现当中 +func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { + + //实现转账的业务逻辑代码 + ... + + //发送转账收据 + mc.emitTransferMyCoin( + mc.sdk.Message().Contract().Address(), + sender, + to, + value) +} +``` + + + +### 3.10 public:method + +标记```public:method```用于标识合约的公开方法。 + +标记```public:method```之后的有效代码必须紧跟一个针对通过标记```contract```标注的合约类的成员函数定义(函数名称必须由大写字母开头),这个合约的成员函数可以通过BCBChain链的广播交易来执行。 + +标记```public:method```为可选标记,在整个合约代码中可以出现零次、一次或多次。 + +标记```public:method```的BNF范式定义如下: + +``` + ::= "//@:public:method:gas[" <燃料数量> "]" +<燃料数量> ::= ["-"] <十进制数> +<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +``` + +注: + +* 燃料数量为正整数表示该方法调用消耗的燃料费用由交易最初的发起者支付; +* 燃料数量为0表示不需要支付手续费; +* 燃料数量为负整数表示该方法调用消耗的燃料费用由当前智能合约的账户进行支付。 + +示例如下: + +``` +//@:public:method:gas[500] +func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { + ... +} +``` + +注: 标签 `public:method` 只能出现在标记 `contract` 标注的合约中。 + + + +### 3.11 public:interface + +标记```public:interface```用于标识合约的公开接口。 + +标记```public:interface```之后的有效代码必须紧跟一个针对通过标记```contract```标注的合约类的成员函数定义(函数名称必须由大写字母开头),这个合约的成员函数可以从别的合约通过跨合约调用机制来执行。 + +标记```public:interface```为可选标记,在整个合约代码中可以出现零次、一次或多次。 + +标记```public:interface```的BNF范式定义如下: + +``` + ::= "//@:public:interface:gas[" <燃料数量> "]" +<燃料数量> ::= <十进制数> +<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +``` + +示例如下: + +``` +//@:public:interface:gas[450] +func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { + ... +} +``` + +注: + +- 燃料数量大于等于0。 + +标记```public:interface```标记```public:method```可以同时标注合约类的同一个成员函数,上面的例子实际上应该定义如下: + +``` +//@:public:method:gas[500] +//@:public:interface:gas[450] +func (mc *Mycoin) Transfer(to types.Address, value bn.Number) { + ... +} +``` + +注: 标签 `public:interface` 只能出现在标记 `contract` 标注的合约中。 + + + +### 3.12 public:function + +标记```public:function```用于标识库的公开接口。 + +标记```public:function```之后的有效代码必须紧跟一个针对通过标记```library```标注的库类的成员函数定义(函数名称必须由大写字母开头),这个合约的成员函数可以从别的合约通过库调用机制来执行。 + +标记```public:function```为可选标记,在整个合约代码中可以出现零次、一次或多次。 + +标记```public:function```的BNF范式定义如下: + +``` + ::= "//@:public:function:gas[" <燃料数量> "]" +<燃料数量> ::= <十进制数> +<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +``` + +示例如下: + +``` +//@:public:function:gas[450] +func (eea *EEA) Encrypt(data string){ + ... +} +``` + +注: + +- 燃料数量大于等于0。 +- 标签 `public:function` 只能出现在标记 `library` 标注的库中。 + + + +### 3.13 public:mine + +标记```public:mine```用于标识合约公开的挖矿接口。 + +标记```public:mine```之后的有效代码必须紧跟一个针对通过标记```contract```标注的合约类的成员函数定义(挖矿函数原型必须为:```func Mine() int64```),这个挖矿函数将在 BCBChain 的区块达成共识后自动执行。 + +标记```public:mine```为可选标记,在整个合约代码中可以出现零次或一次。只有 BCBChain 的基础组织的智能合约才允许包含挖矿接口。 + +标记```public:mine```的BNF范式定义如下: + +``` + ::= "//@:public:mine" +``` + +示例如下: + +``` +//@:public:mine +func (mc *Mycoin) Mine() int64 { + ... +} +``` + +注: + +- 挖矿函数 ```Mine()``` 只能与标记 ```public:mine``` 进行配合。 +- 标签 `public:mine` 只能出现在标记 `contract` 标注的合约中。 + + + +### 3.14 import + +标记```import```用于导入一个外部合约或库的接口原型,方便在当前合约中调用外部合约或库。 + +标记```import```为可选标记,在整个合约代码中可以出现多次,每次导入一个外部合约的跨合约调用接口或库的库调用接口,每一个外部合约或库只能导入一次。 + +标记```import```的BNF范式定义如下: + +``` + ::= "//@:import:" <合约名称> | <库名称> +<合约名称> ::= <字母> | <合约名称> <字母数字串> +<库名称> ::= <字母> | <合约名称> <字母数字串> +<字母数字串> ::= <字母> | <十进制数字> | <字母数字串> <字母> | <字母数字串> <十进制数字> +<字母> ::= "_" | "-" | "." | <小写字母> | <大写字母> +<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | + "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + "y" | "z" +<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | + "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | + "Y" | "Z" +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +``` + +以前面的```mycoin```代币合约为例,如果在其它合约中需要调用该合约,需要编写如下代码: + +``` +//本段代码在合约mycontract中 + +//@:import:mycoin +type mycoin interface { + Transfer(to types.Address, value bn.Number) +} +``` + +以前面的```EEA_lib```库为例,如果在其它合约中需要调用该合约,需要编写如下代码: + +``` +//本段代码在合约mycontract中 + +//@:import:EEA_lib +type eea_lib interface { + Encrypt(data string) +} +``` + + + +外部合约的接口名称自定义,只要遵循 ```golang``` 语法规范即可。 + +外部合约的接口在合约代码中不能直接访问,根据BCBChain合约规范,BCBChain提供的 ```SDK``` 配套工具会自动生成访问该外部合约接口的实现函数。 + +以调用前面的```mycoin```代币合约为例自动生成的代码示例如下: + +``` +//本段代码是由SDK配套工具自动生成的 + +//mycoin This is method of MyContract +func (m *MyContract) mycoin() *InterfacemycoinStub { + return &InterfacemycoinStub{} +} + +//Transfer This is a method of InterfacemycoinStub +func (is *InterfacemycoinStub) Transfer(to types.Address, value bn.Number) { + return +} + +//run Transfer some receipts to destination contract +func (is *InterfacemycoinStub) run(f func()) (*InterfacemycoinStub) { + ... + f() + ... + return is +} + +//contract Wrap the destination contract information +func (is *InterfacemycoinStub) contract() IContract { + ... + return contract +} +``` + +以调用前面的```EEA_lib```库为例自动生成的代码示例如下: + +``` +//本段代码是由SDK配套工具自动生成的 + +//eea_lib This is method of MyContract +func (m *MyContract) eea_lib() *Interfaceeea_libStub { + return &Interfaceeea_libStub{} +} + +//Encrypt This is a method of Interfaceeea_libStub +func (is *Interfaceeea_libStub) Encrypt(data string) { + return +} + +//run Transfer some receipts to destination contract +func (is *Interfaceeea_libStub) run(f func()) (*Interfaceeea_libStub) { + ... + f() + ... + return is +} + +//contract Wrap the destination contract information +func (is *Interfaceeea_libStub) contract() IContract { + ... + return contract +} +``` + + + +下面是在合约代码中调用外部合约的示例代码: + +``` +//本段代码在合约mycontract中 + +func (m *MyContract)TransferTest(to types.Address, value bn.Number) { + m.mycoin().Transfer(to, value) +} +``` + +下面是在合约代码中调用库的示例代码: + +``` +//本段代码在合约mycontract中 + +func (m *MyContract)Encrypt(data string) { + m.eea_lib().Encrypt(data) +} +``` + + + +跨合约调用还支持向被调用合约传递收据,以下为示例代码(从合约 ```contract2``` 中调用合约 ```contract1``` 的接口): + +> 合约1,提供跨合约接口服务: +> +> ``` +> //@:contract:contract1 +> type C1 struct { +> sdk sdk.ISmartContract +> ... +> } +> +> //@:public:interface:gas[450] +> func (c *C1) Register() { +> +> //I need receipt from caller +> transfers := dw.sdk.Message().GetTransferToMe() +> sdk.Require(transfers!=nil && len(transfers)==1, +> types.ErrInvalidParameter, "Please transfer to me first") +> +> token := transfers[0].Token +> value := transfers[0].Value +> +> ... +> } +> ``` +> +> 合约2,跨合约调用者: +> +> ``` +> //@:contract:contract2 +> type C2 struct { +> sdk sdk.ISmartContract +> ... +> } +> +> //@:import:contract1 +> type mycoin interface { +> Register() +> } +> +> //@:public:method:gas[450] +> func (c *C2) Register() { +> ... +> c.contract1().run(func(){ +> c.sdk.Message().Contract().Account.TransferByName( +> "bcb", +> c.contract1().contract().Account().Address(), +> bn.N(1000000000), +> ) +> }).Register() +> ... +> } +> ``` + + + +库调用还支持向被调用库传递收据,以下为示例代码(从合约 ```contract3``` 中调用库 ```library1``` 的接口): + +> 库1,提供库接口服务: +> +> ``` +> //@:library:library1 +> type C1 struct { +> sdk sdk.ILibrary +> ... +> } +> +> //@:public:function:gas[450] +> func (c *C1) RegisterMember() { +> //I need receipt from caller +> transfers := dw.sdk.Message().GetTransferToMe() +> sdk.Require(transfers!=nil && len(transfers)==1, +> types.ErrInvalidParameter, "Please transfer to me first") +> +> token := transfers[0].Token +> value := transfers[0].Value +> +> ... +> } +> ``` +> +>合约2,库调用者: +> +>``` +> //@:contract:contract3 +> type C3 struct { +> sdk sdk.ISmartContract +> ... +> } +> +> //@:import:library1 +> type library1 interface { +> Register() +> } +> +> //@:public:method:gas[450] +> func (c *C3) RegisteMemberr() { +> ... +> c.library1().run(func(){ +> c.sdk.Message().Contract().Account.TransferByName( +> "bcb", +> c.contract1().contract().Account().Address(), +> bn.N(1000000000), +> ) +> }).RegisterMember() +> ... +> } +> ``` + + + +## 4. 合约规范 + +### 4.1 BNF范式定义 + +综合上面对合约标记的描述,下面定义合约规范的 ```BNF``` 范式: + +``` +<智能合约> ::= <合约定义代码文件> {<合约实现代码文件>} {<合约测试代码文件>} + +<合约定义代码文件> ::= <代码包定义> <合约定义代码> +<合约实现代码文件> ::= <代码包定义> <合约实现代码> +<合约测试代码文件> ::= <代码包定义> <合约测试代码> +<代码包定义> ::= "package" <合约包名> +<合约包名> ::= 遵循glang语法规范,但不能为 std +<合约定义代码> ::= "import (" + <合约SDK包根路径> + {[包别名] <合约支撑代码包路径>} + ")" + <普通合约类> | <库合约类> + [<合约上链初始化函数定义>] + [<挖矿定义>] + [<合约收据定义>] + {<跨合约调用接口定义>} + {<合约公开函数定义>} + {<合约实现代码>} +<普通合约类> ::= <普通合约类定义> + [<合约上链初始化函数定义>] + [<挖矿定义>] + [<合约收据定义>] + {<跨合约调用接口定义>} + {<合约公开函数定义>} + {<合约实现代码>} +<库合约类> ::= <库合约类定义> + [<合约收据定义>] + {<跨合约调用接口定义>} + {<库公开函数定义>} + {<合约实现代码>} +<合约SDK包根路径> ::= double_quote "blockchain/smcsdk/sdk" double_quote +<合约支撑代码包路径> ::= 遵循golang代码包路径规范,遵循BCBChain合约规范的白名单与灰名单规范 +<包别名> ::= 遵循golang代码规范的代码包别名,不允许使用 '.' +<合约实现代码> ::= 遵循golang代码规范的合约实现代码(不需要BCBChain合约标记的代码),包括类型定义、 + 常量定义、函数定义(注:不能包含全局变量定义,不允许使用 for 关键字,不允许递归 + 调用) +<合约测试代码> ::= 遵循golang单元测试规范的测试代码 + +<普通合约类定义> ::= "//@:contract:" <合约名称> + "//@:version:" <合约版本> + "//@:organization:" <组织ID> + "//@:author:" <账户公钥> + "type " <合约类名> " struct {" + " sdk sdk.ISmartContract" + {<状态变量定义>} + {} + "}" +<库合约类定义> ::= "//@:library:" <库名称> + "//@:version:" <合约版本> + "//@:organization:" <组织ID> + "//@:author:" <账户公钥> + "type " <合约类名> " struct {" + " sdk sdk.ILibrary" + {<状态变量定义>} + {} + "}" +<合约名称> ::= 参见<合约标记:contract> +<库名称> ::= 参见<合约标记:library> +<合约版本> ::= 参见<合约标记:version> +<组织ID> ::= 参见<合约标记:organization> +<账户公钥> ::= 参见<合约标记:author> +<合约类名> ::= <大写字母开头的标识符> + ::= 遵循golang代码规范的标准变量定义代码 +<状态变量定义> ::= <基本状态变量定义> | <带缓存的状态变量定义> +<基本状态变量定义> ::= "//@:public:store" + <变量名称> ["*"] <变量类型> +<带缓存的状态变量定义> ::= "//@:public:store:cache" + <变量名称> ["*"] <变量类型> +<变量名称> ::= <标识符> +<变量类型> ::= <元数据类型> | <数组类型> | <映射表类型> + +<合约上链初始化函数定义> ::= <部署函数> | <升级函数> +<部署函数> ::= "//@:constructor" + "func (" <合约对象定义> ") InitChain() {" + <上链代码> + "}" +<升级函数> ::= "//@:constructor" + "func (" <合约对象定义> ") UpdateChain() {" + <上链代码> + "}" +<挖矿定义> ::= "//@:public:mine" + "func (" <合约对象定义> ") Mine() int64 {" + <挖矿代码> + "}" +<合约对象定义> ::= <变量名称> "*" <合约类名> +<上链代码> ::= + 注1:只允许访问状态变量 + 注2:sdk中不允许访问Message()和Tx() +<挖矿代码> ::= + 注1:sdk中不允许访问Message()和Tx() + ::= 遵循golang代码规范的函数体实现代码,参见 <合约实现代码> 的定义 + +<合约收据定义> ::= "//@:public:receipt" + "type receipt interface {" + <收据函数名称> <函数入口参数定义> + "}" +<收据函数名称> ::= "emit" <大写字母开头的标识符> +<函数入口参数定义> ::= "(" <参数表> ")" +<参数表> ::= <参数定义> | <参数表> "," <参数定义> +<参数定义> ::= <变量名称> ["*"] <变量类型> + +<跨合约调用接口定义> ::= "//@:import:" <合约名称> + "type "<接口类名称>" interface {" + <接口函数名称> <函数入口参数定义> <函数返回定义> + "}" +<接口类名称> ::= <大写字母开头的标识符> +<接口函数名称> ::= <大写字母开头的标识符> +<函数返回定义> ::= <空> | ["*"] <变量类型> | "(" <返回表> ")" +<空> ::= 空白 +<返回表> ::= <返回定义> | <返回表> "," <返回定义> +<返回定义> ::= [<变量名称>] ["*"] <变量类型> + +<合约公开函数定义> ::=<合约公开方法标记> | <合约公开接口标记> + "func (" <合约对象定义> ")" <公开函数名称> <函数入口参数定义> <函数返回定义> "{" + + "}" +<合约公开方法标记> ::= "//@:public:method:gas[ "<燃料数量> "]" +<合约公开接口标记> ::= "//@:public:interface:gas[ "<燃料数量> "]" +<燃料数量> ::= 参见<合约标记:public:method> +<公开函数名称> ::= <大写字母开头的标识符> + +<库公开函数定义> ::=<库公开函数标记> + "func (" <合约对象定义> ")" <公开函数名称> <函数入口参数定义> <函数返回定义> "{" + + "}" +<库公开函数标记> ::= "//@:public:function:gas[ "<燃料数量> "]" + +<标识符> ::= <字母> | <标识符> <字母数字串> +<大写字母开头的标识符> ::= <大写字母> | <大写字母开头的标识符> <字母数字串> +<字母数字串> ::= <字母> | <十进制数字> | <字母数字串> <字母> | <字母数字串> <十进制数字> +<字母> ::= "_" | <小写字母> | <大写字母> +<小写字母> ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | + "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + "y" | "z" +<大写字母> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | + "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | + "Y" | "Z" +<十进制数字> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 8 | "9" +<十进制数> ::= <十进制数字> | <十进制数> <十进制数字> + +<元数据类型> := | | <自定义数据结构> + ::= "int" | "int8" | "int16" | "int32" | "int64" | + "uint" | "uint8" | "uint16" | "uint32" | "uint64" | + "bool" | "string" | "byte" + ::=
| | | | +
::= "types.Address" + ::= "types.HexBytes" + ::= "types.Hash" + ::= "types.PubKey" + ::= "bn.Number" +<自定义数据结构> ::= "type " <标识符> " struct {" + {<变量名称> ["*"] <变量类型>} + "}" +<数组类型> ::= <数组维度定义> [<数组维度定义>] <元数据类型> +<数组维度定义> ::= "[" [<十进制数>] "]" +<映射表类型> ::= <映射表类型1> | <映射表类型2> +<映射表类型1> ::= <映射表定义> [<映射表定义>] <元数据类型> +<映射表类型2> ::= <映射表定义> [<映射表定义>] <数组类型> +<映射表定义> ::= "map[" <元数据类型> "]" +``` + + + +### 4.2 包结构规范 + +下面定义合约包结构的 ```BNF``` 范式: + +``` +<智能合约包结构> ::= <合约定义代码文件> {<合约实现代码文件>} {<合约测试代码文件>} +<合约定义代码文件> ::= <合约代码目录> "/" +<合约实现代码文件> ::= <合约代码目录> "/" +<合约测试代码文件> ::= <合约代码目录> "/" +<合约代码目录> ::= "/" <合约根目录> "/" <合约名称> "/v" <合约版本> "/" <合约名称> + ::= golang标准环境变量GOPATH指向的目录 +<合约根目录> ::= "src/contract/" <组织ID> "/code" +<组织ID> ::= 参见<合约标记:organization> + ::= <合法文件名> ".go" + ::= <合法文件名> "_test.go" +<任意文件名> ::= <合法文件名>|<非法文件名> +<非法文件名> ::= [<合法文件名>] "autogen" [<合法文件名>] | <任意文件名> "_test" +<合法文件名> ::= 排除<非法文件名>以后所有操作系统认可作为文件名的字符串 +``` + +注:规范定义中出现的空白符间隔仅为排版需要,不作为规范的一部分。 + + + +### 4.3 白名单列表 + +出于安全考虑,BCBChain智能合约限制为只允许导入行为明确且不会导致不同节点运行结果不一致的代码支撑包。这样的包包含在白名单列表中。 + +白名单列表中支持的代码包在智能合约中可以放心的使用。 + +白名单与灰名单的具体内容随智能合约 ```SDK``` 及配套的BCBChain插件版本升级将会有所不同,详细信息请参见智能合约 ```SDK``` 及配套的BCBChain插件的相关文件。 + +下面是第一版白名单列表: + +``` +//以下为可用的 golang 标识包 +bytes +container/heap +container/list +container/ring +crypto +crypto/aes +crypto/cipher +crypto/des +crypto/hmac +crypto/md5 +crypto/rc4 +crypto/sha1 +crypto/sha256 +crypto/sha512 +encoding +encoding/ascii85 +encoding/asn1 +encoding/base32 +encoding/base64 +encoding/binary +encoding/csv +encoding/gob +encoding/hex +encoding/json +encoding/pem +encoding/xml +errors +fmt +hash +hash/adler32 +hash/crc32 +hash/crc64 +hash/fnv +index/suffixarray +math +math/big +math/bits +math/cmplx +reflect +regexp +regexp/syntax +sort +strconv +strings +unicode +unicode/utf16 +unicode/utf8 + +//以下为 SDK 提供的标准包 +blockchain/smcsdk/sdk +``` + + + + +## 5. BRC20代币 + +本文档概述中发布的智能合约 “代币” 是一个演示性的代币合约,不是符合BCBChain标准的 ```BRC20``` 代币。 + +BCBChain的用户可以自己编写智能合约发行符合BRC20代币规范的代币。本节定义符合 ```BRC20``` 代币规范的智能合约。 + +规范描述如下: + +* 合约必须明确调用智能合约SDK提供的接口```ITokenHelper::RegisterToken(...)```向BCBChain注册一个新的代币(每个智能合约能且只能注册一个代币); + +* 合约注册代币会自动在BCBChain上记录一条标准的代币生成收据,收据定义如下: + + ``` + import ( + "blockchain/smcsdk/sdk/bn" + "blockchain/smcsdk/sdk/types" + ) + + // Name of receipt: std::newToken + type NewToken struct { + TokenAddress types.Address `json:"tokenAddr"` // 代币地址 + ContractAddress types.Address `json:"contractAddr"` // 代币的合约地址 + Owner types.Address `json:"owner"` // 代币拥有者的外部账户地址 + Name string `json:"name"` // 代币名称 + Symbol string `json:"symbol"` // 代币符号 + TotalSupply bn.Number `json:"totalSupply"` // 代币总供应量(单位:cong) + AddSupplyEnabled bool `json:"addSupplyEnabled"`// 代币是否支持增发 + BurnEnabled bool `json:"burnEnabled"` // 代币是否支持燃烧 + GasPrice int64 `json:"gasPrice"` // 代币燃料价格(单位:cong) + } + ``` + +* 合约必须实现本章定义的方法与接口。 + + + +### 5.1 Transfer + +**方法原型** + +``` +import ( + "blockchain/smcsdk/sdk/bn" + "blockchain/smcsdk/sdk/types" +) + +//@:public:method:gas[500] +//@:public:interface:gas[450] +Transfer(types.Address,bn.Number) +``` + +**功能说明** + +- 执行代币转移功能。 + +**实现说明** + +- 必须实现; +- 必须标记为```public:method```; +- 必须标记为```public:interface```。 + +**调用说明** + +- 允许任何人通过广播交易进行调用; +- 允许同组织的其它智能合约进行跨合约调用; +- 允许同组织的其它智能合约通过SDK提供的接口```IAccount::Transfer```直接调用(```SDK``` 底层自动进行跨合约调用)。 + +**输入参数** + +- _to types.Address 代币接收方地址(可以是外部账户地址、合约账户地址、合约地址) + + _value bn.Number 转移的代币数额(单位:```cong```) + + 注:如果接收方地址是合约地址,会自动将接收到的代币转入该合约的账户地址。 + +**输出收据** + +- BRC20代币 ```Transfer() ``` 方法会自动在BCBChain上记录一条标准的代币转移收据,收据定义如下: + + ``` + import ( + "blockchain/smcsdk/sdk/bn" + "blockchain/smcsdk/sdk/types" + ) + + // Name of receipt: std::transfer + type Transfer struct { + Token smc.Address `json:"token"` // 代币地址 + From smc.Address `json:"from"` // 转出方地址 + To smc.Address `json:"to"` // 代币接收方地址 + Value bn.Number `json:"value"` // 转移的代币数额((单位:cong) + } + ``` + +### 5.2 AddSupply + +**方法原型** + +``` +import ( + "blockchain/smcsdk/sdk/bn" +) + +//@:public:method:gas[2400] +AddSupply(bn.Number) +``` + +**功能说明** + +- 为代币增加供应量。 + +**实现说明** + +- 可选实现; +- 如果实现,必须标记为```public:method```。 + +**调用说明** + +- 只有代币拥有者才能通过广播交易进行调用。 + +**输入参数** + +- _value bn.Number 新增加的供应量(单位:```cong```)。 + +**输出收据** + +- BRC20代币 ```AddSupply() ``` 方法会自动在BCBChain上记录一条标准的代币增发收据,收据定义如下: + + ``` + import ( + "blockchain/smcsdk/sdk/bn" + "blockchain/smcsdk/sdk/types" + ) + + // Name of receipt: std::addSupply + type AddSupply struct { + Token types.Address `json:"token"` // 代币地址 + Value bn.Number `json:"value"` // 增发的供应量(单位:cong) + TotalSupply bn.Number `json:"totalSupply"` // 新的总供应量(单位:cong) + } + ``` +- BRC20代币 ```AddSupply()``` 方法还会自动在BCBChain上记录一条标准的```std::transfer```收据。 + + + +### 5.3 Burn + +**方法原型** + +``` +import ( + "blockchain/smcsdk/sdk/bn" +) + +//@:public:method:gas[2400] +Burn(bn.Number) +``` + +**功能说明** + +- 燃烧代币的供应量。 + +**实现说明** + +- 可选实现; +- 如果实现,必须标记为```public:method```。 + +**调用说明** + +- 只有代币拥有者才能通过广播交易进行调用。 + +**输入参数** + +- _value bn.Number 燃烧掉的供应量(单位:```cong```)。 + +**输出收据** + +- BRC20代币 ```Burn()``` 方法会自动在BCBChain上记录一条标准的代币燃烧收据,收据定义如下: + + ``` + import ( + "blockchain/smcsdk/sdk/bn" + "blockchain/smcsdk/sdk/types" + ) + + // Name of receipt: std::burn + type Burn struct { + Token types.Address `json:"token"` // 代币地址 + Value bn.Number `json:"value"` // 燃烧的供应量(单位:cong) + TotalSupply bn.Number `json:"totalSupply"` // 新的总供应量(单位:cong) + } + ``` +- BRC20代币 ```Burn()``` 方法还会自动在BCBChain上记录一条标准的```std::transfer```收据。 + + + +### 5.4 SetGasPrice + +**方法原型** + +``` +//@:public:method:gas[2400] +SetGasPrice(int64) +``` + +**功能说明** + +- 设置燃料价格。 + +**实现说明** + +- 可选实现; +- 如果实现,必须标记为```public:method```。 + +**调用说明** + +- 只有代币拥有者才能通过广播交易进行调用。 + +**输入参数** + +- _value int64 新的燃料价格(单位:```cong``` )。 + +**输出收据** + +- BRC20代币 ```SetGasPrice() ``` 方法会自动在BCBChain上记录一条标准的设置燃料价格收据,收据定义如下: + + ``` + import ( + "blockchain/smcsdk/sdk/types" + ) + + // Name of receipt: std::setGasPrice + type SetGasPrice struct { + Token types.Address `json:"token"` // 代币地址 + GasPrice int64 `json:"gasPrice"` // 燃料价格(单位:cong) + } + ``` + +### 5.5 SetOwner + +**方法原型** + +``` +import ( + "blockchain/smcsdk/sdk/types" +) + +//@:public:method:gas[2400] +SetOwner(types.Address) +``` + +**功能说明** + +- 转移智能合约及代币的所有权。 + +**实现说明** + +- 可选实现; +- 如果实现,必须标记为```public:method```。 + +**调用说明** + +- 只有代币拥有者才能通过广播交易进行调用。 + +**输入参数** + +- _newOwner types.Address 合约新的拥有者的外部账户地址。 + +**输出收据** + +- BRC20代币 ```SetOwner()``` 方法会自动在BCBChain上记录一条标准的转移所有权收据,收据定义如下: + + ``` + import ( + "blockchain/smcsdk/sdk/types" + ) + + // Name of receipt: std::setOwner + type SetOwner struct { + ContractAddr types.Address `json:"contractAddr"` // 智能合约地址 + NewOwner types.Address `json:"newOwner"` // 合约新的拥有者的外部账户地址 + } + ``` +- BRC20代币 ```SetOwner()``` 方法还会自动在BCBChain上记录一条标准的```std::transfer```收据。 diff --git "a/zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/\345\274\200\345\217\221\345\272\223/01-\345\272\223.md" "b/zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/\345\274\200\345\217\221\345\272\223/01-\345\272\223.md" new file mode 100755 index 0000000..8ad0cb6 --- /dev/null +++ "b/zh-cn/04-\346\231\272\350\203\275\345\220\210\347\272\246\345\274\200\345\217\221/\346\231\272\350\203\275\345\220\210\347\272\246(golang)/\345\274\200\345\217\221\345\272\223/01-\345\272\223.md" @@ -0,0 +1,283 @@ +# 库 + +## 1. 创建库 + +下面我们开始创建库。 + +用 `GoLand` 打开刚刚创建的 `GoLand Project`,然后通过 BCBChain 智能合约开发插件创建智能合约,以下为执行路径: + +``` +CopyGoLand左边栏 Project 浏览器 + >> 在 src/contract 目录上点击鼠标右键出现菜单 + >> BCB Smart Contract + >> New... + >> 输入 Name #banklib + >> 输入 Type #BankLib + >> 输入 ContractType #Library + >> 输入 Version #1.0 + >> 输入 OrganizationName #DAO + >> 计算 Organization #orgE37w7wxhZwaox1fhndt5Czm8WnLBrh6db - DAO + >> 输入 Author #alice + >> OK +``` + +智能合约创建成功,将会自动生成框架代码,文件路径如下: + +``` +Copysrc/contract/banklib/v1.0/banklib/banklib.go +``` + +库初始代码如下: + +``` +package banklib + +import ( + "blockchain/smcsdk/sdk" +) + +//banklib This is struct of contract +//@:library:banklib +//@:version:1.0 +//@:organization:orgE37w7wxhZwaox1fhndt5Czm8WnLBrh6db +//@:author:e5b95784049c3b8669aa58ba08abd9f54281df8905dd3af24bf795b0103f9ec7 +type BankLib struct { + sdk sdk.ISmartContract + + //This is a sample field which is to store in db + //@:public:store + sampleStore string + +} + +//SampleFunction This is a sample function +//@:public:function:gas[500] +func (m *Mydonation) SampleMethod() { + +} +``` + +## 2. 代码示例 + +### 2.1 库代码 + +库的编写流程与智能合约的编写流程是一致的,可参考智能合约的开发 + +下面我们一个简单的库合约作为示例 + +该库为一个银行合约的库,提供了2个公用的库方法`SetManager`以及`SetSigner`,可用于不同的银行智能合约调用 + +```go +package banklib + +import ( + "blockchain/smcsdk/sdk" + "blockchain/smcsdk/sdk/types" + +) + +//banklib This is struct of contract +//@:library:banklib +//@:version:1.0 +//@:organization:orgE37w7wxhZwaox1fhndt5Czm8WnLBrh6db +//@:author:5e8339cb1a5cce65602fd4f57e115905348f7e83bcbe38dd77694dbe1f8903c9 +type BankLib struct { + sdk sdk.ILibrary + + //@:public:store + manager types.Address + + //@:public:store + signer types.PubKey + +} + +//@:public:receipt +type receipt interface { + emitSetManager(manager types.Address) + emitSetSigner(signer types.PubKey) +} + +//@:public:function:gas[-1] +func (bl *BankLib) SetManager(manager types.Address) { + sdk.RequireOwner() + + sdk.RequireAddress(manager) + + bl._setManager(manager) + + bl.emitSetManager(manager) +} + +//@:public:function:gas[-1] +func (c *BankLib) SetSigner(pubKey types.PubKey) { + bl.RequireManager() + + sdk.Require(len(pubKey) == PUBKEYLEN, + types.ErrInvalidParameter, "Invalid pubkey.") + + bl._setSigner(pubKey) + + bl.emitSetSigner(pubKey) +} + +``` + + + +### 2.2 合约代码 + +BeiJingBank 智能合约调用库部分的代码如下 + +```go +package beijingbank + +import ( + "blockchain/smcsdk/sdk" + "blockchain/smcsdk/sdk/types" +) + +//BeiJingBank This is struct of contract +//@:contract:BeiJingBank +//@:version:2.0 +//@:organization:orgE37w7wxhZwaox1fhndt5Czm8WnLBrh6db +//@:author:5e8339cb1a5cce65602fd4f57e115905348f7e83bcbe38dd77694dbe1f8903c9 +type BeiJingBank struct { + sdk sdk.ISmartContract + + //@:public:store + manager types.Address + + //@:public:store + signer types.PubKey + +} + +//@:import:control +type lib interface { + SetManager(types.Address) + SetSigner(types.PubKey) +} + +//@:constructor +func (bjb *BeiJingBank) InitChain() { +} + +//@:public:method:gas[-1] +func (bjb *BeiJingBank) SetManager(manager types.Address) { + bjb.banklib().SetManager(manager) +} + +//@:public:method:gas[-1] +func (bjb *BeiJingBank) SetSigner(pubKey types.PubKey) { + bjb.banklib().SetSigner(pubKey) +} +``` + + + +## 2. 库规范检查 + +下面的执行路径可以检查上面完成的库代码是否符合 BCBChain 库规范: + +``` +GoLand左边栏 Project 浏览器 + >> 在 src/contract/banklib/v1.0/banklib 目录上点击鼠标右键出现菜单 + >> BCB Smart Contract + >> Check +``` + + + +## 3. 代码补全 + +### 3.1库 + +到现在为止,我们已经完成了库代码,但这个库代码不能在 ```GoLand IDE``` 中直接运行,需要使用插件提供的代码生成功能补全代码以后才能在本地运行,下面是补全代码的执行路径: + +``` +GoLand左边栏 Project 浏览器 + >> 在 src/contract/banklib/v1.0/banklib 目录上点击鼠标右键出现菜单 + >> BCB Smart Contrat + >> Generate Code +``` + +补全代码执行以后,在合约代码目录将会自动生成如下代码文件: + +``` +//以下文件每次自动生成代码都会将原来的内容覆盖,请不要修改这些代码文件 +banklib_autogen_store.go //自动补全的状态数据库访问代码 +banklib_autogen_receipt.go //自动补全的收据发送代码 +banklib_autogen_sdk.go //自动补全的杂项代码 +banklib_wrap_test.go //自动补全的单元测试基础代码 + +//以下文件为单元测试代码,需要程序员完成对各个方法的单元测试,下次自动生成代码时不会自动修改其中内容 +banklib_case_setManager_test.go //自动补全的单元测试代码 - 针对 SetManager 方法进行单元测试 +banklib_case_setSigner_test.go //自动补全的单元测试代码 - 针对 SetSigner 方法进行单元测试 +``` + + + +### 3.2 合约 + +合约代码的补全操作与库的一致: + +``` +GoLand左边栏 Project 浏览器 + >> 在 src/contract/BeiJingBank/v1.0/BeiJingBank 目录上点击鼠标右键出现菜单 + >> BCB Smart Contrat + >> Generate Code +``` + +补全代码执行后,在合约代码目录将会自动生成如下代码文件: + +``` +//以下文件每次自动生成代码都会将原来的内容覆盖,请不要修改这些代码文件 +beijingbank_autogen_store.go //自动补全的状态数据库访问代码 +beijingbank_autogen_receipt.go //自动补全的收据发送代码 +beijingbank_autogen_sdk.go //自动补全的杂项代码 +beijingbank_wrap_test.go //自动补全的单元测试基础代码 +beijingbank_autogen_import_banklib.go //自动补全的库调用的相关辅助代码 + +//以下文件为单元测试代码,需要程序员完成对各个方法的单元测试,下次自动生成代码时不会自动修改其中内容 +beijingbank_case_setManager_test.go //自动补全的单元测试代码 - 针对 SetManager 方法进行单元测试 +beijingbank_case_setSigner_test.go //自动补全的单元测试代码 - 针对 SetSigner 方法进行单元测试 +``` + + + +这里主要对`beijingbank_autogen_import_banklib.go`代码作说明,其余部分与正常的合约代码生成的内容一致: + +```go +func (intfc *InterfaceStub) SetManager(manager types.Address) { + + methodID := "e463fdb2" // prototype: SetManager(types.Address) + oldSmc := intfc.stub.GetSdk() + + contract := oldSmc.Helper().ContractHelper().ContractOfName(importContractName) + sdk.Require(contract != nil, types.ErrExpireContract, "") + + newSmc := sdkhelper.OriginNewMessage(oldSmc, oldSmc.Message().Contract(), methodID, intfc.receipts) + + var rn interface{} + rn = SetManagerParam{ + manager:manager, + } + + var response types3.Response + gls.Mgr.SetValues(gls.Values{gls.SDKKey: newSmc}, func() { + response = intfc.stub.Invoke(methodID, rn) + }) + if response.Code != types.CodeOK { + panic(response.Log) + } + oldmsg := oldSmc.Message() + oldmsg.(*object.Message).AppendOutput(intfc.stub.GetSdk().Message().(*object.Message).OutputReceipts()) + intfc.receipts = nil +} + +注: +1. 库调用中,sdk的相关设置依旧是原合约的设置 +2. newSmc所用的合约对象为原合约的合约对象 +``` +