go语言操作bsc合约 go语言编写智能合约

如何看待go语言泛型的最新设计?Go 由于不支持泛型而臭名昭著go语言操作bsc合约,但最近 , 泛型已接近成为现实 。Go 团队实施go语言操作bsc合约了一个看起来比较稳定go语言操作bsc合约的设计草案,并且正以源到源翻译器原型的形式获得关注 。本文讲述的是泛型的最新设计,以及如何自己尝试泛型 。
例子
FIFO Stack
假设go语言操作bsc合约你要创建一个先进先出堆栈 。没有泛型 , 你可能会这样实现:
type Stack []interface{}func (s Stack) Peek() interface{} {
return s[len(s)-1]
}
func (s *Stack) Pop() {
*s = (*s)[:
len(*s)-1]
}
func (s *Stack) Push(value interface{}) {
*s =
append(*s, value)
}
但是 , 这里存在一个问题:每当你 Peek 项时,都必须使用类型断言将其从 interface{} 转换为你需要的类型 。如果你的堆栈是 *MyObject 的堆栈,则意味着很多 s.Peek().(*MyObject)这样的代码 。这不仅让人眼花缭乱,而且还可能引发错误 。比如忘记 * 怎么办?或者如果您输入错误的类型怎么办?s.Push(MyObject{})` 可以顺利编译,而且你可能不会发现到自己的错误,直到它影响到你的整个服务为止 。
通常,使用 interface{} 是相对危险的 。使用更多受限制的类型总是更安全,因为可以在编译时而不是运行时发现问题 。
泛型通过允许类型具有类型参数来解决此问题:
type Stack(type T) []Tfunc (s Stack(T)) Peek() T {
return s[len(s)-1]
}
func (s *Stack(T)) Pop() {
*s = (*s)[:
len(*s)-1]
}
func (s *Stack(T)) Push(value T) {
*s =
append(*s, value)
}
这会向 Stack 添加一个类型参数 , 从而完全不需要 interface{} 。现在,当你使用 Peek() 时,返回的值已经是原始类型,并且没有机会返回错误的值类型 。这种方式更安全,更容易使用 。(译注:就是看起来更丑陋,^-^)
此外,泛型代码通常更易于编译器优化,从而获得更好的性能(以二进制大小为代价) 。如果go语言操作bsc合约我们对上面的非泛型代码和泛型代码进行基准测试,我们可以看到区别:
type MyObject struct {
X
int
}
var sink MyObjectfunc BenchmarkGo1(b *testing.B) {
for i := 0; ib.N; i{
var s Stack
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink = s.Peek().(MyObject)
}
}
func BenchmarkGo2(b *testing.B) {
for i := 0; ib.N; i{
var s Stack(MyObject)
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink = s.Peek()
}
}
结果:
BenchmarkGo1BenchmarkGo1-161283752887.0 ns/op48 B/op2 allocs/opBenchmarkGo2BenchmarkGo2-162840647941.9 ns/op24 B/op2 allocs/op
在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍 。
合约(Contracts)
上面的堆栈示例适用于任何类型 。但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码 。例如 , 你可能希望堆栈要求类型实现 String() 函数
一学就会 , 手把手教你用Go语言调用智能合约智能合约调用是实现一个 DApp 的关键 , 一个完整的 DApp 包括前端、后端、智能合约及区块 链系统,智能合约的调用是连接区块链与前后端的关键 。
go语言操作bsc合约我们先来了解一下智能合约调用的基础原理 。智能合约运行在以太坊节点的 EVM 中 。因此要 想调用合约必须要访问某个节点 。
以后端程序为例,后端服务若想连接节点有两种可能,一种是双 方在同一主机,此时后端连接节点可以采用 本地 IPC(Inter-Process Communication , 进 程间通信)机制,也可以采用 RPC(Remote Procedure Call,远程过程调用)机制;另 一种情况是双方不在同一台主机,此时只能采用 RPC 机制进行通信 。
提到 RPC,读者应该对 Geth 启动参数有点印象,Geth 启动时可以选择开启 RPC 服务 , 对应的 默认服务端口是 8545 。。
接着 , go语言操作bsc合约我们来了解一下智能合约运行的过程 。
智能合约的运行过程是后端服务连接某节点 , 将 智能合约的调用(交易)发送给节点,节点在验证了交易的合法性后进行全网广播,被矿工打包到 区块中代表此交易得到确认,至此交易才算完成 。
就像数据库一样,每个区块链平台都会提供主流 开发语言的 SDK(Software Development Kit,软件开发工具包),由于 Geth 本身就是用 Go 语言 编写的 , 因此若想使用 Go 语言连接节点、发交易,直接在工程内导入 go-ethereum(Geth 源码) 包就可以了,剩下的问题就是流程和 API 的事情了 。
总结一下 , 智能合约被调用的两个关键点是节点和 SDK 。
由于 IPC 要求后端与节点必须在同一主机,所以很多时候开发者都会采用 RPC 模式 。除了 RPC,以太坊也为开发者提供了 json- rpc 接口 , 本文就不展开讨论了 。
接下来介绍如何使用 Go 语言,借助 go-ethereum 源码库来实现智能合约的调用 。这是有固定 步骤的,我们先来说一下总体步骤,以下面的合约为例 。
步骤 01:编译合约,获取合约 ABI(Application Binary Interface,应用二进制接口) 。单击【ABI】按钮拷贝合约 ABI 信息 , 将其粘贴到文件 calldemo.abi 中(可使用 Go 语言IDE 创建该文件,文件名可自定义,后缀最好使用 abi) 。
最好能将 calldemo.abi 单独保存在一个目录下,输入“ls”命令只能看到 calldemo.abi 文件,参 考效果如下:
步骤 02:获得合约地址 。注意要将合约部署到 Geth 节点 。因此 Environment 选择为 Web3 Provider 。
在【Environment】选项框中选择“Web3 Provider”,然后单击【Deploy】按钮 。
部署后,获得合约地址为:0xa09209c28AEf59a4653b905792a9a910E78E7407 。
步骤 03:利用 abigen 工具(Geth 工具包内的可执行程序)编译智能合约为 Go 代码 。abigen 工具的作用是将 abi 文件转换为 Go 代码,命令如下:
其中各参数的含义如下 。(1)abi:是指定传入的 abi 文件 。(2)type:是指定输出文件中的基本结构类型 。(3)pkg:指定输出文件 package 名称 。(4)out:指定输出文件名 。执行后,将在代码目录下看到 funcdemo.go 文件,读者可以打开该文件欣赏一下,注意不要修改它 。
步骤 04:创建 main.go , 填入如下代码 。注意代码中 HexToAddress 函数内要传入该合约部署后的地址 , 此地址在步骤 01 中获得 。
步骤 04:设置 go mod,以便工程自动识别 。
前面有所提及,若要使用 Go 语言调用智能合约,需要下载 go-ethereum 工程,可以使用下面 的指令:
该指令会自动将 go-ethereum 下载到“$GOPATH/src/github.com/ethereum/go-ethereum”,这样还算 不错 。不过,Go 语言自 1.11 版本后,增加了 module 管理工程的模式 。只要设置好了 go mod,下载 依赖工程的事情就不必关心了 。
接下来设置 module 生效和 GOPROXY,命令如下:
在项目工程内,执行初始化,calldemo 可以自定义名称 。
步骤 05:运行代码 。执行代码,将看到下面的效果,以及最终输出的 2020 。
上述输出信息中,可以看到 Go 语言会自动下载依赖文件,这就是 go mod 的神奇之处 。看到 2020,相信读者也知道运行结果是正确的了 。
C语言的书写规则主要有哪些呢?1.
1.1符号命名规则
1.1.1符号名包括模块名、常量名、标号名、子程序名等 。这些名字应该能反映它所代表go语言操作bsc合约的实际东西go语言操作bsc合约 , 具有一定的意义,使其能够见名知义 , 有助于对程序功能的理解 。命名采用匈牙利命名法 。规则如下:
(1)所有宏定义、枚举常数和const变量,用大写字母命名 。在复合词里用下划线隔开每个词 。
(2)复合词中每个单词的第一个字母大写 。除了规则5.1.1.1以外,避免使用下划线 。
(3)类、类型定义和枚举型名的第一个字母大写 。
(4)函数名是复合词的,第一个词采用全部小写 , 随后每个单词采用第一个字母大写,其它字母小写方式go语言操作bsc合约;如果是单个词的,采用全部小写方式 。
(5)循环变量可采用i, j, k等,不受上述规则限制 。
(6) 类的成员变量应采用m_开头 。
(7) 全局变量词头为g_。
(8) 临时变量词头为tmp_。
(9) 对结构体内的变量命名, 遵循变量的具体含义命名原则
(10)用小写字母的前缀表示变量的类型,前缀的下一个字母用大写 。
表 1
词头 类型 词头 类型
ch char l long
i integer u unsigned
b boolean p pointer
f float lp long pointer
d double s string
st structure sz ASCII string
by byte n short int
H handle x,y 分别为x,y坐标
dw DWORD fn function
表 2
词头 变量名 词头 变量名
task task sig signal
sb binary semaphores wd watchdog
sm mutual exclusion tm timer
sc counting semaphores msg message
pipe pipe
例:
#define ARRAY_SIZE 24 /*规则5.1.1.1*/
int g_iFlag;
class MyClass /*规则5.1.1.3*/
{
};
void someFunc( ) /*规则5.1.1.2和5.1.1.4*/
{
int nArray[ARRAY_SIZE];
unsigned char uchByte;
char szName[ ];
char *pszName = szName;
}
(11)有些词头(如p和u)可以和其它词头组合 。
例:WDOG_ID wdId;
WDOG_ID g_wdId; /*全局watchdog Id,故以g_开头*/
1.1.2名字的长度一般不要过长或过短 。过长的名字会增加工作量,使程序逻辑流程变得模糊;过短的名字无法表达符号的实际意义 。约定长度范围:3-31;
1.2数据和函数说明
1.2.1数据说明次序应当规范化,使数据属性容易查找,也有利于测试、排错和维护 。说明的先后次序应固定,应按逻辑功能排序,逻辑功能块内建议采用下列顺序:整型说明、实型说明、字符说明、逻辑量说明 。
1.2.2如果设计了一个复杂的数据结构,应当通过注释对其变量的含义、用途进行说明 。
1.2.3在函数的声明中使用异常声明 。
如:void f() throw(toobig, toosmall, divzero);
在声明一个函数时,将它所抛出的异常列出,便于函数的使用者了解可能会发生哪些异常 。
1.3 程序注释
1.3.1程序注释是程序员与日后的程序读者之间通信的重要手段之一,注释分为文件注释、函数注释和功能注释 。
1.3.2正规程序的注释应注意:
——注释行的数量占到整个源程序的1/3到1/2 。
1.3.3文件注释位于整个源程序的最开始部分,注释后空两行开始程序正文 。它包括:
——程序标题 。
——目的、功能说明 。
——文件作者、最后修改日期等说明 。
例:
./********************************************************************
(空一行)
标题: Demo.c
功能: 测试VxWorks的各种系统调用.
说明:
该程序测试各种VxWorks的系统调用函数 。包括任务(taks)的创建、挂起及任务间通过信号灯实现同步 , 通过消息队列 进行通讯 。
程序创建了两个任务:一个高优先级的任务和一个低优先级的任务 。两个任务间通过一个二进制的信号灯进行同步,通过消息队列进行通讯 。
当前版本: x.x
修改信息: 2000.06.05 John,Initial Version
2000.07.05 Tom,Bug xxxx fixed
**************************************************************/
(空2行,开始程序正文)
1.3.4 函数注释通常置于每函数或过程的开头部分,它应当给出函数或过程的整体说明对于理解程序本身具有引导作用 。一般包括如下条目:
——模块标题 。
——有关本模块功能和目的的说明 。
——调用格式
——接口说明:包括输入、输出、返回值、异常 。
——算法 。如果模块中采用了一些复杂的算法 。
例:
file://(/注释开头应和上一函数空两行)
(注释开头与上一函数最后一行间隔两行)
/********************************************************************
标题:assignmentComplete
功能:BSC=MSC消息生成函数,生成assignment_complete指配完成消息(BSMAP消息) .
格式:
int assignmentComplete(int iCellId, int iServiceChannnelNum, char *pszMSGData) throw(exception1, exception2)
输入:
int iCellId: MS所在的小区识别
iCellId取值:0x00-——0xff
int iServiceChannnelNum:MS所占的业务信道号码
输出:
char * pszMSGData:指配完成消息数据
返回值: 0x00正常
异常:exception1异常情况1, exception2异常情况2
********************************************************************/
( 注释后直接开始程序正文 , 不空行 。)
1.3.5功能性注释嵌在源程序体中,用于描述其后的语句或程序段做什么工作,也就是解释下面要做什么,或是执行了下面的语句会怎么样 。而不要解释下面怎么做,因为解释怎么做常常与程序本身是重复的 。
例:
/*把 amount 加到 total中*/
total = amounttotal;
这样的注释仅仅是重复了下面的程序,对于理解它的工作并没有什么作用 。而下面的注释,有助于读者理解 。
/*将每月的销售额amount加到年销售额total中*/
total = amounttotal;
1.4 函数编写应尽可能短小精悍 , 一般不超过两屏,以便于调试和理解 。
1.5语句结构
为保证语句结构的清晰和程序的可读性 , 在编写软件程序时应注意以下几个方面的问题:
——在一行内只写一条语句,并采用空格、空行和移行保证清楚的视觉效果 。
——每一个嵌套的函数块,使用一个TAB缩进(可以设定为4个空格),大括号必须放在条件语句的下一行,单独成一行 , 便于匹对:
如,有一段程序如下:
for(i=1;in-1;i){ t=1; for(j=i 1;jn;j){
if(a[j]a[t] ) t=j; if(t!=i ){work=a[t];a[t]=a[I];a[I]=work;}}}
应写为
for( i=1; in-1; i)
{
t=1;
for(j = i 1; jn; j)
{
if(a[i]a[j])
t=j;
if(t!=1)
{ .5.
Q/ECC/BJ 010—2001
work=a[t];
a[t]=a[i];
a[i]=work;
}
}
}
——文件之中不得存在无规则的空行 , 比如说连续十个空行 。
一般来讲函数与函数之间的空行为2-3行;
在函数体内部,在逻辑上独立的两个函数块可适当空行,一般为1-2行 。
——程序编写首先应考虑清晰性 , 不要刻意追求技巧性而使得程序难以理解 。
——每行长度尽量避免超过屏幕宽度,应不超过80个字符 。
——除非对效率有特殊要求,编写程序要作到清晰第一,效率第二 。
——尽可能使用函数库 。
——尽量用公共过程或子程序去代替重复的功能代码段 。要注意 , 这个代码应具有一个独立的功能,不要只因代码形式一样便将其抽出组成一个公共过程或子程序 。
——使用括号清晰地表达算术表达式和逻辑表达式的运算顺序 。如将 x=a*b/c*d 写成 x=(a*b/c)*d可避免阅读者误解为x=(a*b)/(c*d) 。
——避免不必要的转移 。
——避免采用过于复杂的条件测试 。
——避免过多的循环嵌套和条件嵌套 。
——建议不要使用 *= , ^=, /=等运算符 。
——一个函数不要超过200行 。一个文件应避免超过2000行 。
——尽量避免使用go to语句 。
——避免采用多赋值语句,如x = y = z ;
——不鼓励采用?:操作符,如z = (ab)?a:b;
——不要使用空的if else 语句 。如
if(cMychar = ‘A’)
if(cMychar = ‘Z’)
printf(“This is a letter \n”);
else
printf(“This is not a letter \n”);
else到底是否定哪个if容易引起误解 。可通过加{}避免误解 。
——尽量减少使用“否定”条件的条件语句 。如:
把 if( !( (cMychar’0’) || (cMychar’9’) ) )
改为if( (cMychar=’0’)(cMychar=’9’)
【go语言操作bsc合约 go语言编写智能合约】关于go语言操作bsc合约和go语言编写智能合约的介绍到此就结束了 , 不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读