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

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

智能合约调用是实现一个 DApp 的关键,一个完整的 DApp 包括前端、后端、智能合约及区块 链系统,智能合约的调用是连接区块链与前后端的关键。

专注于为中小企业提供网站建设、网站设计服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业临潼免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上1000家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。

我们先来了解一下智能合约调用的基础原理。智能合约运行在以太坊节点的 EVM 中。因此要 想调用合约必须要访问某个节点。

以后端程序为例,后端服务若想连接节点有两种可能,一种是双 方在同一主机,此时后端连接节点可以采用 本地 IPC(Inter-Process Communication,进 程间通信)机制,也可以采用 RPC(Remote Procedure Call,远程过程调用)机制;另 一种情况是双方不在同一台主机,此时只能采用 RPC 机制进行通信。

提到 RPC, 读者应该对 Geth 启动参数有点印象,Geth 启动时可以选择开启 RPC 服务,对应的 默认服务端口是 8545。。

接着,我们来了解一下智能合约运行的过程。

智能合约的运行过程是后端服务连接某节点,将 智能合约的调用(交易)发送给节点,节点在验证了交易的合法性后进行全网广播,被矿工打包到 区块中代表此交易得到确认,至此交易才算完成。

就像数据库一样,每个区块链平台都会提供主流 开发语言的 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,相信读者也知道运行结果是正确的了。

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

Fabric区块链开发详解,本课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、通道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、链码与应用开发的操作实践,是学习Fabric区块链开发的最佳选择。

给你个免费区块链博客看看。

如何看待go语言泛型的最新设计?

Go 由于不支持泛型而臭名昭著,但最近,泛型已接近成为现实。Go 团队实施了一个看起来比较稳定的设计草案,并且正以源到源翻译器原型的形式获得关注。本文讲述的是泛型的最新设计,以及如何自己尝试泛型。

例子

FIFO Stack

假设你要创建一个先进先出堆栈。没有泛型,你可能会这样实现:

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() 时,返回的值已经是原始类型,并且没有机会返回错误的值类型。这种方式更安全,更容易使用。(译注:就是看起来更丑陋,^-^)

此外,泛型代码通常更易于编译器优化,从而获得更好的性能(以二进制大小为代价)。如果我们对上面的非泛型代码和泛型代码进行基准测试,我们可以看到区别:

type MyObject struct {

int

}

var sink MyObjectfunc BenchmarkGo1(b *testing.B) {

for i := 0; i  b.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; i  b.N; i++ {

var s Stack(MyObject)

s.Push(MyObject{})

s.Push(MyObject{})

s.Pop()

sink = s.Peek()

}

}

结果:

BenchmarkGo1BenchmarkGo1-16     12837528         87.0 ns/op       48 B/op        2 allocs/opBenchmarkGo2BenchmarkGo2-16     28406479         41.9 ns/op       24 B/op        2 allocs/op

在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍。

合约(Contracts)

上面的堆栈示例适用于任何类型。但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码。例如,你可能希望堆栈要求类型实现 String() 函数

C语言的书写规则主要有哪些呢?

1.

1.1符号命名规则

1.1.1符号名包括模块名、常量名、标号名、子程序名等。这些名字应该能反映它所代表的实际东西,具有一定的意义,使其能够见名知义,有助于对程序功能的理解。命名采用匈牙利命名法。规则如下:

(1)所有宏定义、枚举常数和const变量,用大写字母命名。在复合词里用下划线隔开每个词。

(2)复合词中每个单词的第一个字母大写。除了规则5.1.1.1以外,避免使用下划线。

(3)类、类型定义和枚举型名的第一个字母大写。

(4)函数名是复合词的,第一个词采用全部小写,随后每个单词采用第一个字母大写,其它字母小写方式;如果是单个词的,采用全部小写方式。

(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 = amount + total;

这样的注释仅仅是重复了下面的程序,对于理解它的工作并没有什么作用。而下面的注释,有助于读者理解。

/*将每月的销售额amount加到年销售额total中*/

total = amount + total;

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语言智能合约
分享网址:http://myzitong.com/article/ddcoioc.html