Go语言与智能合约的交互
实现步骤
- 首先要编写一份智能合约
- 将智能合约通过工具转化为go文件
- 自己编写go文件调用智能合约转化成的go文件提供的接口
具体实现
- 编写一份智能合约
智能合约使用solidity语言写的,可以用在线的remix编辑器进行编辑。
pragma solidity >=0.4.22 <0.6.0;
contract DataStorage {
bytes32 public fheAddressHash ;
bytes32 public rsaPublicKeyHash;
bytes32 public dataHash;
bytes32 public modelHash;
bytes32 public encryptedAesKeyHash;
bytes32 public trainedResultHash;
bytes32 public finalDistance;
bytes32 public modelParamsHash;
constructor() public{
}
function setFheAddressHash(bytes32 _fheAddressHash) payable public {
fheAddressHash=_fheAddressHash;
emit LogSetFheAddressHash(fheAddressHash);
}
function setModelHash(bytes32 _modelHash) payable public {
modelHash=_modelHash;
emit LogSetModelHash(modelHash);
}
function setRsaPublicKeyHash(bytes32 _rsaPublicKeyHash) payable public{
rsaPublicKeyHash=_rsaPublicKeyHash;
emit LogSetRsaPublicKeyHash(rsaPublicKeyHash);
}
function setDataHash(bytes32 _dataHash) payable public{
dataHash=_dataHash;
emit LogSetDataHash(dataHash);
}
function setEncryptedAesKeyHash(bytes32 _encryptedAesKeyHash) payable public{
encryptedAesKeyHash=_encryptedAesKeyHash;
emit LogEncryptedAesKeyHash(encryptedAesKeyHash);
}
function setTrainedResultHash(bytes32 _trainedResultHash) payable public{
trainedResultHash=_trainedResultHash;
emit LogTrainedResultHash(trainedResultHash);
}
function setFinalDistance(bytes32 _finalDistance) payable public{
finalDistance=_finalDistance;
emit LogSetFinalDistance(finalDistance);
}
function setModelParamsHash(bytes32 _modelParamsHash) payable public{
modelParamsHash=_modelParamsHash;
emit LogSetModelParamsHash(modelParamsHash);
}
event LogSetFheAddressHash(bytes32 _fheAddressHash);
event LogSetModelHash(bytes32 _modelHash);
event LogSetRsaPublicKeyHash(bytes32 _rsaPublicKeyHash);
event LogSetDataHash(bytes32 _dataHash);
event LogEncryptedAesKeyHash(bytes32 _encryptedAesKeyHash);
event LogTrainedResultHash(bytes32 _trainedResultHash);
event LogSetFinalDistance(bytes32 _finalDistance);
event LogSetModelParamsHash(bytes32 _modelParamsHash);
}
- 将智能合约用工具转化为go语言:
我们用abigen
将智能合约转化为go文件
abigen安装的方式如下:
go get github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum/
make
make devtools
接着我们用如下命令将智能合约转化为go语言文件:
DataStorage.sol为智能合约文件,filedir为生成的文件的保存目录。
#第一行命令会生成.abi文件在filedir目录中
solcjs DataStorage.sol -o filedir --abi
#第二行命令会生成.bin文件在filedir目录中
solcjs DataStorage.sol -o filedir --bin
#第三行命令用filedir中的.abi文件和.bin文件生成一个package 为main的名为DataStorage.go的go语言文件.该文件保存在/Users/huyifan/DAI中.
abigen --abi DataStorage_sol_ DataStorage.abi --bin DataStorage_sol_ DataStorage.bin --pkg main --out /Users/huyifan/DAI/ DataStorage.go
- 自己编写go文件调用智能合约转化成的go文件提供的接口
我这里只调用了setDataHash这个智能合约函数,将字符串存进区块链智能合约中,然后再从区块链中获取我们存进去的字符串。其他的函数以此类推。
package main
import (
"fmt"
"log"
"math/big"
"strings"
//"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/ethclient"
"github.com/ethereum/go-ethereum/common"
//"github.com/oraclize/ethereum-api/oraclizeAPI.sol";
//"github.com/ethereum/go-ethereum/crypto"
)
//本地以太坊私链的某一个账户,这里我用了coinbase对应的账户,这个账户信息在私链数据目录下的keystore目录里面
const key=`{"address":"12769c3419a7f491cf4e576e2e983e009d579076","crypto":{"cipher":"aes-128-ctr","ciphertext":"215430a18ab1132c6eaecdf966bc0d878a3be06cff5dce173d801afec5002db5","cipherparams":{"iv":"d41d87954da3dfca1f38e14111169fb8"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"d5268e70fbf8666435bf82ee53850f14486810f1944110de2aede933ae97fff1"},"mac":"a80e95fc657473f543bb989da9e8e2cde73e2b8bde52e6b05355b44a40c165ec"},"id":"5e034459-6e81-4208-9e26-c91641d20f5d","version":3}`
func main() {
//我们要存进区块链智能合约的字符串
dataHash := "testDataHash"
//智能合约的在区块链上的地址
contractAddress := "0x47d73709bf274160118f8f175695ac3616b7f08f"
//连接本地的以太坊私链(一定要保证本地以太坊私链已经启动)
conn, err := ethclient.Dial("http://127.0.0.1:8545")
fmt.Println("connect to local geth node...", conn)
if err != nil {
log.Fatalf("could not connect to local node: %v", err)
}
//fmt.Println("get the contract object...")
token, err := NewMain(common.HexToAddress(contractAddress), conn)
if err != nil {
log.Fatalf("Failed to instantiate a Token contract: %v", err)
}
fmt.Println("contract token======>:", token)
//解锁对应账户
auth, err := bind.NewTransactor(strings.NewReader(key), "abc")
if err != nil {
log.Fatalf("could not create auth: %v", err)
}
alloc := make(core.GenesisAlloc)
alloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(1337000000000)}
sim := backends.NewSimulatedBackend(alloc, 100000000)
// fmt.Println("token:=====>",token)
var arr [32]byte
for k, v := range []byte(dataHash) {
arr[k] = byte(v)
}
fmt.Println(string(arr[:len(arr)]))
//调用对应的go语言函数与以太坊私链进行交互
fmt.Println("=========start put data=========")
_, err = token.SetDataHash(&bind.TransactOpts{
From: auth.From,
Signer: auth.Signer,
GasLimit: 288162,
Value: big.NewInt(30),
}, arr)
if err!=nil {
log.Fatalf("transaction produce fail: %v", err)
}
//提交交易
sim.Commit()
if err != nil {
log.Fatalf("put data to data pool err:%v", err)
}
fmt.Println("put data to smart contract success!")
fmt.Println("==================================")
fmt.Println("get data from smart contract")
//从以太坊上获得之前存进去的值
info, _ := token.DataHash(&bind.CallOpts{Pending: true})
//fmt.Printf("the total data prices and desciption are: %s\n", info);
fmt.Printf(string(info[:len(info)]))
}
结果如下所示:
以太坊产生的信息如下图所示:
注意点:
- 只要将想要从以太坊获得值的那个变量在智能合约中设置成
public
,智能合约转为go语言文件后会自动的生成相应的函数,调用那个函数就可以从以太坊中获得变量值。 - 如果智能合约的某个函数会改变以太坊的状态(例如设置某个变量的值,更新了某个变量的值等等),那么在智能合约中这个函数就要用
payable
修饰,因为调用这个函数的时候必须要发起交易,待这个交易被确认后,调用的这个函数的产生的结果才生效。我们也可以从设置值的函数和获取值的函数的参数看出端倪:
- 也可以事先不部署智能合约。这样的话,就需要调用DataStorage.go文件中的DeployMain函数自己手动的部署智能合约,代码修改如下:
以太坊的状态如下图所示:
这里再次提醒只要对区块链状态发生了改变的话,必须提交交易,并被验证通过才能算真正的改变了区块链.