与GETH模拟后端的智能合约交互
我正在发布我的“hacky”golang测试环境,该环境深入链接到go-ethereum代码库。
这是该系列的下一个……
现在您已经运行了模拟的以太坊网络,现在是时候看到EVM在运行了。
GETH附带了一个非常有用的工具,可以帮助您将GO代码与可靠的智能合约集成。
介绍ABIGEN
ABIGEN围绕您的智能合约创建一个盘点器,以帮助您进行大多数您可能想要执行的交互。
我通常将合约存储在具有适当名称的子文件夹中。
在这种情况下,我正在为Devcon 5拍卖合约进行测试。我将它放在contracts
文件夹中。
您可以在此处找到实际的拍卖合约
https://etherscan.io/address/0x096bE08D7d1CaeEA6583eab6b75a0f5EaaB012a5#code
如果我们将该源代码放入auction.sol
合约文件夹中,您将创建盘点器:
abigen --sol contracts/auction.sol --pkg contracts --out contracts/auction.go
合约的名称是auction
这样的,ABIGEN将创建一个名为的函数DeployAuction
您会注意到构造函数需要一些日期参数,金额和钱包地址,我们首先创建一个辅助函数。
func chkerr(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
// I like British dates !
func getTime(dateStr string) *big.Int {
t, err := time.Parse("02/01/06", dateStr)
chkerr(err)
return big.NewInt(t.Unix())
}
现在创建构造函数变量
startBids := getTime("13/09/19")
endBids := getTime("15/09/19")
startReveal := endBids
endReveal := getTime("17/09/19")
minimumBid, _ := etherUtils.StrToEther("4.7")
wallet, _ := memorykeys.GetAddress("wallet")
现在部署合约……
bankTx, _ := memorykeys.GetTransactor("banker")
auctionAddress, tx, auctionContract, err := contracts.DeployAuction(bankTx, client, startBids, endBids, startReveal, endReveal minimumBid, *wallet)
chkerr(err)
fmt.Println(auctionAddress.Hex(), tx.Hash().Hex())
我们需要一个绑定的transactor(参见memorykeys post)然后我们发送事务来部署返回的合约
- 合约的地址
- 允许我们与它进行交易的绑定对象(一旦部署)
- 事务对象
- 一个错误对象(像往常一样)
我们现在可以导致该交易被挖矿
client.Commit()
然后我们可以在合约中调用一个方法,让我们检查最低出价
min, err := auctionContract.MinimumBid(nil)
chkerr(err)
fmt.Println("minumum bid is ", etherUtils.EtherToStr(min))
运行testAuction.go我们将获得随机分配的地址,但最低出价将被清楚地看作是4.7
$go run testAuction.go
0x8095E4E397c8BEDffE7d2c8E3EaA30F646aab6dC 0x24fa2c113beab3eecf2129ef868e9121c3a7d8f7e084c6c66fbd10cb67b680a5
4.700000000000000000
奖金 – 瓶中的时间
Devcon5拍卖合约是一项时间依赖合约。分为三个阶段:
- 招标期
- 揭示时期
- 提款期
如果我们想测试这样的合约,我们需要能够加快区块链时钟到达特定时间。
获得模拟区块链的时间
func currentTime() uint64 {
client, err := getClient()
chkerr(err)
block := client.Blockchain().CurrentBlock()
return block.Time()
}
及时跳跃
我对go-ethereum代码库的小贡献是AdjustTime
模拟后端的功能。
func jumpTo(newTime *big.Int) {
client, err := getClient()
chkerr(err)
now := client.Blockchain().CurrentBlock().Time()
target := newTime.Uint64()
if now >= target {
return
}
err = client.AdjustTime(time.Duration(target-now) * time.Second)
chkerr(err)
client.Commit()
}
我们还可以创建一个功能来报告投标是否公开
func isBiddingOpen(auction *contracts.Auction) {
biddingOpen, err := auction.InBidding(nil)
chkerr(err)
state := "IS NOT"
if biddingOpen {
state = "IS"
}
fmt.Println("Bidding", state, "open")
}
因此,这允许我们跳转到测试的开始。
fmt.Println("time:",currentTime())
isBiddingOpen(auctionContract)
jumpTo(startBids)
fmt.Println("time:",currentTime())
isBiddingOpen(auctionContract)
$go run testAuction.go
0x06cfB9BD9a3093603EFf47BC0679A729AF6a884c 0x0bf7dd3223934f955981816fda317e7fa6b669252c5a5695c71d3d82451b604f
minumum bid is 4.700000000000000000
time : 10
Bidding IS NOT open
time : 1568332810
Bidding IS open
完整的代码
package main
import (
"fmt"
"log"
"math/big"
"os"
"time"
"./contracts"
"github.com/DaveAppleton/etherUtils"
"github.com/DaveAppleton/memorykeys"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
)
var baseClient *backends.SimulatedBackend
func getClient() (client *backends.SimulatedBackend, err error) {
if baseClient != nil {
return baseClient, nil
}
funds, _ := etherUtils.StrToEther("10000.0")
bankerAddress, err := memorykeys.GetAddress("banker")
if err != nil {
return nil, err
}
baseClient = backends.NewSimulatedBackend(core.GenesisAlloc{
*bankerAddress: {Balance: funds},
}, 8000000)
return baseClient, nil
}
func chkerr(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func getTime(dateStr string) *big.Int {
t, err := time.Parse("02/01/06", dateStr)
chkerr(err)
return big.NewInt(t.Unix())
}
func currentTime() uint64 {
client, err := getClient()
chkerr(err)
block := client.Blockchain().CurrentBlock()
return block.Time()
}
func jumpTo(newTime *big.Int) {
client, err := getClient()
chkerr(err)
now := client.Blockchain().CurrentBlock().Time()
target := newTime.Uint64()
if now >= target {
return
}
err = client.AdjustTime(time.Duration(target-now) * time.Second)
chkerr(err)
client.Commit()
}
func isBiddingOpen(auction *contracts.Auction) {
biddingOpen, err := auction.InBidding(nil)
chkerr(err)
state := "IS NOT"
if biddingOpen {
state = "IS"
}
fmt.Println("Bidding", state, "open")
}
func main() {
client, err := getClient()
if err != nil {
log.Fatal(err)
}
startBids := getTime("13/09/19")
endBids := getTime("15/09/19")
startReveal := endBids
endReveal := getTime("17/09/19")
minimumBid, _ := etherUtils.StrToEther("4.7")
wallet, _ := memorykeys.GetAddress("wallet")
bankTx, _ := memorykeys.GetTransactor("banker")
auctionAddress, tx, auctionContract, err := contracts.DeployAuction(bankTx, client, startBids, endBids, startReveal, endReveal, minimumBid, *wallet)
chkerr(err)
fmt.Println(auctionAddress.Hex(), tx.Hash().Hex())
client.Commit()
min, err := auctionContract.MinimumBid(nil)
chkerr(err)
fmt.Println("minumum bid is ", etherUtils.EtherToStr(min))
fmt.Println("time :", currentTime())
isBiddingOpen(auctionContract)
jumpTo(startBids)
fmt.Println("time :", currentTime())
isBiddingOpen(auctionContract)
}