Skip to content

Golang Client For FISCO BCOS 2.0

License

Notifications You must be signed in to change notification settings

mingzhenliu/gobcos

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gobcos

Golang Client For FISCO BCOS 2.0.0

FISCO BCOS Go语言版本的SDK,基于以太坊进行改进,主要的功能有:

  • FISCO BCOS 2.0.0 JSON-RPC的Golang API 服务
  • Solidity合约编译为Go文件
  • 部署、查询、写入智能合约

环境准备

功能使用

RPC API 测试

此部分只对项目代码中的RPC API接口调用进行测试,以确定是否能顺利连接FISCO BCOS 2.0.0节点以获取区块链信息。

首先需要拉取代码:

git clone https://github.com/KasperLiu/gobcos.git

进行代码测试前,请先按照实际部署节点的RPC URL更改client/goclient_test.go中的默认的FISCO BCOS RPC连接以及群组ID:

func GetClient(t *testing.T) (*Client) {
    // RPC API
    c, err := Dial("http://localhost:8545", 1) // change it to your RPC IP & port, groupID that you want to connect
    if err != nil {
        t.Fatalf("can not dial to the RPC API: %v", err)
    }
    return c
}

测试代码默认开启的测试函数为GetClientVersion, GetBlockNumber, GetPBFTView,其余函数需去除注释并更改为实际存在的数据后才能执行。如:

// GetBlockHashByNumber returns the block hash by its block number
func TestBlockHshByNumber(t *testing.T) {
    c := GetClient(t)
    // provide a specific blocknumber
    bnum := "0x1"
    raw, err := c.GetBlockHashByNumber(context.Background(), bnum)
    if err != nil {
        t.Fatalf("block hash not found: %v", err)
    }

    t.Logf("block hash by number:\n%s", raw)
}

执行RPC client的测试代码命令为:

go test -v -count=1 ./client

JSON-RPC API调用

在测试成功后,可以在用户的工程项目中引用gobcos的RPC客户端,以调用RPC方法,所有的方法返回的是[]byte,用户可根据实际需要做进一步的JSON解析:

import "github.com/KasperLiu/gobcos/client"

下面假设有一个block.go文件需要获取FISCO BCOS 区块链的某一个区块的信息,则在引入客户端代码包后首先需要初始化客户端,提供需要连接的FISCO BCOS区块链的RPC URL及群组ID:

package main
import (
    "context"
    "github.com/KasperLiu/gobcos/client"
)

func main() {
    client, err := client.Dial("http://localhost:8545", groupID) # change to your RPC URL and GroupID
    if err != nil {
    	// handle err
    }
}

然后可按照FISCO BCOS的RPC API文档进行区块信息查询,需要注意的是,客服端的方法调用需要更改为大写字母Get

blockHash := "0xc0b21d064b97bafda716e07785fe8bb20cc23506bb980f12c7f7a4f4ef50ce30" # fake hash
includeTx := false # only display the transaction hash
block, err := client.GetBlockByHash(context.BackGround(), blockHash, includeTx) # invoke "getBlockByHash“
if err != nil {
    // handle err
}

若要在代码的后续使用中获取其他群组的区块信息,则可以直接调用客户端的SetGroupID方法进行动态切换,如:

// switch to other group
client.SetGroupID(otherGroupID)
client.GetBlockNumber(context.BackGround()) # get the lastest block number of the otherGroupID

Solidity合约编译为Go文件

在利用SDK进行项目开发时,对智能合约进行操作时需要将Solidity智能合约利用gobcos的abigen工具转换为Go文件代码。整体上主要包含了五个流程:

  • 准备需要编译的智能合约
  • 配置好相应版本的solc编译器
  • 构建gobcos的合约编译工具abigen
  • 编译生成go文件
  • 使用生成的go文件进行合约调用

下面的内容作为一个示例进行使用介绍。

1.提供一份简单的样例智能合约Store.sol如下:

pragma solidity ^0.4.25;

contract Store {
  event ItemSet(bytes32 key, bytes32 value);

  string public version;
  mapping (bytes32 => bytes32) public items;

  constructor(string _version) public {
    version = _version;
  }

  function setItem(bytes32 key, bytes32 value) external {
    items[key] = value;
    emit ItemSet(key, value);
  }
}

2.安装对应版本的solc编译器,目前FISCO BCOS默认的solc编译器版本为0.4.25

solc --version
# solc, the solidity compiler commandline interface
# Version: 0.4.25+commit.59dbf8f1.Linux.g++

3.构建gobcos的代码生成工具abigen

git clone https://github.com/KasperLiu/gobcos.git # 下载gobcos代码,如已下载请跳过
cd gobcos # 进入代码目录
go build ./cmd/abigen # 编译生成abigen工具

执行命令后,检查根目录下是否存在abigen,并将生成的abigen以及所准备的智能合约Store.sol放置在一个新的目录下:

mkdir ../newdir && cp ./abigen ../newdir && cd ../newdir
# cp your/Store.sol path/to/newdir

4.编译生成go文件,先利用solc将合约文件生成abibin文件,以前面所提供的Store.sol为例:

solc --bin -o ./ Store.sol && solc --abi -o ./ Store.sol

Store.sol目录下会生成Store.binStore.abi。此时利用abigen工具将Store.binStore.abi转换成Store.go

./abigen --bin=Store.bin --abi=Store.abi --pkg=store --out=Store.go

最后目录下面存在以下文件:

>>ls
abigen  Store.abi  Store.bin  Store.go  Store.sol

5.调用生成的Store.go文件进行合约调用

至此,合约已成功转换为go文件,用户可根据此文件在项目中利用SDK进行合约操作。具体的使用可参阅下一节。

部署、查询、写入智能合约

此部分承接上一节的内容,同时也简单涵盖了SDK的合约使用部分以及账户私钥的加载。

创建外部账户

SDK发送交易需要一个外部账户,导入gobcos的crypto包,该包提供用于生成随机私钥的GenerateKey方法:

privateKey, err := crypto.GenerateKey()
if err != nil {
    log.Fatal(err)
}

然后我们可以通过导入golangcrypto/ecdsa包并使用FromECDSA方法将其转换为字节:

privateKeyBytes := crypto.FromECDSA(privateKey)

我们现在可以使用gobcos的common/hexutil包将它转换为十六进制字符串,该包提供了一个带有字节切片的Encode方法。 然后我们在十六进制编码之后删除“0x”。

fmt.Println(hexutil.Encode(privateKeyBytes)[2:])

这就是用于签署交易的私钥,将被视为密码,永远不应该被共享给别人

由于公钥是从私钥派生的,加密私钥具有一个返回公钥的Public方法:

publicKey := privateKey.Public()

将其转换为十六进制的过程与我们使用转化私钥的过程类似。 我们剥离了0x和前2个字符04,它始终是EC前缀,不是必需的。

publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
    log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}

publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
fmt.Println(hexutil.Encode(publicKeyBytes)[4:])

现在我们拥有公钥,就可以轻松生成你经常看到的公共地址。 加密包里有一个PubkeyToAddress方法,它接受一个ECDSA公钥,并返回公共地址。

address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
fmt.Println(address) // 0x96216849c49358B10257cb55b28eA603c874b05E

公共地址可以查询合约信息。

整体的代码示例为:

import (
    "crypto/ecdsa"
    "fmt"
    "log"
    "github.com/KasperLiu/gobcos/crypto"
    "github.com/KasperLiu/gobcos/common/hexutil"
)

func main() {
    privateKey, err := crypto.GenerateKey()
    if err != nil {
        log.Fatal(err)
    }
    privateKeyBytes := crypto.FromECDSA(privateKey)
    fmt.Println(hexutil.Encode(privateKeyBytes)[2:]) // privateKey in hex
    publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
    if !ok {
        log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
    }

    publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
    fmt.Println(hexutil.Encode(publicKeyBytes)[4:])  // publicKey in hex without "0x"
    address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
    fmt.Println(address)  // account address
}

部署智能合约

首先在利用abigen生成的Store.go文件下,创建一个新的contract_load.go文件用来调用Store.go文件,并创建一个新的文件夹来放置Store.go以方便调用,同时利用go mod进行包管理,初始化为一个contract包:

mdkir testfile
mv ./Store.go testfile
touch contract.go
go mod init contract

此时目录下会生成go.mod包管理文件。而在contract.go部署合约之前,需要先从gobcos中导入accounts/abi/bind包,然后调用传入私钥的NewKeyedTransactor

package main

import (
    "fmt"
    "github.com/KasperLiu/gobcos/client"
    "github.com/KasperLiu/gobcos/accounts/abi/bin"
    store "contranct/testfile" // import Store.go
)

func main(){
    client, err := client.Dial("http://localhost:8545", 1)
    if err != nil {
        log.Fatal(err)
    }
    privateKey, err := crypto.HexToECDSA("input your privateKey in hex without \"0x\"")
    if err != nil {
        log.Fatal(err)
    }
    auth := bind.NewKeyedTransactor(privateKey) // input your privateKey
    input := "Store deployment 1.0"
    address, tx, instance, err := store.DeployHelloworldfi(auth, client, input)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("contract address: ", address.Hex())  // the address should be saved
    fmt.Println("transaction hash: ", tx.Hash().Hex())
}

加载智能合约

在部署完智能合约后,获取到合约的地址,在进行合约查询以及写入时,需要先加载智能合约:

address := common.HexToAddress("contract addree in hex") // 0x147B8eb97fD247D06C4006D269c90C1908Fb5D54
instance, err := store.NewStore(address, client)
if err != nil {
    log.Fatal(err)
}
_ = instance

查询智能合约

在部署过程中设置的Store.sol合约中有一个名为version的全局变量。 因为它是公开的,这意味着它们将成为我们自动创建的getter函数。 常量和view函数也接受bind.CallOpts作为第一个参数:

client, err := client.Dial("http://localhost:8545", 1)
if err != nil {
    log.Fatal(err)
}

// load the contract
address := common.HexToAddress("contract addree in hex") // 0x147B8eb97fD247D06C4006D269c90C1908Fb5D54
instance, err := store.NewStore(address, client)
if err != nil {
    log.Fatal(err)
}

opts := &bind.CallOpts{From: common.HexToAddress("account address")} //0xdcf15c4239b6a29ee6b27cb848e0f6936d6ccfb4
version, err := instance.Version(opts)
if err != nil {
    log.Fatal(err)
}

fmt.Println("version :", version) // "Store deployment 1.0"

写入智能合约

写入智能合约需要我们用私钥来对交易事务进行签名:

client, err := client.Dial("http://localhost:8545", 1)
if err != nil {
    log.Fatal(err)
}

// load the contract
address := common.HexToAddress("contract addree in hex") // 0x147B8eb97fD247D06C4006D269c90C1908Fb5D54
instance, err := store.NewStore(address, client)
if err != nil {
    log.Fatal(err)
}

key := [32]byte{}
value := [32]byte{}
copy(key[:], []byte("foo"))
copy(value[:], []byte("bar"))

privateKey, err := crypto.HexToECDSA("input your privateKey in hex")
if err != nil {
    log.Fatal(err)
}

auth := bind.NewKeyedTransactor(privateKey)
tx, err := instance.SetItem(auth, key, value)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("tx sent: %s", tx.Hash().Hex()) // 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870
opts := &bind.CallOpts{From: common.HexToAddress("account address")}
result, err := instance.Items(opts, key)
if err != nil {
    log.Fatal(err)
}

fmt.Println(string(result[:])) // "bar"

样例代码

// TODO

About

Golang Client For FISCO BCOS 2.0

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Go 50.2%
  • C 36.2%
  • C++ 5.9%
  • Assembly 2.0%
  • Java 1.9%
  • Python 1.9%
  • Other 1.9%