Merge pull request #152 from meshplus/docs/make-documents-better

docs(docs): move documents in wiki to the document station
This commit is contained in:
Aiden X 2020-09-01 17:01:56 +08:00 committed by GitHub
commit 18263042a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1068 additions and 25 deletions

View File

@ -3,38 +3,44 @@ module.exports = {
description: 'BitXHub部署文档、使用文档、设计文档站',
themeConfig: {
sidebar: [
// {
// title: '版本信息',
// path: '/version/'
// },
{
title: '快速开始',
path: '/quick/'
path: '/quick/',
},
{
title: '部署文档',
path: '/deploy/',
},
{
title: '使用文档',
path: '/usage/',
},
{
title: '开发文档',
path: '/develop/',
children: [
{
title: '共识算法插件化使用文档',
path: '/develop/consensus_usage'
},
{
title: '共识算法插件化设计文档',
path: '/develop/consensus_design'
},
{
title: '跨链合约编写规范',
path: '/develop/interchain_contract'
},
{
title: '验证引擎规则编写规范',
path: '/develop/rule'
}
],
},
// {
// title: '使用文档',
// path: '/usage/',
// children: [
// {
// title: '部署文档',
// path: '/usage/deploy',
// }
// ],
// },
{
title: 'FAQ',
path: '/faq/',
},
// {
// title: '开发文档',
// path: '/develop/',
// children: [
// {
// title: '共识模块开发',
// path: '/develop/order'
// }
// ]
// }
]
}

View File

@ -0,0 +1,78 @@
# 共识算法插件化设计文档
## 1. 项目结构
该项目为BitXHub提供共识算法的插件化具体项目结构如下
```none
./
├── Makefile //编译文件
├── README.md
├── build
│   └── consensus.so //编译后的共识算法二进制插件
├── go.mod
├── go.sum
├── order.toml //共识配置文件
└── consensus //共识算法项目代码
├── config.go
├── node.go
└── stack.go
```
其中注意在`go.mod`中需要引用BitXHub项目源码需要让该插件项目与BitXHub在同一目录下建议在$GOPATH路径下
```none
replace github.com/meshplus/bitxhub => ../bitxhub/
```
## 2. 编译Plugin
我们采用GO语言提供的插件模式实现`BitXHub`对于Plugin的动态加载。
编写`Makefile`编译文件:
```shell
SHELL := /bin/bash
CURRENT_PATH = $(shell pwd)
GO = GO111MODULE=on go
plugin:
@mkdir -p build
$(GO) build --buildmode=plugin -o build/consensus.so consensus/*.go
```
运行下面的命令,能够得到 `consensus.so`文件。
```shell
$ make plugin
```
修改节点的`bitxhub.toml`
```none
[order]
plugin = "plugins/consensus.so"
```
将你编写的动态链接文件和`order.toml`文件分别放到节点的plugins文件夹和配置文件下。
```none
./
├── api
├── bitxhub.toml
├── certs
│   ├── agency.cert
│   ├── ca.cert
│   ├── node.cert
│   └── node.priv
├── key.json
├── logs
├── network.toml
├── order.toml //共识算法配置文件
├── plugins
│   ├── consensus.so //共识算法插件
├── start.sh
└── storage
```
结合我们提供的`BitXHub`中继链,就能接入到跨链平台来。

View File

@ -0,0 +1,346 @@
# 共识算法插件化使用文档
在本教程中,你将构建一个完整功能的共识服务。过程中能学习基本的概念和具体使用细节。该示例将展示如何快速、轻松地**接入自己的共识算法到BitXHub中来**。
## 1. 开发要求
- 安装 [__go1.13+__](https://golang.org/doc/install)
- 设置好$GOPATH等环境
## 2. 准备
为了更加便捷的开发共识服务接入到`BitXHub`中来,我们提供了一些接口和参数。
### 2.1 Order接口
我们规定了下面一些必要的接口,这些接口是`BitXHub`与共识服务的交互接口。
```go
type Order interface {
//开启共识服务
Start() error
//停止共识服务,关闭共识服务资源
Stop()
//交易发送到共识服务,共识服务将处理并打包该交易
Prepare(tx *pb.Transaction) error
//返回打包的区块
Commit() chan *pb.Block
//从网络接收到的共识消息
Step(ctx context.Context, msg []byte) error
//集群中产生了新Leader系统通过该接口判断共识服务是否正常
Ready() bool
//系统会通知该接口已经持久化的区块,
ReportState(height uint64, hash types.Hash)
//集群中可以正常工作的最少节点数量如在raft中要求正常节点数是N/2+1
Quorum() uint64
}
```
### 2.2 Config参数
我们规定了下面一些参数可以从BitXHub传递给共识服务。
```go
type Config struct {
Id uint64 //节点ID
RepoRoot string //根路径
StoragePath string //存储文件路径
PluginPath string //插件文件路径
PeerMgr peermgr.PeerManager //网络模块组件
PrivKey crypto.PrivateKey //节点私钥
Logger logrus.FieldLogger //日志组件
Nodes map[uint64]types.Address //集群节点网络地址
Applied uint64 //当前区块高度
Digest string //当前区块哈希
GetTransactionFunc func(hash types.Hash) (*pb.Transaction, error) //获取已经持久化的交易函数
GetChainMetaFunc func() *pb.ChainMeta //获取链的元信息函数,其中包括当前区块高度和区块哈希
}
```
### 2.3 Filter过滤器
为了更加方便过滤重复交易,我们提供了布隆过滤器的组件。
```go
const (
filterDbKey = "bloom_filter"
m = 10000000 //filter的字节位数
k = 4 //计算hash的次数
)
type ReqLookUp struct {
filter *bloom.BloomFilter //bloom过滤器
storage storage.Storage //leveldb存储
b bytes.Buffer //filter缓存
}
```
## 3. 程序目的
本教程以开发一个简单Solo版本的共识算法为例。
### 3.1 开始编写你的程序
首先选择你的工程目录按照正常的GO程序的流程建立项目
```shell
$ go version // 确认你安装的GO版本
$ mkdir ${YOUR_PROJECT}
$ cd ${YOUR_PROJECT}
$ go mod init
```
### 3.2 Node对象
首先创建一个`node.go`文件这个文件是共识Plugin的核心和入口来看看`Node`具体结构
```go
type Node struct {
sync.RWMutex
height uint64 // 当前区块高度
pendingTxs *list.List //交易池
commitC chan *pb.Block //区块channel
logger logrus.FieldLogger //日志
reqLookUp *order.ReqLookUp //bloom过滤器
getTransactionFunc func(hash types.Hash) (*pb.Transaction, error) //获取持久化的交易函数
packSize int //出块的最大交易数量
blockTick time.Duration //出块间隔
ctx context.Context
cancel context.CancelFunc
}
```
然后应该提供一个`Order`的实例化的接口(类似于构造函数),具体代码如下:
```go
func NewNode(opts ...order.Option) (order.Order, error) {
//处理Order参数
config, err := order.GenerateConfig(opts...)
if err != nil {
return nil, fmt.Errorf("generate config: %w", err)
}
//创建leveldb用于存储bloom filter
storage, err := leveldb.New(config.StoragePath)
if err != nil {
return nil, fmt.Errorf("new leveldb: %w", err)
}
//创建bloom filter用于过滤重复交易
reqLookUp, err := order.NewReqLookUp(storage)
if err != nil {
return nil, fmt.Errorf("new bloom filter: %w", err)
}
//创建node的上下文
ctx, cancel := context.WithCancel(context.Background())
return &Node{
height: config.Applied,//区块的当前高度
pendingTxs: list.New(),
commitC: make(chan *pb.Block, 1024),
packSize: 500, //出块的最大交易数量可配置在order.toml中
blockTick: 500 * time.Millisecond, //出块时间间隔可配置在order.toml中
reqLookUp: reqLookUp,
getTransactionFunc: config.GetTransactionFunc,
logger: config.Logger,
ctx: ctx,
cancel: cancel,
}, nil
}
```
### 3.3 Node主要方法
通过描述Node的主要方法介绍pending的交易是如何被打包到区块中以及如何与`BitXHub`系统进行交互。
#### 3.3.1 Start方法
功能:定时在交易池中扫描交易并出块。
```go
func (n *Node) Start() error {
go n.execute()
return nil
}
func (n *Node) execute(){
//开启定时器定时扫描交易池中是否存在pending的交易
ticker := time.NewTicker(n.blockTick)
defer ticker.Stop()
for {
select {
case <-ticker.C:
n.Lock()
l := n.pendingTxs.Len()
if l == 0 {
n.Unlock()
continue
}
var size int
if l > n.packSize {
size = n.packSize
} else {
size = l
}
//打包交易
txs := make([]*pb.Transaction, 0, size)
for i := 0; i < size; i++ {
front := n.pendingTxs.Front()
tx := front.Value.(*pb.Transaction)
txs = append(txs, tx)
n.pendingTxs.Remove(front)
}
//区块高度+1
n.height++
n.Unlock()
//区块出块
block := &pb.Block{
BlockHeader: &pb.BlockHeader{
Version: []byte("1.0.0"),
Number: n.height,
Timestamp: time.Now().UnixNano(),
},
Transactions: txs,
}
n.commitC <- block
case <-n.ctx.Done():
ticker.Stop()
}
}
return nil
}
```
#### 3.3.2 Stop方法
功能:停止共识,释放共识相关资源。
```go
func (n *Node) Stop() {
n.cancel()
}
```
#### 3.3.3 Prepare方法
功能:从`BitXHub`系统中传入交易,收集进交易池。
```go
func (n *Node) Prepare(tx *pb.Transaction) error {
hash := tx.TransactionHash
//检查交易是否存在,防止交易二次打包
if ok := n.reqLookUp.LookUp(hash.Bytes()); ok {
if tx, _ := n.getTransactionFunc(hash); tx != nil {
return nil
}
}
//交易进入交易池
n.pushBack(tx)
return nil
}
```
#### 3.3.4 Commit方法
功能:返回新区块的`channel`。
```go
func (n *Node) Commit() chan *pb.Block {
return n.commitC
}
```
#### 3.3.5 Step方法
功能:通过该接口接收共识的网络消息。
```go
//由于示例是Solo的版本故具体不实现该方法
func (n *Node) Step(ctx context.Context, msg []byte) error {
return nil
}
```
#### 3.3.6 Ready方法
功能判断共识是否完成Leader是否完成选举。
```go
//由于示例是Solo的版本单节点直接返回True
func (n *Node) Ready() bool {
return true
}
```
#### 3.3.7 ReportState方法
功能:新区块被持久化后,`BitXHub`会调用该接口通知共识服务
```go
func (n *Node) ReportState(height uint64, hash types.Hash) {
//每一个新区块被存储后持久化bloom filter
if err := n.reqLookUp.Build(); err != nil {
n.logger.Errorf("bloom filter persistence error", err)
}
//每十个区块做一个Check Point
if height%10 == 0 {
n.logger.WithFields(logrus.Fields{
"height": height,
"hash": hash.ShortString(),
}).Info("Report checkpoint")
}
}
```
#### 3.3.8 Quorum方法
功能集群中可以正常工作的最少节点数量比如在raft中要求正常节点数是N/2+1
```go
//由于示例是Solo的版本直接返回1
func (n *Node) Quorum() uint64 {
return 1
}
```
## 4. 接入共识算法
### 4.1 配置文件
可以通过配置`order.toml`文件,自定义你的共识算法。
```none
[solo]
[solo.tx_pool]
pack_size = 500 # 出块最大交易数
block_tick = "500ms" # 出块间隔
```
### 4.2 插件化
[共识算法插件化设计文档](https://github.com/meshplus/bitxhub/wiki/%E5%85%B1%E8%AF%86%E7%AE%97%E6%B3%95%E6%8F%92%E4%BB%B6%E5%8C%96%E8%AE%BE%E8%AE%A1%E6%96%87%E6%A1%A3)

View File

@ -0,0 +1,292 @@
# 跨链合约编写规范
## 跨链合约设计说明
按照跨链合约的设计我们需要在有跨链需求的应用链上部署两种合约。一个合约负责对接跨链网关Pier为跨链管理合约Broker一个合约负责具体的业务场景为业务合约。业务合约需要跨链时要统一将跨链请求提交到Broker合约上Broker统一和Pier进行交互。一个Broker合约可以负责对接多个业务合约。
同时为了简化Broker的编写我们设计了Broker合约和业务合约的相应接口。
### Broker 合约接口
```java
public interface Broker {
// 提供给业务合约注册。注册且审核通过的业务合约才能调用Broker合约的跨链接口
register(string id) Response
// 提供给管理员审核已经注册的业务合约
audit(string id, bool status) Response
// getInnerMeta 是获取跨链请求相关的Meta信息的接口。以Broker所在的区块链为目的链的一系列跨链请求的序号信息。如果Broker在A链则可能有多条链和A进行跨链如B->A:3; C->A:5。返回的map中key值为来源链IDvalue对应该来源链已发送的最新的跨链请求的序号如{B:3, C:5}。
getInnerMeta() Response
// getOuterMeta 是获取跨链请求相关的Meta信息的接口。以Broker所在的区块链为来源链的一系列跨链请求的序号信息。如果以Broker在A链则A可能和多条链进行跨链如A->B:3; A->C:5。返回的map中key值为目的链IDvalue对应已发送到该目的链的最新跨链请求的序号如{B:3, C:5}。
getOuterMeta() Response
// getCallbackMeta 是获取跨链请求相关的Meta信息的接口。以Broker所在的区块链为来源链的一系列跨链请求的序号信息。如果Broker在A链则A可能和多条链进行跨链如A->B:3; A->C:5同时由于跨链请求中支持回调操作即A->B->A为一次完整的跨链操作我们需要记录回调请求的序号信息如A->B->:2; A->C—>A:4。返回的map中key值为目的链IDvalue对应到该目的链最新的带回调跨链请求的序号如{B:2, C:4}。(注意 callbackMeta序号可能和outMeta是不一致的这是由于由A发出的跨链请求部分是没有回调的
getCallbackMeta() Response
// getInMessage 查询历史跨链请求。查询键值中srcChainID指定来源链idx指定序号查询结果为以Broker所在的区块链作为目的链的跨链请求。
getInMessage(string srcChainID, uint64 idx) Response
// getOutMessage 查询历史跨链请求。查询键值中dstChainID指定目的链idx指定序号查询结果为以Broker所在的区块链作为来源链的跨链请求。
getOutMessage(string dstChainID, uint64 idx) Response
// 提供给业务合约发起跨链资产交换的接口
InterchainTransferInvoke(string dstChainID, string destAddr, string args) Response
// 提供给业务合约发起跨链数据交换的接口
InterchainDataSwapInvoke(string dstChainID, string destAddr, string key) Response
// 提供给业务合约发起通用的跨链交易的接口
InterchainInvoke(string dstChainID, string sourceAddr, string destAddr, string func, string args, string callback) Response
// 提供给跨链网关调用的接口,跨链网关收到跨链充值的请求时候调用
interchainCharge(string srcChainID, uint64 index, string destAddr, string sender, string receiver, uint64 amount) Response
// 提供给跨链网关调用的接口,跨链网关收到跨链转账执行结果时调用
interchainConfirm(string srcChainID, uint64 index, string destAddr, bool status, string sender, uint64 amount) Response
// 提供给跨链网关调用的接口,跨链网关收到跨链数据交换请求时调用
interchainGet(string srcChainID, uint64 index, string destAddr, string key) Response
// 提供给跨链网关调用的接口,跨链网关收到跨链数据交换执行结果时调用
interchainSet(string srcChainID, uint64 index, string destAddr, string key, string value) Response
// 提供给合约部署初始化使用
initialize() Response
}
```
#### 重要接口说明
1. `InterchainInvoke` 接口
改接口是实现通用的跨链调用的接口。接受的参数有目的链ID发起跨链交易的业务合约地址或ID目的链业务合约地址或ID跨链调用的函数名该函数的参数回调函数名。
Broker会记录跨链交易相应的元信息并对跨链交易进行编号保证跨链交易有序进行。并且抛出跨链事件以通知跨链网关跨链交易的产生。
2. `InterchainTransferInvoke` 接口
是对`InterchainInvoke` 接口的封装专门用于发起一次跨链转账业务。接受参数有目的链ID目的链业务合约地址或ID发起转账账户名接受转账账户名转账金额。
3. `InterchainDataSwapInvoke` 接口
是对InterchainInvoke 接口的封装专门用于发起一次跨链数据交换业务。接受参数有目的链ID目的链业务合约地址或IDKey值。
4. `interchainCharge` 接口
接受跨链转账的接口。由Broker合约根据业务合约地址或ID对业务合约进行充值操作并记录相应的元信息。接受参数有来源链ID交易序号目的业务合约ID发起账户名接收账户名转账金额。
5. `interchainConfirm` 接口
接受跨链转账的接口。由Broker合约根据业务合约地址或ID对业务合约进行充值操作并记录相应的元信息。接受参数有来源链ID交易序号目的业务合约ID跨链转账交易状态接收账户名转账金额。
Broker需要根据跨链交易的状态决定是否进行回滚操作并记录相应元信息。
6. `interchainGet` 接口
接受跨链数据获取的接口。接受参数有来源链ID交易序号目的业务合约IDKey值。
7. `interchainSet` 接口
接受跨链数据回写的接口。接受参数有来源链ID交易序号目的业务合约IDKey值value值。
### 业务合约接口
业务合约现阶段分为资产类和数据交换类的业务合约,由于资产类的有操作原子性和安全性的考虑,需要的接口实现上比数据交换类的业务合约更复杂。
#### Transfer 合约
```java
public interface Transfer {
// 发起一笔跨链交易的接口
transfer(string dstChainID, string destAddr, string sender, string receiver, string amount) Response
// 提供给Broker合约收到跨链充值所调用的接口
interchainCharge(string sender, string receiver, uint64 val) Response
// 跨链交易失败之后提供给Broker合约进行回滚的接口
interchainRollback(string sender, uint64 val) Response
}
```
#### DataSwapper合约
```java
public interface DataSwapper {
// 发起一个跨链获取数据交易的接口
get(string dstChainID, string dstAddr, string key) Response
// 提供给Broker合约调用当Broker收到跨链获取数据的请求时取数据的接口
interchainGet(string key) Response
// 跨链获取到的数据回写的接口
interchainSet(string key, string value) Response
}
```
## 具体实现
对于想要接入到我们的跨链平台中的Fabric区块链我们已经有提供跨链管理合约Broker和相应的Plugin你只需要对你的业务合约进行一定的改造便可拥有跨链功能。
**如果是其他应用链你可以根据我们的设计思路自行开发跨链管理合约以及相应的Plugin。**
现在我们已经有Solidity版本和chaincode版本编写的跨链合约样例实现具体说明如下
* [Solidity 跨链合约实现]()
* [chaincode 跨链合约实现]()
如果你需要新的语言编写合约,你可以按照我们的设计思路和参考实现进行进一步的开发。
### 改造业务合约
本章主要说明在Fabric应用链上如何使用我们提供的跨链管理合约Broker在你已有业务合约的基础上添加接口以或得跨链能力。
#### 业务合约Demo
假设你已经有了一个简单的KV存储的业务合约代码如下
```go
type KVStore struct{}
func (s *KVStore) Init(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nil)
}
func (s *KVStore) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
function, args := stub.GetFunctionAndParameters()
fmt.Printf("invoke: %s\n", function)
switch function {
case "get":
return s.get(stub, args)
case "set":
return s.set(stub, args)
default:
return shim.Error("invalid function: " + function + ", args: " + strings.Join(args, ","))
}
}
func (s *KVStore) get(stub shim.ChaincodeStubInterface, args []string) peer.Response {
// args[0]: key
value, err := stub.GetState(args[0])
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(value)
}
// get is business function which will invoke the to,tid,id
func (s *KVStore) set(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("incorrect number of arguments")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
func main() {
err := shim.Start(new(KVStore))
if err != nil {
fmt.Printf("Error starting chaincode: %s", err)
}
}
```
现在你想在这个合约的基础上增加一个跨链获取数据的功能,如果使用我们的跨链管理合约提供的接口,很简单的增加几个接口就可以了。
##### 发起跨链数据交换的接口
为了方便用户使用,我们在原来获取数据的接口基础增加这个功能:
```go
const (
channelID = "mychannel"
brokerContractName = "broker"
interchainInvokeFunc = "InterchainDataSwapInvoke"
)
func (s *KVStore) get(stub shim.ChaincodeStubInterface, args []string) peer.Response {
switch len(args) {
case 1:
// args[0]: key
value, err := stub.GetState(args[0])
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(value)
case 3:
// args[0]: destination appchain id
// args[1]: destination contract address
// args[2]: key
b := util.ToChaincodeArgs(interchainInvokeFunc, args[0], args[1], args[2])
response := stub.InvokeChaincode(brokerContractName, b, channelID)
if response.Status != shim.OK {
return shim.Error(fmt.Errorf("invoke broker chaincode %s error: %s", brokerContractName, response.Message).Error())
}
return shim.Success(nil)
default:
return shim.Error("incorrect number of arguments")
}
}
```
由于我们的跨链管理合约一旦部署之后chaincode name和所在的channel和跨链接口都是不变的所以在业务变量中直接使用常量指定Broker合约的相关信息。
```go
b := util.ToChaincodeArgs(interchainInvokeFunc, args[0], args[1], args[2])
response := stub.InvokeChaincode(brokerContractName, b, channelID)
```
这两行代码调用了我们的跨链管理合约只需要提供参数目的链ID目的链上业务合约的地址想要获取数据的`Key`值。
##### 跨链获取的接口
```go
func (s *KVStore) interchainGet(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("incorrect number of arguments")
}
value, err := stub.GetState(args[0])
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(value)
}
```
`interchainGet` 接受参数 `key`,在本合约中查询该`Key`值对应的`value`,并返回。该接口提供给`Broker`合约进行跨链获取数据的调用。
##### 跨链回写的接口
```go
func (s *KVStore) interchainSet(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("incorrect number of arguments")
}
err := stub.PutState(args[0], []byte(args[1]))
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
```
`interchainSet` 接受参数 `key`,在本合约中设置`Key`值对应的`value`。该接口提供给`Broker`合约回写跨链获取数据的时候进行调用。
### 总结
经过上面的改造,你的业务合约已经具备跨链获取数据的功能了,完整的代码可以参数[这里](https://github.com/meshplus/pier-client-fabric/tree/master/example)

View File

166
docs/develop/rule.md Normal file
View File

@ -0,0 +1,166 @@
# 验证引擎规则编写规范
## 智能合约
BitXHub提供wasm虚拟机来运行验证规则的智能合约应用链方可以灵活自主实时的部署和更新验证规则的智能合约。
验证规则的智能合约的主要目的是:
1. 解析IBTP的Proof字段不同的应用链在Pier发送IBTP时会对自身的验证信息做不同形式的封装智能合约需要对封装的Proof进行解析得到需要验签的参数
1. 对得到的验签的参数采用对应的签名算法进行验签。
BitXHub采用wasmer作为wasm的虚拟机因此验证规则智能合约需要遵循wasi的规范验证规则合约需要使用指定的方法名。规则合约的编写者可以通过以下两种方式来进行合约的编写入口函数为verify
3. 编写智能合约时可以引入BitXHub提供给wasm的签名函数。
```rust
// BitXHub provides ecdsa signature verify method
// Take this as an example
extern {
fn ecdsa_verify(...) -> ... ;
}
#[no_mangle]
fn verify(...) -> ... {
...
}
```
4. 应用链方在合约中自己编写验签方法并编译成wasm智能合约。
## 规则部署
应用链在注册到BitXHub以后需要将自身对跨链交易的背书验证规则注册到BitXHub上不然跨链交易的请求是无法通过的。所以应用链在注册到BitXHub后想要进行跨链请求就必须将自身的验证规则合约部署到BitXHub上。
首先应用链需要通过部署合约的方式将验证规则的合约部署到BitXHub上部署完后WASM虚拟机会返回一个合约的地址这个地址就是验证引擎需要调用的地址。客户端在拿到部署合约的结果验证规则的地址后。需要调用BitXHub的内置合约把验证规则的地址和自己应用链的ID关联起来。
之后每一次跨链交易产生,验证引擎就会通过这个合约地址拿到验证规则合约,将合约载入到虚拟机中对验证信息进行校验。
## 规则调用
当BitXHub处理一条跨链交易的时候需要通过验证引擎验证交易的有效性验证引擎会根据来源链的ID找到对应的验证规则加载到WASM虚拟机中WASM虚拟机会通过验证的入口函数Verify调用验证规则的智能合约。运行验证规则返回验证结果。
## 证书签名
一般来说验证签名需要证书或者公钥验证引擎的背书公钥和证书是在应用链注册到BitXHub中的时候上传到BitXHub中的。
## 合约编写
### 参数传递
由于wasm虚拟机对传入传出的数据类型有限制现行支持的类型不足以支持将验证信息直接传入到wasm虚拟机中为了使wasm合约编写者能对验证信息直接读写BitXHub提供了一套验证引擎合约的模板模板对各个入口进行封装用户只需要在入口函数中添加自己的验证逻辑即可。
wasm现阶段不支持对字符串或者是byte数组的输入输出所以如何将证明信息传入到wasm虚拟机需要使用特定的方法。BitXHub的模板提供了对虚拟机内存访问的接口通过该接口可以在虚拟机外部直接读写虚拟机的内存从而变相的达到传入传出字符串的目的。
```rust
#[no_mangle]
pub extern fn allocate(size: usize) -> *mut c_void {
let mut buffer = Vec::with_capacity(size);
let pointer = buffer.as_mut_ptr();
mem::forget(buffer);
pointer as *mut c_void
}
#[no_mangle]
pub extern fn deallocate(pointer: *mut c_void, capacity: usize) {
unsafe {
let _ = Vec::from_raw_parts(pointer, 0, capacity);
}
}
```
### 引用BitXHub提供的方法
由于wasm交叉编译对一些动态链接库的支持性低许多合约编写者可能无法直接调用高级语言的算法库进行合约编写。BitXHub提供了一些基础的密码学的库方便智能合约的编写。
原理介绍:
wasm虚拟机允许外部import函数让wasm来调用本质上是通过cgo的方式将执行函数的入口指针提供给wasm虚拟机这样wasm就可以调用Go的函数了。
虚拟机的内存是Go程序内存的一部分虚拟机内部无法访问到Go程序的内存所以wasm函数返回的指针都是基于虚拟机内存的偏移量这就造成了Go的函数无法通过wasm函数返回的指针直接访问到指针对应的变量。我们这边的解决方案是将虚拟机内存的起始地址作为所有import函数的上下文这样所有import函数都可以通过这个上下文和wasm函数返回的指针来定位变量的地址了。
```go
data := ctx.Data().(map[int]int)
memory := ctx.Memory()
signature := memory.Data()[sig_ptr : sig_ptr+70]
```
## 验证引擎与WASM虚拟机
### WASM虚拟机实例
当BitXHub的Executor创建时验证引擎的实例也就被创建了。当每一次有跨链交易的请求到达时验证引擎会根据交易来源链的ID拿到存储在链上的验证规则合约然后会实例化一个WASM的虚拟机将该验证规则合约加载到虚拟机中。
由于BitXHub本身提供很多密码学的方法当WASM虚拟机实例化之前需要将这些方法import进来从而可以提供给合约进行调用。
```go
imports, _ := wasm.NewImports().Append("*", *, C.*)
instance, _ := wasm.NewInstanceWithImports(bytes, imports)
```
### 验证引擎传参
验证引擎需要将IBTP解析出的Proof字段传输到WASM中以便合约能够执行由于WASM虚拟机对传入数据的类型有非常严格的限制现在只支持整形和浮点参数的传入所以字符串和byte数组的信息无法直接被合约方法调用。
验证引擎首先需要根据传入参数的大小长度在WASM的虚拟机中划分出一块内存作为输入读取内存并获得输入读取内存的指针并且划分出一块内存作为输出读取内存同样的获得输出内存指针。指针都可以用i32的整型数据变量来表示。
内存的划分和释放方法由BitXHub提供的合约模板提供入口函数为allocate和deallocate。
### 验证引擎验证
根据验证引擎传参章节的描述验证引擎在虚拟机划出输入输出的内存以后就会调用验证规则的入口函数start将输入输出的内存指针作为穿参让入口函数执行。
合约模板已经帮合约编写者处理好了参数在wasm中的输入输出问题。合约编写者可以直接拿到Proof字段和Validator的信息只需要对这些信息进行验证规则的编写即可。
```rust
pub fn verify<'a>(proof: &[u8], validators: &Vec<&'a str>) -> bool {
// Code your rule here with variables: proof and validators
...
}
```

View File

@ -0,0 +1,155 @@
# BitXHub使用文档
## 准备工作
BitXHub使用[**golang**](https://golang.org/doc/install)开发版本要求1.13以上。
如果你想在本地或者服务器上运行BitXHub你需要在机器上安装有以下的依赖
[__packr__](https://github.com/gobuffalo/packr)、[__gomock__](https://gist.github.com/thiagozs/4276432d12c2e5b152ea15b3f8b0012e#installation)、[__mockgen__](https://gist.github.com/thiagozs/4276432d12c2e5b152ea15b3f8b0012e#installation)
可以通过下面的命令,快速安装依赖:
```bash
bash scripts/prepare.sh
```
## 编译
编译bitxhub
```bash
make build
```
编译完后,会在`bin`目录下生成一个`bitxhub`二进制文件。
使用`version`命令进行验证:
```bash
$ ./bin/bitxhub version
BitXHub version: 1.0.0-master-2bb82e8
App build date: 2020-03-31T00:02:19
System version: darwin/amd64
Golang version: go1.13.8
```
如果想直接安装`bitxhub`到可执行环境,可以使用`install`命令:
```bash
make install
```
## 启动solo模式bitxhub
使用下面的命令即可启动一个单节点的bitxhub
```bash
cd scripts
bash solo.sh
```
启动成功会打印出BitXHub的ASCII字体
<p>
<img src="https://user-images.githubusercontent.com/29200902/77936225-30cc6400-72e5-11ea-9499-1ead165c5495.png" width="50%" />
</p>
## 本地快速启动4节点
本地快速启动脚本依赖于[tmux](https://github.com/tmux/tmux/wiki),需要提前进行安装。
使用下面的命令克隆项目:
```shell
git clone git@github.com:meshplus/bitxhub.git
```
BitXHub还依赖于一些小工具使用下面的命令进行安装
```shell
cd bitxhub
bash scripts/prepare.sh
```
最后运行下面的命令即可运行一个四节点的BitXHub中继链
```shell
make cluster
```
启动成功会在四个窗格中分别打印出BitXHub的ASCII字体。
**注意:** `make cluster`启动会使用`tmux`进行分屏,所以在命令执行过程中,最好不要进行终端切换。
## 自定义启动
首先,使用项目提供的配置脚本快速生成配置文件:
```shell
cd bitxhub/scripts
bash config.sh <number> // number是节点数量
```
上面命令以生成4个节点配置作为例子会在当前目录下的build文件夹下生成如下文件
```shell
.
├── addresses
├── agency.cert
├── agency.priv
├── bitxhub
├── ca.cert
├── ca.priv
├── node1
├── node2
├── node3
├── node4
├── pids
└── raft.so
```
node1-node4下的文件信息如下
```shell
.
├── api
├── bitxhub.toml
├── certs
├── network.toml
├── order.toml
├── plugins
└── start.sh
```
### 修改端口信息
端口的配置主要在`bitxhub.toml`文件中。
`port.grpc` 修改节点的grpc端口
`port.gateway` 修改节点的grpc gateway端口
`port.pprof` 修改节点的pprof端口
### 修改初始化账号
`addresses`文件中记录了各节点的地址,将里面的地址填写到`genesis.addresses`中即可。
### 修改网络信息
网络配置修改在`network.toml`中。
`N`字段修改为节点数量默认是4个。
`id`字段代表了节点的顺序id范围在1-N节点间不能重复。
`addr`和`id`分别是各节点的地址和id其中`/ip4/`后填写节点所在服务器的ip地址`/tcp/`后填写节点的libp2p端口端口不重复即可`/p2p/`后填写Libp2p的id具体的值从pids文件中按照顺序获取。
### 启动
启动前需要将build目录下的bitxhub二进制拷贝到node1-node4目录下将build目录下的raft.so插件拷贝到node1-node4下的plugins下。
分别进入到node1-node4目录下执行以下命令即可启动
```shell
bash start.sh
```

View File