使用Go与以太坊智能合约交互

尽管最近遇到了些麻烦,但以太坊仍然是区块链领域内智能合约的最大参与者,这似乎不会很快改变。

在我看来,技术本身具有很大的潜力,是从学术的角度看很有意思,但正如上面提到的问题和之前的许多问题是区块链技术方面的,智能合约,特别是具有Solidity的以太坊生态系统还是不太成熟的,并没有为prime-time和生产用例做好准备。

然而,这是一个学习和了解这项技术的好时机,并且当它达到严格应用程序的可接受成熟度前,需要做好准备。

在我之前关于Solidity的帖子中,我创建了一个带有简单的Winner-Takes-All Crowdfunding合约的小应用程序。在这篇文章中,我们将用到其中的contract.sol,看看我们是否可以使用Go部署它并与之交互。

为什么是go?好问题,go语言是令人惊讶的而且使用最广泛的以太坊客户端使用go写的,这意味着有一个很好的生态系统使用已经转为具有很好的功能,如代码生成和可重复使用的助手和智能合约互动的共享库。

在这个例子中,我们将不使用真实的区块链作为部署目标,而是使用由go-ethereum,所以我们可以安全地测试并没有花费任何金钱实验提供的SimulatedBackend。

智能合约本身非常简单:我不会详细介绍它的功能或工作方式,因为已经涵盖了这一点。可以说,合约部署有3个参数:

  • 项目的最低入场费。
  • 提交新项目的截止日期。
  • 支持项目的截止日期。

然后,在第一阶段,可以使用名称和网址提交项目,其中至少包含交易的最低费用。在第二阶段,可以通过将以太发送到合约中的地址来支持项目。

但是,在这篇文章中,我们将重点关注:

  • 部署合约
  • 从合约中读取数据
  • 与合约互动(交易)
  • 通过地址实例化已部署的合约

我们将在Go和70行代码中完成所有操作。让我们开始吧!

代码示例

为了能够跟踪,你需要一些东西。首先也是最重要的是,你需要solc Solidity编译器。

然后,只需获取go-ethereum并构建它:

1
2
3
4
go get github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum/
make
make devtools

好吧,使用solc和geth devtools,我们可以从生成contract.sol文件的Go版本开始,该文件包含我们的智能合约:

1
abigen --sol=Contract.sol --pkg=main --out=contract.go

生成的代码看这里

如你所见,我们有部署和实例化合约的方法,以及所有公共合约方法到Go的映射。

下一步是将合约部署到模拟后端。

为此,需要进行一些设置。如上所述,为了简单起见,我们将使用SimulatedBackend作为我们的目标区块链,但在本文的最后,将有一个关于如何使用testnet甚至真正的以太坊区块链进行此操作的简短部分。

使用go-ethereum的一些依赖关系,我们可以开始设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import(
"fmt"
"log"
"math/big"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
)

func main() {
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)

alloc := make(core.GenesisAlloc)
alloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(133700000)}
sim := backends.NewSimulatedBackend(alloc)

我们只需创建一个密钥,用一堆以太创建一个Genesis帐户并启动模拟后端,它会返回一个bind.ContractBackend。

现在我们可以使用生成的DeployWinnerTakesAll方法部署合约:

1
2
3
4
addr, _, contract, err := DeployWinnerTakesAll(auth, sim, big.NewInt(10), big.NewInt(time.Now().Add(2*time.Minute).Unix()), big.NewInt(time.Now().Add(5*time.Minute).Unix()))
if err != nil {
log.Fatalf("could not deploy contract: %v", err)
}

我们使用bigInt传递一个auth对象,它表示我们的身份,后端sim和最小入场费,项目截止日期和活动截止日期的值。该方法返回将部署合约的地址以及合约的句柄和错误。还有一个返回的交易对象,但我们不会在这里处理它。

现在合约已经部署,我们应该能够与它进行交互。例如,我们可以检查我们发送的截止日期是否在合约中正确设置:

1
2
deadlineCampaign, _ := contract.DeadlineCampaign(nil)
fmt.Printf("Pre-mining Campaign Deadline: %s\n", deadlineCampaign)

但是,如果我们执行此操作,我们会在截止日期前返回。也就是说,因为我们的合约尚未开采。如果我们使用真实网络作为后端,我们将不得不等到发生这种情况,但是使用我们的模拟后端,我们可以简单地执行此操作:

1
2
3
4
5
fmt.Println("Mining...")
sim.Commit()

postDeadlineCampaign, _ := contract.DeadlineCampaign(nil)
fmt.Printf("Post-mining Campaign Deadline: %s\n", time.Unix(postDeadlineCampaign.Int64(), 0))

我们回到部署期间设置的日期:

1
Post-mining Campaign Deadline: 2017-07-23 20:37:22 +0200 CEST

ok。因此,我们可以读取合约中公开的数据。现在我们想与它互动。在这种情况下,最简单的事情是我们通过发送一个带有名称和项目网址的交易来提出一个新项目,其中至少有最低费用作为值:

1
2
3
4
5
6
7
8
9
10
numOfProjects, _ := contract.NumberOfProjects(nil)
fmt.Printf("Number of Projects before: %d\n", numOfProjects)

fmt.Println("Adding new project...")
contract.SubmitProject(&bind.TransactOpts{
From: auth.From,
Signer: auth.Signer,
GasLimit: big.NewInt(2381623),
Value: big.NewInt(10),
}, "test project", "http://www.example.com")

当然我们需要继续挖矿:

1
2
3
4
5
6
7
fmt.Println("Mining...")
sim.Commit()

numOfProjects, _ = contract.NumberOfProjects(nil)
fmt.Printf("Number of Projects after: %d\n", numOfProjects)
info, _ := contract.GetProjectInfo(nil, auth.From)
fmt.Printf("Project Info: %v\n", info)

…但是我们得到以下输出:

1
2
3
4
5
Number of Projects before: 0
Adding new project...
Mining...
Number of Projects after: 1
Project Info: {test project http://www.example.com 0}

太棒了,这意味着我们的项目已经创建。因此,我们能够部署合约,对其进行读写。

但是如果合约已经部署并且我们只想与它进行交互怎么办?幸运的是,生成的代码包含一个NewWinnerTakesAll方法,它只使用已部署合约的地址,允许我们实例化合约:

1
2
3
4
5
6
instContract, err := NewWinnerTakesAll(addr, sim)
if err != nil {
log.Fatalf("could not instantiate contract: %v", err)
}
numOfProjects, _ = instContract.NumberOfProjects(nil)
fmt.Printf("Number of Projects of instantiated Contract: %d\n", numOfProjects)

我们获得与已部署合约相同的返回值,并且可以与此版本完全相同的方式进行交互,该版本由地址实例化。

好的,所以我们完成了与合约有意义地交互所需的所有步骤,但仅限于模拟的后端。为了使用testnet或真正的以太坊区块链,我们只需要调整一些事项:

1
2
3
4
5
6
7
8
9
const key = "your key json"
conn, err := rpc.NewIPCClient("/path/to/your/.ethereum/testnet/geth.ipc")
if err != nil {
log.Fatalf("could not create ipc client: %v", err)
}
auth, err := bind.NewTransactor(strings.NewReader(key), "your password")
if err != nil {
log.Fatalf("could not create auth: %v", err)
}

这会产生我们自己创建的auth对象。当然,请不要在代码中使用纯文本的密钥和/或密码,而是以安全的方式加载它们。:)

如果已经部署了合约,我们不需要创建NewIPCClient,但只需Dial一个节点:

1
2
3
4
conn, err := ethclient.Dial("/path/to/your/.ethereum/testnet/geth.ipc")
if err != nil {
log.Fatalf("could not connect to remote node: %v", err)
}

如此而已!可以在此处找到该示例的完整代码。

结论

正如我在本文开头所述,在我看来,依靠Solidity智能合约进行认真的应用还为时尚早,但这种以及其他几种基于区块链的智能合约方法的潜力巨大,因此了解周边技术肯定是值得的。

Go非常适合与基于以太坊的智能合约交互的任务,因为有很多来自geth的可重用代码,甚至还有一些关于如何开始使用的文档。这当然可以用任何其他语言来实现(例如:使用web3),但如果Go是你喜欢的,它似乎是一个可靠的选择。:)

======================================================================

分享一些比特币、以太坊、EOS、Fabric等区块链相关的交互式在线编程实战教程:

  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • c#比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在C#代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是C#工程师不可多得的比特币开发学习课程。
  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • ERC721以太坊通证实战,课程以一个数字艺术品创作与分享DApp的实战开发为主线,深入讲解以太坊非同质化通证的概念、标准与开发方案。内容包含ERC-721标准的自主实现,讲解OpenZeppelin合约代码库二次开发,实战项目采用Truffle,IPFS,实现了通证以及去中心化的通证交易所。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • EOS入门教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • 深入浅出玩转EOS钱包开发,本课程以手机EOS钱包的完整开发过程为主线,深入学习EOS区块链应用开发,课程内容即涵盖账户、计算资源、智能合约、动作与交易等EOS区块链的核心概念,同时也讲解如何使用eosjs和eosjs-ecc开发包访问EOS区块链,以及如何在React前端应用中集成对EOS区块链的支持。课程内容深入浅出,非常适合前端工程师深入学习EOS区块链应用开发。
  • Hyperledger Fabric 区块链开发详解,本课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、nodejs链码与应用开发的操作实践,是Nodejs工程师学习Fabric区块链开发的最佳选择。
  • Hyperledger Fabric java 区块链开发详解,课程面向初学者,内容即包含Hyperledger Fabric的身份证书与MSP服务、权限策略、信道配置与启动、链码通信接口等核心概念,也包含Fabric网络设计、java链码与应用开发的操作实践,是java工程师学习Fabric区块链开发的最佳选择。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是使用Go与以太坊智能合约交互