區塊鏈研究實驗室|在Go中測試和部署以太坊智慧合約的操作指南

買賣虛擬貨幣

在本文中,我們將逐步研究如何使用Go程式語言測試,部署和與簡單的以太坊智慧合約進行互動。

網路上有很多教程,詳細介紹瞭如何部署以太坊智慧合約並與之互動。 但是這些教程僅以Javascript或Javascript和Go的某種組合來完成。

作為新興的Gopher和以太坊發燒友,我想使用Go在以太坊上構建,測試,部署和與智慧合約進行互動。 但是我找不到簡單的逐步指南來幫助我入門。我不得不從不同的來源收集資訊,並透過Go Ethereum原始碼進行挖掘,以理解並整理所有必要的難題。 在這篇文章中,我將嘗試使用我所學到的知識來編寫一個易於遵循的分步指南,以期幫助其他人快速使用Go和以太坊進行生產力。

在以下情況下,這篇文章對您最有用:

1. 您熟悉Go程式語言,並且已經設定了Go開發環境。

2. 您對以太坊區塊鏈和相關的智慧合約有基本的瞭解。

3. 您已經接觸過用於編寫智慧合約的Solidity程式語言。

安裝Go-Ethereum和相關的開發工具

首先,我們需要為以太坊協議安裝Go繫結。假設您已安裝Go並正確設定了GOPATH環境變數,則可以透過以下方式獲取繫結:

go get -d github.com/ethereum/go-ethereum

一旦我們檢查了原始碼,就可以繼續構建geth(Geth是用於執行基於Go的以太坊節點的完整實現的主要命令列工具)以及所有開發工具:

$cd$GOPATH/src/github.com/ethereum
$ go install ./...

設定專案結構

我們將編寫並部署一個簡單的inbox合約。為此讓我們首先設定以下目錄和檔案結構:

# Navigate to your Go src directory. Mine looks like: 
# $GOPATH/src/github.com/sabbas
$ cd $GOPATH/src/github.com/sabbas
$ mkdir -p inbox/contracts
$ touch contracts/inbox_test.go fetch.go update.go deploy.go
$ tree inbox/
inbox/
├── contracts
│   └── inbox_test.go
├── deploy.go
├── fetch.go
└── update.go

我們建立一個名為inbox的新專案資料夾。在此資料夾中,我們建立一個名為contract的包資料夾,該資料夾將包含我們inbox智慧合約的Solidity程式碼及其關聯的Go繫結(我們將很快生成)。在智慧合約包資料夾中,我們還有一個inbox_test.go檔案。該檔案將包含我們所有的測試。 還有三個其他檔案,分別名為deploy.go,fetch.go和update.go。我們將使用這些附加檔案來編寫程式碼以在公共網路上部署以太坊智慧合約並與之互動,這些檔案現在可以為空。

建立一個簡單的以太坊合約

現在我們準備為inbox智慧合約編寫一些Solidity程式碼。導航到inbox/contracts資料夾,然後建立一個名為Inbox.sol的檔案。

$ tree inbox/
inbox/
└── contracts
    └── Inbox.sol

編輯inbox.sol檔案,併為我們的Inbox智慧合約加以下Solidity程式碼行:

pragma solidity ^0.4.17;

contract Inbox {

stringpublic message;

function Inbox(string initialMessage)public{
        message = initialMessage;
    }

function setMessage(string newMessage)public{
        message = newMessage;
    }
}

inbox智慧合約非常簡單。它具有一個稱為message的公共資料變數,該變數儲存最新訊息字串的內容。 該智慧合約還定義了一個公共setMessage方法,該方法更新訊息資料變數的內容。

使用Go中的以太坊合約

現在我們已經在Solidity中定義了inbox智慧合約,我們希望能夠在Go應用程式中使用此智慧合約。更具體地說,我們希望能夠將此智慧合約部署到以太坊網路上,並可以在Go應用程式中方便地與之互動。Go-Ethereum透過提供一個程式碼生成器工具使此操作非常簡單,該工具可以將Solidity智慧合約檔案轉換為型別安全的go包,我們可以直接從Go應用程式中匯入和使用它。 該工具稱為abigen,它是在上述go-ethereum設定期間構建和安裝的。要使用abigen,請導航到“inbox/contracts”資料夾並執行:

$ abigen -sol inbox.sol -pkg contracts -out inbox.go
$ tree inbox
inbox
└── contracts
    ├── Inbox.sol
    └── inbox.go

我們將要為其生成Go包的Solidity智慧合約檔案的名稱傳遞給sol命令列引數。我們還為pkg和out命令列引數指定Go軟體包名稱和輸出檔名稱。執行abigen會生成inbox.go軟體包檔案,其中包含Inbox Solidity合同的Go繫結。 一旦有了這些繫結,就可以開始測試inbox智慧合約了。

在部署到公共網路之前測試以太坊合約

在將智慧合約部署到公共以太坊網路之前,我們要確保其按預期執行。對於inbox智慧合約,我們要測試是否可以使用初始訊息來部署智慧合約,檢索此初始訊息並稍後更新其值。Go-Ethereum為區塊鏈模擬器提供了一個很好的實用程式,對自動化單元測試非常有幫助。在隨後的程式碼片段中,我們將看到如何使用區塊鏈模擬器應用程式來測試inbox智慧合約。

package contracts

import (
"testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core"
"math/big"
)

// Test inbox contract gets deployed correctly
funcTestDeployInbox(t *testing.T) {

//Setup simulated block chain
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
alloc := make(core.GenesisAlloc)
alloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(1000000000)}
blockchain := backends.NewSimulatedBackend(alloc)

//Deploy contract
address, _, _, err := DeployInbox(
auth,
blockchain,
"Hello World",
)
// commit all pending transactions
blockchain.Commit()
if err != nil {
t.Fatalf("Failed to deploy the Inbox contract: %v", err)
}

iflen(address.Bytes()) == 0 {
t.Error("Expected a valid deployment address. Received empty address byte array instead")
}

}

TestDeployInbox方法透過呼叫crypto.GenerateKey生成私鑰開始。該金鑰用於建立交易簽名器功能,用於授權模擬區塊鏈中的交易。透過對bind.NewKeyedTransactor的呼叫,生成了交易簽名者函式以及一個我們可以用來進行交易的地址。然後,該地址用於建立一個創始塊,該創始塊包含具有一些初始餘額的單個帳戶(透過呼叫make(core.GenesisAlloc和core.GenesisAccount)。然後該創世塊用於為模擬區塊鏈播種。最後我們“ 透過明確地提交所有未決交易並檢查inbx智慧合約是否已部署到有效地址來進行下一步。

我們可以導航到inbox\contracts並執行go test以確保部署測試透過

go test -v
=== RUN   TestDeployInbox
--- PASS: TestDeployInbox (0.01s)
PASS
ok   github.com/sabbas/inbox/contracts 0.042s

接下來,我們要測試是否在部署inbox智慧合約時使用正確的初始訊息進行部署。

package contracts

import (
"testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core"
"math/big"
)

//Test initial message gets set up correctly
funcTestGetMessage(t *testing.T) {

//Setup simulated block chain
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
alloc := make(core.GenesisAlloc)
alloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(1000000000)}
blockchain := backends.NewSimulatedBackend(alloc)

//Deploy contract
_, _, contract, _ :=DeployInbox(
auth,
blockchain,
"Hello World",
)

// commit all pending transactions
blockchain.Commit()

if got, _ := contract.Message(nil); got != "Hello World" {
t.Errorf("Expected message to be: Hello World. Go: %s", got)
}

}

與TestDeployInbox函式類似,TestGetMessage函式透過設定模擬的區塊鏈開始(模擬的區塊鏈函式可以重構為單獨的可重用函式。我在這裡避免這樣做只是為了提高可讀性)。然後它為Inbox Solidity合同呼叫作為自動生成的Go繫結的一部分而建立的DeployInbox函式。請注意,DeployInbox函式返回一個指向已部署Inbox合同例項的指標作為第三個返回值。我們可以使用此指標與已部署的收件箱合同進行互動(超級酷!)。這正是測試要執行的查詢和檢查儲存在我們剛剛部署的收inbox智慧合約例項中的初始訊息的方法。

我們可以導航到inbox\contracts並執行go test以確保我們的測試透過

$ go test -v
=== RUN   TestDeployInbox
--- PASS: TestDeployInbox (0.01s)
=== RUN   TestGetMessage
--- PASS: TestGetMessage (0.00s)
PASS
ok   github.com/sabbas/inbox/contracts 0.045s

最後,我們要測試是否可以將已部署的inbox智慧合約中的訊息資料屬性更新為新值。

package contracts

import (
"testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core"
"math/big"
)

// Test message gets updated correctly
funcTestSetMessage(t *testing.T) {


//Setup simulated blockchain
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
alloc := make(core.GenesisAlloc)
alloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(1000000000)}
blockchain := backends.NewSimulatedBackend(alloc)

//Deploy contract

_, _, contract, _ :=DeployInbox(
auth,
blockchain,
"Hello World",
)

// commit all pending transactions
blockchain.Commit()
contract.SetMessage(&bind.TransactOpts{
From:auth.From,
Signer:auth.Signer,
Value: nil,
}, "Hello from Mars")

blockchain.Commit()

if got, _ := contract.Message(nil); got != "Hello from Mars" {
t.Errorf("Expected message to be: Hello World. Go: %s", got)
}

TestSetMessage函式以我們之前看到的用於設定模擬區塊鏈和部署inbox智慧合約的相同樣板程式碼開始。 如前所述,成功呼叫DeployInbox函式呼叫將返回指向已部署Inbox智慧合約例項的指標,作為第三個返回值。在TestSetMessage函式中,我們使用此智慧合約指標透過呼叫SetMessage函式來更新Inbox合同的訊息資料屬性。由於SetMessage函式修改了inbox智慧合約,因此實際上會生成一個新事務。結果我們傳入一個指向填充有交易授權資料的TrasactOpts結構的指標。由於我們不需要與SetMessage呼叫一起傳送任何資金,因此我們將TransactOpts結構的Value屬性設定為nil。

我們可以導航到inbox\contracts,然後再執行一次go test,以確保一切正常。

$ go test -v
=== RUN   TestDeployInbox
--- PASS: TestDeployInbox (0.01s)
=== RUN   TestGetMessage
--- PASS: TestGetMessage (0.00s)
=== RUN   TestSetMessage
--- PASS: TestSetMessage (0.01s)
PASS
ok   github.com/sabbas/inbox/contracts 0.051s

一旦我們的本地測試透過,我們的inbox智慧合約就可以準備好了。 在下一部分中,我們將研究如何獲得Inbox智慧合約並將其部署在公共以太坊網路上。

我們將在Rinkeby Test Ethereum公共網路上部署Inbox智慧合約。 在以太坊主網路上部署以太坊合約並與之互動需要花費實際金錢,而在我們只是學習或玩耍時,這並不需要。

要在Rinkeby之類的公共網路上部署以太坊合約並與之互動,需要採取以下幾項措施:

  • 我們需要在網路上擁有一個有資金(以太幣)的帳戶

  • 我們需要能夠連線到以太坊節點並與之互動。

在以下各節中,我們將依次介紹這些內容。

使用Metamask建立帳戶

我們可以直接從之前構建的geth cli建立和管理帳戶。但是剛開始時,我發現使用瀏覽器中的Metamask Chrome擴充套件程式要簡單得多。 安裝Metamask並建立一個帳戶非常簡單。

一旦啟動並執行Metamask並建立了一個帳戶,我們就要確保將Metamask指向Rinkeby Test網路

向賬戶注資

Rinkeby網路執行著一個測試水龍頭(https://faucet.rinkeby.io/),我們可以使用它來請求以太坊。 但是要從該測試水龍頭請求以太幣,我們首先需要釋出一個僅包含以太坊賬戶地址的帖子(該賬戶是我們要將資金轉入的賬戶,我們可以透過單擊賬戶名稱旁邊的省略號來複制該地址在社交網站上的Metamask擴充套件中)並提供指向該帖子的連結。

這將需要幾秒鐘,我們應該會在Metamask擴充套件程式的帳戶中看到資金顯示。

連線到以太坊節點

我們可以使用geth在Rinkeby測試網路上啟動和管理我們自己的以太坊節點。這既耗費資源和時間,也不總是一個平穩的過程。更好的選擇是連線到第三方提供商託管的執行中的以太坊節點。Infura是這樣的提供者之一。我們可以註冊一個Infura免費帳戶。註冊後,Infura將向我們傳送URL,以連線到執行在不同以太坊網路上的節點。Rinkeby測試網路的URL應該類似於https://rinkeby.infura.io/fYE8qC0WiMx4ZAX4Voff。

為我們的帳戶生成加密的JSON金鑰

為了透過Go-Ethereum在Rinkeby等公共以太坊網路上部署和互動合約,我們需要為透過上述Metamask建立的賬戶使用一個加密的json金鑰。這是我們要收取的用於部署和與Inbox智慧合約進行互動的帳戶。

我們可以透過以下方式來生成此JSON金鑰:將Metamask中的帳戶匯出到檔案中,然後將其私鑰(單擊metamask中的帳戶名稱旁邊的省略號以獲取匯出私鑰選項)到檔案中,然後透過geth匯入。完成匯入後,請確保刪除私鑰檔案。

geth account import path/to/private/key/file

上面的命令將在Mac和〜/.ethereum上的〜/Library/Ethereum/keystore中生成一個加密的JSON金鑰檔案。注意該檔案, 在Rinkeby測試網路上部署Inbox智慧合約並與之互動時,我們將在下一部分中使用其內容。

最後的邊疆

我們終於到了準備將Inbox智慧合約部署到Rinkeby網路的地步。首先讓我們看一下我們先前建立的deploy.go檔案中的程式碼,然後詳細介紹它。

package main
import (
"github.com/ethereum/go-ethereum/ethclient"
"log"
"github.com/sabbas/inbox/contracts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"strings"
"fmt"
"os"
)

const key  = `paste the contents of your JSON key file here`
funcmain(){
// connect to an ethereum node  hosted by infura
blockchain, err := ethclient.Dial("https://rinkeby.infura.io/fYe8qCnWi6TXZAXOVof9")

if err != nil {
log.Fatalf("Unable to connect to network:%v\n", err)
}

// Get credentials for the account to charge for contract deployments
auth, err := bind.NewTransactor(strings.NewReader(key), "passphrase associated with your JSON key file")

if err != nil {
log.Fatalf("Failed to create authorized transactor: %v", err)
}
address, _, _, _:= contracts.DeployInbox(
auth,
blockchain,
"Hello World",
)

fmt.Printf("Contract pending deploy: 0x%x\n", address)}

我們將ethclient.Dial方法與我們先前生成的Infura url一起使用,以連線到Rinkeby測試網路上的以太坊節點。然後,我們使用bind.NewTransactor實用程式方法從金鑰檔案的內容(您可以使用geth account list命令檢索您的JSON金鑰檔案在系統上的位置)及其相關密碼建立一個授權的事務處理程式,我們透過 geth帳戶匯入。從那時起,部署智慧合約的程式碼與使用模擬區塊鏈部署inbox智慧合約時所見的程式碼完全相同。最後如果一切順利,我們將列印inbox智慧合約在網路上部署的地址。

go run deploy.go
Contract pending deploy: 0x491c7fd67ac1f0afeceae79447cd98d2a0e6a9ff

挖掘該智慧合約併成為區塊鏈的一部分將需要幾分鐘。我們可以透過以下位置透過etherscan來檢查狀態:https://rinkeby.etherscan.io/address/ [合約地址]。 就我而言,這是https://rinkeby.etherscan.io/address/0x491c7fd67ac1f0afeceae79447cd98d2a0e6a9ff

一旦它被挖掘幷包含在區塊鏈中,我們就可以使用已部署的Inbox智慧合約的地址開始與它進行互動。例如我們可以將下面的程式碼放在上面建立的fetch.go中,以檢索初始訊息。

package main

import (
"github.com/ethereum/go-ethereum/ethclient"
"log"
"github.com/sabbas/inbox/contracts"
"fmt"
"github.com/ethereum/go-ethereum/common"
)

funcmain(){
// connect to an ethereum node  hosted by infura
blockchain, err := ethclient.Dial("https://rinkeby.infura.io/fYe8qCnWiM9gh&ZAXOVoff")


if err != nil {
log.Fatalf("Unable to connect to network:%v\n", err)
}


// Create a new instance of the Inbox contract bound to a specific deployed contract
  contract, err :=contracts.NewInbox(common.HexToAddress("0x491c7fd67ac1f0afeceae79447cd98d2a0e6a9ff"), blockchain)
if err != nil {
log.Fatalf("Unable to bind to deployed instance of contract:%v\n")
}

fmt.Println(contract.Message(nil))

}

如前所示,我們對早期生成的Infura url使用ethclient.Dial方法來連線到Rinkeby測試網路上的以太坊節點。 然後我們使用作為Solidity合約的Go繫結的一部分生成的NewInbox方法,將Inbox合約的例項附加到部署在特定地址的Inbox合約。 最後,我們訪問智慧合約上的Message屬性,該屬性應輸出Hello World。

go run interact.go
Hello World <nil>

我們還可以更新已部署的inbox智慧合約上的Message屬性。 為此請將下面的程式碼放在之前建立的update.go檔案中。

package main


import (
"github.com/ethereum/go-ethereum/ethclient"
"log"
"github.com/sabbas/inbox/contracts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"strings"
"fmt"
"os"
)

const key  = `paste the contents of your JSON key file here`

funcmain(){
// connect to an ethereum node  hosted by infura
blockchain, err := ethclient.Dial("https://rinkeby.infura.io/fYe8qCnWi6TXZAXOVof9")

if err != nil {
log.Fatalf("Unable to connect to network:%v\n", err)
}

// Get credentials for the account to charge for contract deployments
auth, err := bind.NewTransactor(strings.NewReader(key), "passphrase associated with your JSON key file")

if err != nil {
log.Fatalf("Failed to create authorized transactor: %v", err)
}
  contract, err :=contracts.NewInbox(common.HexToAddress("0x491c7fd67ac1f0afeceae79447cd98d2a0e6a9ff"), blockchain)
if err != nil {
log.Fatalf("Unable to bind to deployed instance of contract:%v\n")
}

contract.SetMessage(&bind.TransactOpts{
From:auth.From,
Signer:auth.Signer,
Value: nil,
}, "Hello From Mars")
}

回想一下,SetMessage函式修改了inbox合約並實際上生成了一個新事務。結果我們傳入一個指向填充有交易授權資料的TrasactOpts結構的指標。同樣這筆交易需要幾分鐘的時間才能被開採併成為區塊鏈的一部分。一旦將其挖掘幷包含在區塊鏈中,我們可以按照前面的示例從已部署的智慧合約中檢索Message屬性,以檢視更新後的值。

以太坊(Go Ethereum)我們可以做很多事情,但是剛開始可能有點麻煩。 希望這篇文章為您提供了一個起點,從那裡您可以進一步探索。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;