XuperUnion文档

简介

XuperUnion是超级链体系下的第一个开源项目,是构建超级联盟网络的底层方案。

其主要特点是高性能,通过原创的XuperModel模型,真正实现了智能合约的并行执行和验证,通过自研的WASM虚拟机,做到了指令集级别的极致优化。

在架构方面,其可插拔、插件化的设计使得用户可以方便选择适合自己业务场景的解决方案,通过独有的XuperBridge技术,可插拔多语言虚拟机,从而支持丰富的合约开发语言。

在网络能力方面,XuperUnion具备全球化部署能力,节点通信基于加密的P2P网络,支持广域网超大规模节点,且底层账本支持分叉管理,自动收敛一致性,TDPOS算法确保了大规模节点下的快速共识。在账户安全方面,XuperUnion内置了多私钥保护的账户体系,支持权重累计、集合运算等灵活的策略。

XuperChain架构

XuperChain架构

模块

模块 特性
存储 XuperUnion的底层存储基于KV数据库,存储的数据包括区块数据、交易数据、账户 余额、DPOS投票数据、合约账号数据、智能合约数据等,上链的数据全部持久化到底 层存储。不同的链,存储独立。底层存储支持可插拔,从而可以满足不同的业务场景
网络 负责交易数据的网络传播和广播、节点发现和维护。以P2P通信为基础,实现全分布式 结构化拓扑网络结构,数据传输全程加密。局域网穿透技术采用NAT方案,同一条流保 持长连接且复用。多条链复用同一个p2p网络
共识 共识模块用于解决交易上链顺序问题,过滤无效交易并达成全网一致。XuperUnion实 现了更加高效的DPOS共识算法。支持可插拔,从而可以支持不同的业务场景
密码学 用于构造和验证区块、交易的完整性,采用非对称加密算法生成公私钥、地址。匿名性 较好。支持可插拔,从而可以支持不同的业务场景
智能合约 自研并实现了一套智能合约虚拟机XVM,支持丰富的开发语言,智能合约之间并发执行, 支持执行消耗资源,避免恶意攻击
提案 一种解决系统升级问题的机制。比如修改区块大小,升级共识算法。提案整个过程涉及 到发起提案、参与投票、投票生效三个阶段
账号与权限 为了满足合约调用的权限控制,保证XuperUnion网络的健康运转,自研并实现了一套 基于账户的去中心化的合约权限系统。支持权重累计、集合运算等灵活的策略,可以满足 不同的业务场景

智能合约

自研并实现了一套智能合约虚拟机XVM。特点如下:

  1. 合约状态数据与合约代码运行环境分离,从而能够支持多语言虚拟机且各种合约虚拟机只需要做纯粹的无状态合约代码执行;
  2. 支持执行消耗资源,避免恶意攻击;
  3. 支持丰富的智能合约开发语言,比如go,Solitidy,C/C++,Java等;
  4. 利用读写集确保普通合约调用支持并发执行,充分利用计算机多核特性;

权限系统

实现一个去中心化,区块链内置的合约账户权限系统。特点如下:

  1. 支持多种权限模型,比如背书数、背书率、AK集合、CA鉴权、社区治理等;
  2. 支持完善的账号权限管理,比如账号的创建、添加和删除AK、设置AK权重、权限模型;
  3. 支持设置合约调用权限,添加和删除AK、设置AK权重、权限模型;

隐私和保密

XuperUnion支持多种隐私保护和保密机制,包括但不限于:

  1. 数据在p2p网络中采用ECDH加密传输,保障区块链数据的安全性;
  2. 通过助记词技术,在用户私钥丢失的情况下可以恢复;
  3. 多私钥保护的账户体系;
  4. 基于椭圆曲线算法的公私钥加密和签名体系;

性能

交易处理速度:达到9万TPS

  1. 默认采用DPOS作为共识算法;
  2. 交易处理充分利用计算机多核,支持并发执行;
  3. 智能合约通过读写集技术能够支持并发执行;

总结

XuperUnion是百度自研的一套区块链解决方案,采用经典的UTXO记账模式,并且支持丰富的智能合约开发语言,交易处理支持并发执行,拥有完善的账户与权限体系,采用DPOS作为共识算法,交易处理速度可达到9万TPS。

本章节将指导您获取XuperUnion的代码并部署一个基础的可用环境,还会展示一些基本操作

XuperUnion环境部署

准备环境

XuperUnion主要由Golang开发,需要首先准备编译运行的环境

  • 安装go语言编译环境,版本为1.11或更高
  • 安装git
    • 下载地址:git

编译XuperUnion

1
2
cd src/github.com/xuperchain/xuperunion
make
  • 在output目录得到产出xchain和xchain-cli

注解

可能需要配置go语言环境变量($GOROOT, $PATH)

GOPATH问题报错(go1.11版本之后无需关注)
  • 在1.11版本之前需要配置。配置成以下形式:
  • 比如代码路径xxx/github.com/xuperchain/xuperunion/src/baidu.com/xchain/xxx
  • export GOPATH=xxx/github.com/xuperchain/xuperunion

GCC版本需要升级到4或5以上

XuperUnion基本操作

在output下,主要目录有data, logs, conf, plugins等, 二进制文件有xchain,xchain-cli

各目录的功能如下表:

目录名 功能
output/ 节点根目录
├─ conf xchain.yaml: xchain服务的配置信息(注意端口冲突) plugins.conf: 插件的配置信息
├─ data 数据的存放目录,创世块信息,以及共识和合约的样例
│ ··· ├─ blockchain 账本目录
│ ··· ├─ keys 此节点的地址,具有全局唯一性
│ ··· ├─ netkeys 此节点的网络标识ID,具有全局唯一性
│ ··· └─ config 包括创始的共识,初始的资源数,矿工奖励机制等
├─ logs 程序日志目录
├─ plugins so扩展的存放目录
├─ xchain xchain服务的二进制文件
├─ xchain-cli xchain客户端工具
└─ wasm2c wasm工具(智能合约会用到)

部署xchain服务

创建链

在启动xchain服务之前,我们首先需要创建一条链(创世区块),xchain客户端工具提供了此功能

1
2
# 创建xuper链
./xchain-cli createChain
创建链

创建链

这样我们就使用 config/xuper.json 中的配置创建了一条链(此时 data/blockchain 中会生成 xuper 目录,里面即是我们创建的链的账本等文件)

启动服务

启动服务命令十分简单,还可以配合多种参数使用,详见命令行的 -h 输出

1
2
# 启动服务节点
nohup ./xchain &

确认服务状态

按照默认配置,xchain服务会监听37101端口,可以使用如下命令查看xchain服务的运行状态

1
2
# check服务运行状况
./xchain-cli status -H 127.0.0.1:37101
查看状态

查看运行状态

基本功能的使用

创建新账号

xchain中,账号分为普通账号和“合约账号”,这里先介绍普通账号的创建,命令如下

1
2
3
# 创建普通用户, 包含地址,公钥,私钥
./xchain-cli account newkeys --output data/bob
# 在bob目录下会看到文件address,publickey,privatekey生成
创建账号

创建账号

查询资源余额

对于普通账号,可使用如下命令查询账号资源余额,其中 -H 参数为xchain服务的地址

1
./xchain-cli account balance --keys data/keys -H 127.0.0.1:37101
查询余额

查询余额

转账

转账操作需要提供源账号的私钥目录,也就类似“1.2.4.1”中生成的目录,这里注意到并不需要提供目标账号的任何密钥,只需要提供地址即可

1
2
# --keys 从此地址 转给 --to地址 --amount 钱
./xchain-cli transfer --to czojZcZ6cHSiDVJ4jFoZMB1PjKnfUiuFQ --amount 10 --keys data/keys/ -H 127.0.0.1:37101

命令执行的返回是转账操作的交易id(txid)

普通转账

普通转账操作

查询交易信息

通过以下命令可以查询交易的信息,包括交易状态、交易的源和目标账号、交易的金额、所在的区块(如果已上链)等内容

1
2
# 可查询上一步生成的txid的交易信息
./xchain-cli tx query cbbda2606837c950160e99480049e2aec3e60689a280b68a2d253fdd8a6ce931 -H 127.0.0.1:37101
查询交易

查询交易

查询block信息

通过blockid可以查询区块的相关信息,包括区块内打包的交易、所在链的高度、前驱/后继区块的id等内容

1
2
# 可查询上一步交易所在的block id信息
./xchain-cli block 0354240c8335e10d8b48d76c0584e29ab604cfdb7b421d973f01a2a49bb67fee -H 127.0.0.1:37101
查询区块

查询区块

XuperModel

XuperUnion能够支持合约链内并行的很大的原因是由于其底层自研的XuperModel数据模型。

XuperModel是一个带版本的存储模型,支持读写集生成。该模型是比特币utxo模型的一个演变。在比特币的utxo模型中,每个交易都需要在输入字段中引用早期交易的输出,以证明资金来源。同样,在XuperModel中,每个事务读取的数据需要引用上一个事务写入的数据。在XuperModel中,事务的输入表示在执行智能合约期间读取的数据源,即事务的输出来源。事务的输出表示事务写入状态数据库的数据,这些数据在未来事务执行智能合约时将被引用,如下图所示:

XuperModel事务

XuperModel事务

为了在运行时获取合约的读写集,在预执行每个合约时XuperModel为其提供智能缓存。该缓存对状态数据库是只读的,它可以为合约的预执行生成读写集和结果。验证合约时,验证节点根据事务内容初始化缓存实例。节点将再次执行一次合约,但此时合约只能从读集读取数据。同样,写入数据也会在写入集中生效。当验证完生成的写集和事务携带的写集一致时合约验证通过,将事务写入账本,cache的原理如下所示,图中左边部分是合约预执行时的示意图,右边部分是合约验证时的示意图:

XuperModel合约验证

XuperModel合约验证

XuperBridge

内核调用设计

XuperBridge为所有合约提供统一的合约接口,从抽象方式上类似于linux内核对应于应用程序,内核代码是一份,应用程序可以用各种语言实现,比如go,c。类比到合约上就是各种合约的功能,如KV访问,QueryBlock, QueryTx等,这些请求都会通过跟xchain通信的方式来执行,这样在其上实现的各种合约虚拟机只需要做纯粹的无状态合约代码执行。

XuperBridge

XuperBridge

合约与xchain进程的双向通信

xchain进程需要调用合约虚拟机来执行具体的合约代码,合约虚拟机也需要跟xchain进程通信来进行具体的系统调用,如KV获取等,这是一个双向通信的过程。

合约双向通信

合约双向通信

这种双向通信在不同虚拟机里面有不同的实现,

  • 在native合约里面由于合约是跑在docker容器里面的独立进程,因此牵扯到跨进程通信,这里选用了unix socket作为跨进程通信的传输层,xchain在启动合约进程的时候把syscall的socket地址以及合约进程的socket地址传递给合约进程,合约进程一方面监听在unix socket上等待xchain调用自己运行合约代码,另一方面通过xchain的unix socket创建一个指向xchain syscall服务的grpc客户端来进行系统调用。
合约socket

合约socket

  • 在WASM虚拟机里面情况有所不同,WASM虚拟机是以library的方式链接到xchain二进制里面,所以虚拟机和xchain在一个进程空间,通信是在xchain和WASM虚拟机之间进行的,这里牵扯到xchain的数据跟虚拟机里面数据的交换,在实现上是通过WASM自己的模块机制实现的,xchain实现了一个虚拟的WASM模块,合约代码执行到外部模块调用的时候就转到对应的xchain函数调用,由于xchain和合约代码的地址空间不一样,还是牵扯到序列化和反序列化的动作。
WASM合约

WASM合约

PB接口

合约暴露的代码接口

1
2
3
service NativeCode {
    rpc Call(CallRequest) returns (CallResponse);
}

xchain暴露的syscall接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
service Syscall {
    // KV service
    rpc PutObject(PutRequest) returns (PutResponse);
    rpc GetObject(GetRequest) returns (GetResponse);
    rpc DeleteObject(DeleteRequest) returns (DeleteResponse);
    rpc NewIterator(IteratorRequest) returns (IteratorResponse);

    // Chain service
    rpc QueryTx(QueryTxRequest) returns (QueryTxResponse);
    rpc QueryBlock(QueryBlockRequest) returns (QueryBlockResponse);
    rpc Transfer(TransferRequest) returns (TransferResponse);
}

KV接口与读写集

KV的接口:

  • GetObject(key)
  • PutObject(key, value)
  • DeleteObject(key)
  • NewIterator(start, limit)

各个接口对读写集的影响:

  • Get会生成一个读请求
  • Put会产生一个读加一个写
  • Delete会产生一个读加一个特殊的写(TODO)
  • Iterator会对迭代的key产生读

效果:

  • 读请求不会读到最新的其他tx带来的变更
  • 读请求会读到最新的自己的写请求(包括删除)的变更
  • 写请求在提交前不会被其他合约读到
  • 新写入的会被迭代器读到

实现:

cache部分采用ordered map实现, value部分结构如下

1
2
3
4
5
type Value struct {
    Value []byte
    Flag  uint32
    Ref   *Txinput
}

在生成读写集的时候根据Flag判断是只读引用还是写入或者是删除,最后遍历一遍整个map按照key的顺序生成读写集。

合约上下文

每次合约运行都会有一个伴随合约执行的上下文(context)对象,context里面保存了合约的kv cache对象,运行参数,输出结果等,context用于隔离多个合约的执行,也便于合约的并发执行。

Context的创建和销毁

context在合约虚拟机的Run函数里面创建,在xuper3里面已经是每次执行合约的时候创建context。每个context都有一个context id,这个id由合约虚拟机维护,在xchain启动的时候置0,每次创建一个context对象加1,合约虚拟机保存了context id到context对象的映射。context id会传递给合约虚拟机,在Docker里面即是合约进程,在之后的合约发起KV调用过程中需要带上这个context id来标识本次合约调用以找到对应的context对象。

context的销毁时机比较重要,因为我们还需要从context对象里面获取合约执行过程中的Response以及读写集,因此有两种解决方案,一种是由调用合约的地方管理,这个是xuper3里面做的,一种是统一销毁,这个是目前的做法,在打包成块结束调用Finalize的时候统一销毁所有在这个块里面的合约context对象。

合约上下文的操作

  • MakeContext,创建一个context,需要合约的参数等信息
  • RunContext,运行一个context,这一步是执行合约的过程,合约执行的结果会存储在context里面
  • CommitContext,提交context,这一步会把上下文里面对状态的修改提交到持久化层里面,对应MPT就是提交根,对应XuperModule就是生成读写集

跨合约调用

受限于目前的合约调用方式,跨合约调用目前只在native合约里面实现,等后面xuper3统一存储模型就可以真正做到真正的跨虚拟机实现的跨合约调用。

在跨合约调用模型中2个状态需要记录:

KV状态的修改

KV在目前的合约实现里面是通过MPT来实现的,合约执行完毕后生成root hash,验证节点通过验证root hash是否一致来验证KV状态是否一致,在跨合约调用中由于牵扯了多个合约的MPT状态修改,同时每个合约都是一棵单独的树,因此没办法统一root hash,因此在这里我们采用默克尔树的思想,把多个合约修改后的root hash通过字符串拼接的方式组成一个新的串,在hash之后生成多个MPT root的联合hash,验证节点采用相同的算法生成hash串,比对是否相同即可。

还有一个问题需要确定:多个root hash通过什么顺序来组合?

有两个方案:

  • root hash按字母序排列之后再组合
  • 按合约的调用顺序以后序遍历的顺序连接root hash

转账带来的对UTXO的修改

跟之前的合约内transfer一样,通过生成新tx的方式来支持被调用合约内部调用转账,所有新生成的tx的ContractRef指向root tx id,新生成的排列方式按照调用转账的顺序生成。

合约内转账排列

合约内转账排列

调用递归层数的限制

合约调用在入口的地方可以传递一个计数器,每次进行合约调用就加一,如果计数器超过指定阈值则直接执行失败。

合约调用方式的更改

在目前版本(<=2.3)里面,native合约的调用方式为在执行合约之前统一执行SetContext,打包块结束后再执行Finalize,从这种调用方式上可以看出我们假定合约上下文的生命周期是在一个块里面,我们需要改成状态内敛到合约上下文。

共享tx结构体

整个跨合约调用的过程中tx结构体是共享的,所有合约执行的时候被认为是在同一个交易里面进行的,但参数会专门设置。

状态的回滚

native合约在执行失败的时候,不会提交MPT,在多合约执行的情况下

账户权限控制模型

背景

超级链需要一套去中心化的,内置的权限系统 为了实现这个目标,我们借鉴了业界很多现有系统如Ethereum、EOS、Fabric 的优点,设计一个基于账户的合约权限系统

名词解释

  • AK(Access Key):具体的一个address,由密码学算法生成一组公私钥对,然后将公钥用指定编码方式压缩为一个地址。
  • 账号(Account): 在超级链上部署合约需要有账号, 账号可以绑定一组AK(公钥),并且AK可以有不同的权重。 账户的名字具有唯一性。
  • 合约 (Contract): 一段部署在区块链上的可执行字节码,合约的运行会更新区块链的状态。我们允许一个账户部署多个合约。合约的名字具有唯一性。

模型简介

系统会首先识别用户,然后根据被操作对象的ACL的信息来决定用户能否对其进行哪些操作

ACL简介

ACL简介

  • 个人账户AK:是指一个具体的地址Addr
    • 用户的创建是离线的行为,可以通过命令行工具或者API进行创建
  • 合约账户:超级链智能合约的管理单元。
    • 账户的创建:
      • 任何账户或者AK都可以调用系统级智能合约创建账户
      • 创建账户需要指定账户对应的拥有者的地址集,如果一个账户中只有一个地址, 那么这个Addr对账户完全控制;
      • 创建账户需要指定ACL控制策略,用于账户其他管理动作的权限控制;
      • 创建账户需要消耗账户资源;
    • 账户名命名规则;
      • 合约账户由三部分组成,分为前缀,中间部分,后缀。
      • 前缀为XC,后缀为@链名
      • 中间部分为16个数字组成。
      • 在创建合约账号的时候,只需要传入16位数字,在使用合约账号的时候,使用完整的账号。
    • 账户管理:依地址集合据创建时指定的地址集和权限策略,管理账户其他操作
      • 账户股东剔除和加入
      • 账户资产转账
      • 创建合约,创建智能合约需要消耗账户资源,先将utxo资源打到账户下,通过消耗账户的utxo资源创建合约,验证的逻辑需要走账户的ACL控制
      • 合约Method权限模型管理
    • 智能合约:超级链中的一个具体的合约,属于某个账户
      • 账户所属人员允许在账户内部署合约
      • 账户所属人员可以定义合约管理的权限模型
      • 设置合约方法的权限模型,合约内有一个权限表,记录:{ contract.method,permission_model}
    • 合约命名规则:长度为4~16个字符(包括4和16),首字母可选项为[a-ZA-Z_],末尾字符可选项为[a-zA-Z0-9_],中间部分的字符可选项为[a-zA-Z_.]

实现功能

主要有两个功能:账号权限管理、合约权限管理

  1. 账号权限管理 账号的创建、添加和删除AK、设置AK权重、权限模型
  2. 合约权限管理 设置合约调用权限,支持2种权限模型:
    1. 背书阈值:在名单中的AK或Account签名且他们的权重值加起来超过一定阈值,就可以调用合约
    2. AK集合: 定义多组AK集合,集合内的AK需要全部签名,集合间只要有一个集合有全部签名即可

系统设计

ACL架构

ACL架构

ACL数据结构说明

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// --------   Account and Permission Section -------
enum PermissionRule {
    NULL = 0;             // 无权限控制
    SIGN_THRESHOLD = 1;   // 签名阈值策略
    SIGN_AKSET = 2;       // AKSet签名策略
    SIGN_RATE = 3;        // 签名率策略
    SIGN_SUM = 4;         // 签名个数策略
    CA_SERVER = 5;        // CA服务器鉴权
    COMMUNITY_VOTE = 6;   // 社区治理
}

message PermissionModel {
    PermissionRule rule = 1;
    double acceptValue = 2;    // 取决于用哪种rule, 可以表示签名率,签名数或权重阈值
}

// AK集的表示方法
message AkSet {
repeated string aks = 1; //一堆公钥
}

message AkSets {
    map<string, AkSet> sets = 1;   // 公钥or账户名集
    string expression = 2;      // 表达式,一期不支持表达式,默认集合内是and,集合间是or
}

// Acl实际使用的结构
message Acl {
    PermissionModel  pm = 1;             // 采用的权限模型
    map<string, double>  aksWeight = 2;  // 公钥or账户名  -> 权重
    AkSets akSets = 3;
}

签名阈值策略: Sum{Weight(AK_i) , if sign_ok(AK_i)}  >=  acceptValue

系统合约接口

合约接口 用途
NewAccountMethod 创建新的账户
SetAccountACLMethod 更新账户的ACL
SetMethodACLMethod 更新合约Method的ACL

样例

acl模型如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
    "pm": {
        "rule": 1,

        "acceptValue": 1.0
    },
    "aksWeight": {
        "AK1": 1.0,
        "AK2": 1.0
    }
}
  • 其中rule=1表示签名阈值策略,rule=2表示AKSet签名策略
  • 签名的ak对应的weight值加起来>acceptValue,则符合要求

身份认证

背景

Xuperchain节点之间存在双重身份:P2P节点ID和Xuperchain address,为了解决节点间的身份互信,防止中间人攻击和消息篡改,节点间需要一种身份认证机制,可以证明对称节点声明的XChain address是真实有效的

名词解释

Xuperchain address:当前节点的address,一般为data/keys/address P2P节点ID:当前节点P2P的peer.ID

P2P建立连接过程

连接建立时序

连接建立时序

实现过程

  • 新建的net.Stream连接,已经完成了ECDH密钥协商流程,因此此时节点间已经是加密连接。
  • 连接建立后,增加一步身份认证流程,如果通过,则stream建立成功,加入到streamPool中

其中,身份认证流程如下:

  • 身份认证流程通过开关控制,可开启和关闭 DefaultIsAuthentication: true or false
  • 身份验证支持XChain address的验证方式
  • 如果开启身份验证,则身份验证不通过的Stream直接关闭
  • 身份验证是使用XChain的私钥对PeerID+XChain地址的SHA256哈希值进行签名,并将PeerID、Xuperchain公钥、Xuperchain地址、签名数据一起传递给对方进行验证

主要结构修改点

1
2
3
4
5
6
// stream 增加authenticate接口
func (s *Stream) Authenticate() error {}

// 收到身份验证消息后的回调处理函数接口

func (p *P2PServerV2) handleGetAuthentication(ctx context.Context, msg *xuper_p2p.XuperMessage) (*xuper_p2p.XuperMessage, error) {}

合约账号

访问控制列表(ACL)

如果把合约账号当作一家股份制公司,那么ACL便是公司股东投票的机制,ACL可以规定合约账号背后各“股东”账号的权重,只有当“股东”签名的权重之和大于设定阈值时操作才会有效地进行。

超级链中ACL配置格式如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "pm": {
        "rule": 1,              # rule=1表示签名阈值策略,rule=2表示AKSet签名策略
        "acceptValue": 0.6      # acceptValue为签名需达到的阈值
    },
    "aksWeight": {              # aksWeight里规定了每个地址对应账号签名的权重
        "AK1": 0.3,
        "AK2": 0.3
    }
}

了解了访问控制列表的概念,下面我们就来演示一下如何创建一个合约账号

合约账号创建

Xchain的客户端工具提供了新建账号的功能,基本用法如下:

1
xchain-cli account new --desc account.des

这里的 account.des 就是创建账号所需要的配置了,内容如下:

1
2
3
4
5
6
7
8
9
{
    "module_name": "xkernel",
    "method_name": "NewAccount",
    "args" : {
        "account_name": "1111111111111111",  # 说明:账号名称是16位数字组成的字符串
        # acl 中的内容注意转义
        "acl": "{\"pm\": {\"rule\": 1,\"acceptValue\": 0.6},\"aksWeight\": {\"AK1\": 0.3,\"AK2\": 0.3}}"
    }
}

命令运行后就会调用xchain的系统合约功能 NewAccount 创建一个名为 XC1111111111111111@xuper (如果链名字为xuper)的账号

新建合约账号

新建合约账号

除了上述方法,我们还提供了一个比较简易的方式来创建合约账号,命令如下:

1
xchain-cli account new --account 1111111111111111 # 16位数字组成的字符串

上述命令也会创建一个名为 XC1111111111111111@xuper 的账号,由于我们没有制定ACL的具体内容,其ACL被赋值为默认状态,即背后有权限的账号只有当前节点上默认账号一个(地址默认位于 data/keys/address)

注解

创建合约账号的操作需要提供手续费,需要按照命令行运行结果给出的数值,添加一个不小于它的费用(使用 --fee 参数)

合约账号基本操作

查询账号ACL

Xchain的客户端工具提供了ACL查询功能,只需如下命令

1
xchain-cli acl query —account XC1111111111111111@xuper # account参数为合约账号名称
查询合约账号ACL

查询合约账号ACL

查询账号余额

合约账号查询余额和普通账号类似,只是命令行的参数有些许变化

1
./xchain-cli account balance XC1111111111111111@xuper -H 127.0.0.1:37101

使用此命令即可查询`XC1111111111111111@xuper`的余额

查询合约账号

查询合约账号余额

修改账号ACL

修改ACL的配置和创建账号的配置类似

1
2
3
4
5
6
7
8
9
{
    "module_name": "xkernel",
    "method_name": "SetAccountAcl",  # 这里的方法有了变更
    "args" : {
        "account_name": "1111111111111111",
        # acl字段为要修改成的新ACL
        "acl": "{\"pm\": {\"rule\": 1,\"acceptValue\": 0.6},\"aksWeight\": {\"AK3\": 0.3,\"AK4\": 0.3}}"
    }
}

修改ACL的操作,需要符合当前ACL中设置的规则,即需要具有足够权重的账号签名

我们首先生成一个多重签名的交易

1
./xchain-cli multisig gen --desc acl_new.json --from XC1111111111111111@xuper
生成多重签名交易

成多重签名交易

这样就会生成一个默认为`tx.out`的文件,之后使用原ACL中的账号对其进行签名

1
2
./xchain-cli multisig sign --keys data/account/AK1 --output AK1.sign
./xchain-cli multisig sign --keys data/account/AK2 --output AK2.sign
签名交易

签名交易

最后把生成的`tx.out`发出去

1
./xchain-cli multisig send --tx tx.out AK1.sign,AK2.sign AK1.sign,AK2.sign
发送交易

发送交易

至此便完成了ACL的修改

多节点部署

在阅读本节前,请先阅读“快速入门”,当中介绍了创建单节点网络的创建,在该基础上,搭建一个SINGLE共识的多节点网络,其他节点只要新增p2p网络bootNodes配置即可。如果你想搭建一个TDPoS共识的链,仅需要修改创世块参数中“genesis_consensus”配置参数即可。下面将详细介绍相关操作步骤。

p2p网络配置

我们以搭建3个节点的网络为例来说明(其实搭建更多节点的原理是一致的),首先需要有一个节点作为“bootNode”,其他节点启动前都配置这个“bootNode”的地址即可实现

对于bootNode节点,我们需要先获取它的netUrl,具体命令如下:

1
./xchain-cli netUrl get -H 127.0.0.1:37101

如果不是以默认配置启动的,我们需要先生成它的netUrl,然后再获取

1
./xchain-cli netUrl gen -H 127.0.0.1:37101

如此我们会获得一个类似于 /ip4/127.0.0.1/tcp/47101/p2p/QmVxeNubpg1ZQjQT8W5yZC9fD7ZB1ViArwvyGUB53sqf8e 样式的返回

对其他的节点,我们需要修改其服务配置`conf/xchain.yaml`中p2pv2一节

1
2
3
4
5
6
7
p2pV2:
    // port是节点p2p网络监听的默认端口,如果在一台机器上部署注意端口配置不要冲突,
    // node1配置的是47101,node2和node3可以分别设置为47102和47103
    port: 47102
    // 节点加入网络所连接的种子节点的链接信息,
    bootNodes:
    - "/ip4/127.0.0.1/tcp/47101/p2p/QmVxeNubpg1ZQjQT8W5yZC9fD7ZB1ViArwvyGUB53sqf8e"

注解

需要注意的是,如果节点分布在不同的机器之上,需要把netUrl中的本地ip改为机器的实际ip

修改完配置后,即可在每一个节点使用相同配置创建链,然后分别启动bootNode和其他节点,即完成了多节点环境的部署

这里可以使用系统状态的命令检查环境是否正常

1
./xchain systemstatus -H 127.0.0.1:37101

通过变更 -H 参数,查看每个节点的状态,若所有节点高度都是一致变化的,则证明环境部署成功

搭建TDPoS共识网络

XuperUnion系统支持可插拔共识,通过修改创世块的参数,可以创建一个以TDPoS为共识的链。

下面创世块配置(一般位于 data/config/xuper.json)和单节点创世块配置的区别在于创世共识参数genesis_consensus的config配置,各个配置参数详解配置说明如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    "version" : "1",
    "predistribution":[
        {
            "address" : "mahtKhdV5SZP4FveEBzX7j6FgUGfBS9om",
            "quota" : "100000000000000000000"
        }
    ],
    "maxblocksize" : "128",
    "award" : "1000000",
    "decimals" : "8",
    "award_decay": {
        "height_gap": 31536000,
        "ratio": 1
    },
    "genesis_consensus": {
        "name": "tdpos",
        "config": {
            # tdpos共识初始时间,声明tdpos共识的起始时间戳,建议设置为一个刚过去不旧的时间戳
            "timestamp": "1548123921000000000",
            # 每一轮选举出的矿工数,如果某一轮的投票不足以选出足够的矿工数则默认复用前一轮的矿工
            "proposer_num":"3",
            # 每个矿工连续出块的出块间隔
            "period":"3000",
            # 每一轮内切换矿工时的时间间隔,需要为period的整数倍
            "alternate_interval":"6000",
            # 切换轮时的出块间隔,即下一轮第一个矿工出第一个块距离上一轮矿工出最后一个块的时间间隔,需要为period的整数配
            "term_interval":"9000",
            # 每一轮内每个矿工轮值任期内连续出块的个数
            "block_num":"200",
            # 为被提名的候选人投票时,每一票单价,即一票等于多少Xuper
            "vote_unit_price":"1",
            # 指定第一轮初始矿工,矿工个数需要符合proposer_num指定的个数,所指定的初始矿工需要在网络中存在,不然系统轮到该节点出块时会没有节点出块
            "init_proposer": {
                "1":["RU7Qv3CrecW5waKc1ZWYnEuTdJNjHc43u","XpQXiBNo1eHRQpD9UbzBisTPXojpyzkxn","SDCBba3GVYU7s2VYQVrhMGLet6bobNzbM"]
            }
        }
    }
}

修改完每个节点的创世块配置后,需要确认各节点的 data/blockchain 目录下内容为空。然后重新按照上一节的步骤,在各节点上创建链,启动所有节点,即完成TDPoS共识的环境部署。

创建合约

编写合约

源码可以参考 xuperchain/xuperunion/contractsdk/go/example/math/math.go

主要实现struct中initialize,invoke和query三个方法来实现自己的逻辑

1
2
3
func (m *math) Initialize(nci code.Context) code.Response { ... }
func (m *math) Invoke(nci code.Context) code.Response { ... }
func (m *math) Query(nci code.Context) code.Response { ... }

每个函数的入口参数均为 code.Context ,具体结构可参考 xuperchain/xuperunion/contractsdk/go/code/context.go 接口中定义了如何获取传入方法的参数,如何使用读写功能,以及如何在链上进行交易/区块的查询、转账或调用其他合约

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
type Context interface {
    Args() map[string][]byte
    Caller() string
    Initiator() string
    AuthRequire() []string

    PutObject(key []byte, value []byte) error
    GetObject(key []byte) ([]byte, error)
    DeleteObject(key []byte) error
    NewIterator(start, limit []byte) Iterator

    QueryTx(txid []byte) (*TxStatus, error)
    QueryBlock(blockid []byte) (*Block, error)
    Transfer(to string, amount *big.Int) error
    Call(module, contract, method string, args map[string][]byte) (*Response, error)
}

对于C++版本的合约,可以参考代码 contractsdk/cpp/example/counter.cc 原理和Golang合约是一致的

注解

除了 Initialize 外的其他函数,是可以自行定义函数名的,可参考contractsdk/go/example/counter/counter.go中的具体实例,在之后调用合约时写明函数名即可

部署wasm合约

  1. 编译合约 - Golang

    注意合约编译环境与源码编译环境一致,编译参数如下

    1
    GOOS=js GOARCH=wasm go build XXX.go
    
  2. 编译合约 - C++

    对于C++合约,已提供编译脚本,位于 contractsdk/cpp/build.sh,需要注意的是,脚本依赖从hub.baidubce.com拉取的docker镜像,请在编译前确认docker相关环境是可用的

  3. 部署wasm合约

    将编译好的合约二进制文件(以counter为例)放到目录node/data/blockchain/${chain name}/native/下,这里我们默认的链名 ${chain name}=xuper

    部署合约的操作需要由合约账号完成,部署操作同样需要支付手续费,操作前需要确保合约账号下有足够的余额

    在合约账号权限AK是节点账号的情况下(即ACL中有当前节点的签名即可生效),我们按照如下命令即可完成部署

    1
    ./xchain-cli wasm deploy --account XC1111111111111111@xuper --cname counter -H localhost:37101 data/blockchain/xuper/native/counter
    

    运行时会提示手续费的数目,使用 --fee 参数传入即可

    对于合约账号权限AK有多个的情况,部署操作需要多方的签名,需要提前在 data/acl/addrs 维护好需要的合作的地址列表(每个AK地址写一行),运行时增加 -m 参数

    1
    ./xchain-cli wasm deploy --account XC1111111111111111@xuper --cname counter -H localhost:37101 -m data/blockchain/xuper/native/counter
    

    生成的 tx.out 就像类似修改ACL的操作一样,收集各个AK的签名,然后发送即可

部署native合约

  1. 编译合约

    编译native合约时,只要保持环境和编译xuperunion源码时一致即可,我们还是以example中的counter为例

    1
    2
    3
    cd contractsdk/go/example/counter
    go build
    # 产出二进制counter
    
  2. 激活合约

    native合约部署需要进行一次 提案-投票 操作,

发起提案

XuperUnion中有多种提案-投票操作场景,但原理都是一致的,我们以通过提案更改共识算法(single改为tdpos)来介绍具体的操作流程

部署一个Single共识的超级链环境已经在“快速入门”一节有介绍

首先我们需要准备一个tdpos共识的配置,包括出块时间、代表名单等(假设文件名为proposal.json)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
    "module": "proposal",
    "method": "Propose",
    "args" : {
        "min_vote_percent": 51,                 # 生效的资源比例
        "stop_vote_height": 800                 # 计票截至的高度
    },
    "trigger": {
        "height": 1000,                         # 期望生效的高度
        "module": "consensus",
        "method": "update_consensus",
        "args" : {
            "name": "tdpos",
            "config": {
                "version":"2",
                "proposer_num":"2",             # 代表个数
                "period":"3000",
                "alternate_interval":"6000",
                "term_interval":"9000",
                "block_num":"20",
                "vote_unit_price":"1",
                "init_proposer": {              # 出块的代表名单
                    "1":["dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN", "U5SHuTiiSP1JAAHVMknqrm66QXk2VhXsK"]
                }
            }
        }
    }
}

需要注意的是当前的区块高度,来设置合理的截至计票高度和生效高度。然后在矿工节点下,执行给自己转账的操作,并在 --desc 参数里传入提案

1
./xchain-cli transfer --to dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN --desc proposal.json

运行后会得到本次提案的交易id,需要记录下来供投票使用

对提案进行投票操作由如下命令执行

1
./xchain-cli vote --txid f26d670b695d9fd5da503a34d130ef19e738b35e031b18b70ad4cbbf6dfe2656 --freeze 1100 --amount 100002825031900000000

这里需要注意进行投票的节点需要有矿工账号的密钥对,以及 --freeze 参数的冻结高度大于提案生效的高度。因为最终通过的规则是投票资源大于总资源的51%,所以需要初始token量最多的矿工账号来进行投票,并保证token数符合要求。

如此进行后,等到区块出到设定的生效高度,便完成了提案-投票的整个流程。其他场景的提案机制都是类似的,仅是json配置文件不同而已。

配置变更

配置多盘存储

由区块链本身特点决定的,区块链服务启动后需要的存储空间会逐渐变多,即使交易不频繁,每到固定出块时间也会占用少量的存储空间。XuperUnion提供了一种可以将存储路径配置在多个磁盘上的功能,来更好地支持单个磁盘存储空间不充裕的场景。

位于代码目录下的 conf/xchain.yaml ,包含了大部分Xuperunion服务启动的配置项,其中有磁盘相关的章节

1
2
3
4
5
6
7
8
# 数据存储路径
datapath: ./data/blockchain

# 多盘存储的路径
datapathOthers:
    - /ssd1/blockchain
    - /ssd2/blockchain
    - /ssd3/blockchain

只需将“多盘存储路径”部分去掉注释,便可以灵活配置多个数据存储位置。

替换扩展插件

XuperUnion采用了动态链接库的方式实现了加密、共识算法等扩展插件,可以根据实际使用场景进行替换。

插件目录位于 plugins ,对应的配置文件为 conf/plugins.conf (json格式)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "crypto":[{
        "subtype": "default",
        "path": "plugins/crypto/crypto-default.so.1.0.0",
        "version": "1.0.0",
        "ondemand": false
    },{
        "subtype": "schnorr",
        "path": "plugins/crypto/crypto-schnorr.so.1.0.0",
        "version": "1.0.0",
        "ondemand": false
    }]
    # ......
}

需要替换插件则修改对应的 .so 文件路径即可

电子存证合约

代码样例参看:contractsdk/go/example/eleccert.go

电子存证合约简介

电子存证应用主要是通过区块链解决的存证中的信任问题,而存证合约只需做简单的读写操作即可

电子存证合约具备的读写操作

  • 通过invoke方法,put存证到区块链
  • 通过query方法,get存证

调用json文件示例

Invoke

./xchain-cli native invoke -a '下面json中args字段的内容' --method save -H localhost:37101 eleccert

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "module_name": "native",       // native or wasm
    "contract_name": "eleccert",   // contract name
    "method_name": "invoke",       // invoke or query
    "args": {
        "owner": "aaa",            // user name
        "filehash": "存证文件的hash值",
        "timestamp": "存证的timestamp"
    }
}

Query

./xchain-cli native query -a 'args内容' --method query -H localhost:37101 eleccert

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "module_name": "native",       // native or wasm
    "contract_name": "eleccert",   // contract name
    "method_name": "query",        // invoke or query
    "args": {
        "owner": "aaa",            // user name
        "filehash": "文件hash值"
    }
}
// output
{
    "filehash": "文件hash值",
    "timestamp": "文件存入timestamp"
}

数字资产交易

代码样例参看:contractsdk/go/example/erc721.go

ERC721简介

ERC721是数字资产合约,交易的商品是非同质性商品。其中,每一份资产,也就是token_id都是独一无二的类似收藏品交易。

ERC721具备哪些功能

  • 通过initialize方法,向交易池注入自己的token_id
    • 注意token_id必须是全局唯一
  • 通过invoke方法,执行不同的交易功能
    • transfer: userA将自己的某个收藏品token_id转给userB
    • approve: userA将自己的某个收藏品token_id的售卖权限授予userB
    • transferFrom: userB替userA将赋予权限的收藏品token_id卖给userC
    • pproveAll: userA将自己的所有收藏品token_id的售卖权限授予userB
  • 通过query方法,执行不同的查询功能
    • balanceOf: userA的所有收藏品的数量
    • totalSupply: 交易池中所有的收藏品的数量
    • approvalOf: userA授权给userB的收藏品的数量

调用json文件示例

Initialize

./xchain-cli wasm invoke -a '下面json中args字段的内容' --method initialize -H localhost:37101 erc721

1
2
3
4
5
6
7
8
9
{
    "module_name": "native",      # native或wasm
    "contract_name": "erc721",    # contract name
    "method_name": "initialize",  # initialize or query or invoke
    "args": {
        "from": "dudu",           # userName
        "supply": "1,2"           # token_ids
    }
}

Invoke

./xchain-cli native invoke -a 'args内容' --method invoke -H localhost:37101 erc721

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
    "module_name": "native",      # native或wasm
    "contract_name": "erc721",    # contract name
    "method_name": "invoke",      # initialize or query or invoke
    "args": {
        "action": "transfer",     # action name
        "from": "dudu",           # usera
        "to": "chengcheng",       # userb
        "token_id": "1"           # token_ids
    }
}
{
    "module_name": "native",      # native或wasm
    "contract_name": "erc721",    # contract name
    "method_name": "invoke",      # initialize or query or invoke
    "args": {
        "action": "transferFrom", # action name
        "from": "dudu",           # userA
        "caller": "chengcheng",   # userB
        "to": "miaomiao",         # userC
        "token_id": "1"           # token_ids
    }
}
{
    "module_name": "native",      # native或wasm
    "contract_name": "erc721",    # contract name
    "method_name": "invoke",      # initialize or query or invoke
    "args": {
        "action": "approve",      # action name
        "from": "dudu",           # userA
        "to": "chengcheng",       # userB
        "token_id": "1"           # token_ids
    }
}

Query

./xchain-cli native query -a 'args内容' --method query -H localhost:37101 erc721

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
    "module_name": "native",     # native或wasm
    "contract_name": "erc721",   # contract name
    "method_name": "query",      # initialize or query or invoke
    "args": {
        "action": "balanceOf",   # action name
        "from": "dudu"           # userA
    }
}
{
    "module_name": "native",     # native或wasm
    "contract_name": "erc721",   # contract name
    "method_name": "query",      # initialize or query or invoke
    "args": {
        "action": "totalSupply"  # action name
    }
}
{
    "module_name": "native",      # native或wasm
    "contract_name": "erc721",    # contract name
    "method_name": "query",       # initialize or query or invoke
    "args": {
        "action": "approvalOf",   # action name
        "from": "dudu",           # userA
        "to": "chengcheng"        # userB
    }
}

智能合约SDK使用说明

XuperUnion为方便用户开发属于自己的智能合约,提供了一整套SDK套件,即XuperCDT(XuperChain Crontract Development Toolkit),包含C++语言和Go语言

C++接口API

get_object

bool ContextImpl::get_object(const std::string& key, std::string* value)

输入

参数 说明
key 查询的key值
value 根据key查到的value值

输出

参数 说明
true key值查询成功,返回value值
false key值不存在

put_object

bool ContextImpl::put_object(const std::string& key, const std::string& value)

输入

参数 说明
key 存入的key值
value 存入key值对应的value值

输出

参数 说明
true 存入db成功
false 存入db失败

delete_object

bool ContextImpl::delete_object(const std::string& key)

输入

参数 说明
key 将要删除的key值

输出

参数 说明
true 删除成功
false 删除失败

query_tx

bool ContextImpl::query_tx(const std::string &txid, Transaction* tx)

输入

参数 说明
txid 待查询的txid
tx 得到此txid的transaction

输出

参数 说明
true 查询交易成功
false 查询交易失败

query_block

bool ContextImpl::query_block(const std::string &blockid, Block* block)

输入

参数 说明
blockid 待查询的blockid
block 得到此blockid的block

输出

参数 说明
true 查询block成功
false 查询block失败

table

定义表格
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 表格定义以proto形式建立,存放目录为contractsdk/cpp/pb
syntax = "proto3";
option optimize_for = LITE_RUNTIME;
package anchor;
message Entity {
    int64 id = 1;
    string name = 2;
    bytes desc = 3;
}
// table名称为Entity,属性分别为id,name,desc
初始化表格
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 定义表格的主键,表格的索引
struct entity: public anchor::Entity {
    DEFINE_ROWKEY(name);
    DEFINE_INDEX_BEGIN(2)
    DEFINE_INDEX_ADD(0, id, name)
    DEFINE_INDEX_ADD(1, name, desc)
    DEFINE_INDEX_END();
};
// 声明表格
xchain::cdt::Table<entity> _entity;
put
1
2
template <typename T>
bool Table<T>::put(T t)

输入

参数 说明
t 待插入的数据项

输出

参数 说明
true 插入成功
false 插入失败

样例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 参考样例 contractsdk/cpp/example/anchor.cc
DEFINE_METHOD(Anchor, set) {
    xchain::Context* ctx = self.context();
    const std::string& id= ctx->arg("id");
    const std::string& name = ctx->arg("name");
    const std::string& desc = ctx->arg("desc");
    Anchor::entity ent;
    ent.set_id(std::stoll(id));
    ent.set_name(name.c_str());
    ent.set_desc(desc);
    self.get_entity().put(ent);
    ctx->ok("done");
}
find
1
2
template <typename T>
bool Table<T>::find(std::initializer_list<PairType> input, T* t)

输入

参数 说明
input 查询关键字
t 返回的数据项

输出

参数 说明
true 查询成功
false 查询失败

样例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
DEFINE_METHOD(Anchor, get) {
    xchain::Context* ctx = self.context();
    const std::string& name = ctx->arg("key");
    Anchor::entity ent;
    if (self.get_entity().find({{"name", name}}, &ent)) {
        ctx->ok(ent.to_str());
        return;
    }
    ctx->error("can not find " + name);
}
scan
1
2
template <typename T>
std::unique_ptr<TableIterator<T>> Table<T>::scan(std::initializer_list<PairType> input)

输入

参数 说明
input 查询关键字

输出

参数 说明
TableIterator 符合条件的迭代器

样例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
DEFINE_METHOD(Anchor, scan) {
    xchain::Context* ctx = self.context();
    const std::string& name = ctx->arg("name");
    const std::string& id = ctx->arg("id");
    // const std::string& desc = ctx->arg("desc");
    auto it = self.get_entity().scan({{"id", id},{"name", name}});
    Anchor::entity ent;
    int i = 0;
    std::map<std::string, bool> kv;
    while(it->next()) {
        if (it->get(&ent)) {
            /*
            std::cout << "id: " << ent.id()<< std::endl;
            std::cout << "name: " << ent.name()<< std::endl;
            std::cout << "desc: " << ent.desc()<< std::endl;
            */
            if (kv.find(ent.name()) != kv.end()) {
                ctx->error("find duplicated key");
                return;
            }
            kv[ent.name()] = true;
            i += 1;
        } else {
            std::cout << "get error" << std::endl;
        }
    }
    std::cout << i << std::endl;
    if (it->error()) {
        std::cout << it->error(true) << std::endl;
    }
    ctx->ok(std::to_string(i));
}
del
1
2
template <typename T>
bool Table<T>::del(T t)

输入

参数 说明
t 一个数据项

输出

参数 说明
true 删除成功
false 删除失败

样例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
DEFINE_METHOD(Anchor, del) {
    xchain::Context* ctx = self.context();
    const std::string& id= ctx->arg("id");
    const std::string& name = ctx->arg("name");
    const std::string& desc = ctx->arg("desc");
    Anchor::entity ent;
    ent.set_id(std::stoll(id));
    ent.set_name(name.c_str());
    ent.set_desc(desc);
    self.get_entity().del(ent);
    ctx->ok("done");
}

Go接口API

GetObject

func GetObject(key []byte) ([]byte, error)

输入

参数 说明
key 查询的key值

输出

参数 说明
value, true key值查询成功,返回value值
_, false key值不存在

PutObject

func PutObject(key []byte, value []byte) error

输入

参数 说明
key 存入的key值
value 存入key值对应的value值

输出

参数 说明
true 存入db成功
false 存入db失败

DeleteObject

func DeleteObject(key []byte) error

输入

参数 说明
key 将要删除的key值

输出

参数 说明
true 删除成功
false 删除失败

QueryTx

func QueryTx(txid string) (*pb.Transaction, error)

输入

参数 说明
txid 待查询的txid

输出

参数 说明
tx, true 查询交易成功, 得到此txid的transaction
_, false 查询交易失败

QueryBlock

func QueryBlock(blockid string) (*pb.Block, error)

输入

参数 说明
blockid 待查询的blockid

输出

参数 说明
block, true 查询block成功, 得到此blockid的block
_, false 查询block失败

NewIterator

func NewIterator(start, limit []byte) Iterator

输入

参数 说明
start 关键字
limit 数据项的最大数量

输出

参数 说明
Iterator Interator的接口

样例

1
2
3
4
5
6
Key() []byte
Value() []byte
Next() bool
Error() error
// Iterator 必须在使用完毕后关闭
Close()

操作指导

如何获取XuperUnion

目前XuperUnion已经发布了2个版本,最新版本为v3.2,可以在github获取发布包

如何升级软件

当版本升级时,需要更新为新版本的代码,并重新编译,然后将 plugins文件夹, 二进制文件xchain,xchain-cli 全部替换后全部重新启动即可,注意多节点模式下需要先启动bootNodes节点。

配置文件说明

XuperUnion的配置文件默认读取有3个优先级:

  • 默认配置:系统中所有配置项都有默认的配置信息,这个是优先级最低的配置;
  • 配置文件:通过读取配置文件的方式,可以覆盖系统中默认的参数配置,默认的配置文件为 ./conf/xchain.yaml;
  • 启动参数:有一些参数支持启动参数的方式设置,该设置方式的优先级最高,会覆盖配置文件中的配置项;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
log:
filepath: logs // 日志输出目录
filename: xchain // 日志文件名
console: true //是否答应console日志
level : trace // 日志等级,debug < trace < info < warn < error < crit
tcpServer:
port: :57404 // 节点RPC服务监听端口
p2pv2:
port: 47404 // 节点p2p网络监听的端口
bootNodes: /ip4/127.0.0.1/tcp/47401/p2p/QmXRyKS1BFmneUEuwxmEmHyeCSb7r7gSNZ28gmDXbTYEXK  // 节点加入网络链接的种子节点的netUrl
miner:
keypath: ./data/keys //节点address目录
datapath: ./data/blockchain //账本存储目录
utxo:
cachesize: 5000 //Utxo内存cache大小设置
tmplockSeconds: 60 //GenerateTx的临时锁定期限,默认是60秒

各文件说明

模块 功能及子文件说明
acl acl查询 account_acl.go 查询合约账号ACL的接口定义 acl_manager.go 查询合约账号ACL,合约方法ACL的具体实现 contract_acl.go 查询合约方法ACL的接口定义
cmd XuperUnion命令行功能集合 xchain XuperUnion命令行功能集合,比如多重签名、交易查询、区块查询、合约部署、合约调用、余额查询等
common 公共组件 batch_chan.go 将交易批量写入到channel中 common.go 获取序列化后的交易/区块的大小 lru_cache.go lru cache实现 util.go 去重string切片中的元素
config 系统配置文件 config.go 包括日志配置、Tcp配置、P2p配置、矿工配置、Utxo配置、Fee配置、合约配置、控制台配置、节点配置、raft配置等
consensus 共识模块 base 共识算法接口定义 consensus.go 可插拔共识实现 tdpos dpos共识算法的具体实现 single single共识算法的具体实现
contract 智能合约 contract.go 智能合约接口定义 contract_mgr.go 创建智能合约实例 kernel 系统级串行智能合约 proposal 提案 wasm wasm虚拟机
core xchaincore.go 区块链的业务逻辑实现 xchainmg.go 负责管理多条区块链 xchainmg_validate.go 对区块、交易、智能合约的合法性验证业务逻辑 sync.go 节点主动向其它节点同步区块业务逻辑 xchaincore_net.go 通过广播形式向周围节点要区块 xchainmg_net.go 注册接收的消息类型 xchainmg_util.go 权限验证
crypto 密码学模块 account 生成用户账户 client 密码学模块的客户端接口 config 定义创建账户时产生的助记词中的标记符的值,及其所对应的椭圆曲线密码学算法的类 hash hash算法 sign 签名相关 utils 常用功能
global 全局方法/变量 common.go 全局方法 global.go 全局变量
kv 存储接口与实现 kvdb 单盘存储 mstorage 多盘存储
ledger 账本模块 genesis.go 创世区块相关实现 ledger.go 账本核心业务逻辑实现 ledger_hash.go 账本涉及的hash实现,如生成Merkle树,生成区块ID
log 日志模块 log.go 创建日志实例
p2pv2 p2p网络模块 pb p2p网络消息的pb定义 config.go p2p网络配置 filter.go p2p网络节点过滤实现 server.go p2p网络对外接口实现 stream.go p2p网络流的定义与实现 subscriber.go p2p网络消息订阅定义与实现 util.go p2p网络的全局方法 handlerMap.go p2p网络消息处理入口 node.go p2p网络节点定义与实现 stream_pool.go p2p网络节点对应的流定义与实现 type.go p2p网络对外接口定义
permission 权限验证模块 permission.go 权限验证的业务逻辑实现 ptree 权限树 rule 权限模型 utils 通用工具
pluginmgr 插件管理模块 pluginmgr.go 插件管理的业务逻辑实现 xchainpm.go 插件初始化工作
replica 多副本模块 replica.go 多副本raft业务逻辑实现
server util.go 通用工具实现,如获取远程节点ip
xuper3 contract contract/bridge xuperbridge定义与实现 contract/kernel 系统级合约(走预执行) contract/vm.go 虚拟机接口定义
xuper3 xmodel xmodel实现 xmodel/pb 版本数据pb定义 xmodel/dbutils.go xmodel通用方法 xmodel/env.go 预执行环境初始化 xmodel/xmodel_cache.go model cache实现 xmodel/xmodel_iterator.go model迭代器实现 xmodel/xmodel_verify.go 读写集验证 xmodel/interface.go xmodel接口定 xmodel/versioned_data.go 版本数据 xmodel/xmodel_cache_iterator.go model cache迭代器 xmodel/xmodel.go model业务逻辑实现
vendor 依赖的三方库
utxo utxo模块 acl_valid_verify.go acl验证业务逻辑实现,包括SetAccountAcl, SetMethodAcl, 合约调用时的权限验证 topsort.go 交易集合的拓扑排序实现 txhash 交易相关的hash async.go 异步处理 tx_contract_generator.go 合约交易操作 utxo_cache.go utxo cache实现 utxo_item.go utxo表定义 withdraw.go 赎回实现 tx_contract_verifier.go 合约交易操作

指令介绍(API)

节点rpc接口

详细见:pb/xchain.proto

API 功能
rpc createAccount(AccountInput) returns (AccountOutput) 创建公私钥对
rpc GenerateTx(TxData) returns (TxStatus) 生成交易
rpc PostTx(TxStatus) returns (CommonReply) 对一个交易进行验证并转发给附近网络节点
rpc BatchPostTx(BatchTxs) returns (CommonReply) 对一批交易进行验证并转发给附近网络节点
rpc QueryAcl(AclStatus) returns (AclStatus) 查询合约账号/合约方法的Acl
rpc QueryTx(TxStatus) returns (TxStatus) 查询一个交易
rpc GetBalance(AddressStatus) returns (AddressStatus) 查询可用余额
rpc GetFrozenBalance(AddressStatus) returns (AddressStatus) 查询被冻结的余额
rpc SendBlock(Block) returns (CommonReply) 将当前区块为止的所有区块上账本
rpc GetBlock(BlockID) returns (Block) 从当前账本获取特定区块
rpc GetBlockChainStatus(BCStatus) returns (BCStatus) 获取账本的最新区块数据
rpc ConfirmBlockChainStatus(BCStatus) returns (BCTipStatus) 判断某个区块是否为账本主干最新区块
rpc GetBlockChains(CommonIn) returns (BlockChains) 获取所有的链名
rpc GetSystemStatus(CommonIn) returns (SystemsStatusReply) 获取系统状态
rpc GetNetUrl(CommonIn) returns (RawUrl) 获取区块链网络中某个节点的url
rpc GenerateAccountByMnemonic(GenerateAccountByMnemonicInput) returns (AccountMnemonicInfo) 创建一个带助记词的账号
rpc CreateNewAccountWithMnemonic(CreateNewAccountWithMnemonicInput) returns (AccountMnemonicInfo) 通过助记词恢复账号
rpc MergeUTXO (TxData) returns (CommonReply) 将同一个地址的多个余额项合并
rpc SelectUTXOV2 (UtxoInput) returns(UtxoOutput) 查询一个地址/合约账户对应的余额是否足够
rpc QueryContract(QueryContractRequest) returns (QueryContractResponse) 查询合约数据

开发者接口

详细见:contractsdk/pb/contract.proto

API 功能
rpc PutObject(PutRequest) returns (PutResponse) 产生一个读加一个写
rpc GetObject(GetRequest) returns (GetResponse) 生成一个读请求
rpc DeleteObject(DeleteRequest) returns (DeleteResponse) 产生一个读加一个特殊的写
rpc NewIterator(IteratorRequest) returns (IteratorResponse) 对迭代的key产生读
rpc QueryTx(QueryTxRequest) returns (QueryTxResponse) 查询交易
rpc QueryBlock(QueryBlockRequest) returns (QueryBlockResponse) 查询区块
rpc ContractCall(ContractCallRequest) returns (ContractCallResponse) 合约调用
rpc Ping(PingRequest) returns (PingResponse) 探测是否存活

常见问题解答

XuperUnion是完全匿名的嘛

XuperUnion包括普通的AK以及合约账户。创建普通AK是免费的,而创建合约账号需要消耗账户资源,对于AK操作做是完全匿名的,而合约账号通常和若干个合约绑定,匿名性较AK差。

XuperUnion出块时间是多少

XuperUnion支持可插拔共识算法,默认共识算法为TDPOS。对于TDPOS共识算法,出块时间由period,alternate_interval,term_interval决定。term_interval表示更换轮的时间间隔,alternate_interval表示每一轮内不同的候选人更换时间间隔,period表示同一个候选人内部出块间隔。 对于Pow算法,通过配置出块难度间接确定出块时间。对于Single算法,出块时间取决于配置文件中的配置参数。

XuperUnion安全嘛

XuperUnion通过椭圆曲线算法生成随机数并生成私钥和相应的公钥,对公钥进行SHA256散列后,使用Ripemd160生成散列摘要,然后使用base58生成地址,通过公钥无法推导出私钥,通过地址无法推导出公钥。匿名性比较好。同时,XuperUnion自研并实现了一套基于合约账号的权限系统,通过多私钥保护,丰富的权限模型保障合约调用的安全性。

词汇表

词汇 定义
交易 对区块链进行状态更改的最小操作单元。通常表现为普通转帐以及智能合约调用。
区块(创世区块、普通区块、配置区块、当前区块) 区块链中的最小确认单元,由零个或多个交易组成,一个区块中的交易要么全部生效,要么全部不生效。创世区块:区块链上第一个区块,确定分配规则、共识算法等系统级别的参数。配置区块:需要升级区块链系统级配置时,通过配置区块生效。
账本 存储区块数据、交易数据。
UTXO 一种余额记账方式。用于存储账号的余额数据。
区块链 由若干个区块组成的DAG。从数据结构上来说,区块链就是一个DAG。
系统链 区块链网络中的第一条区块链实例。
平行链 从系统链衍生出来的子链,解决扩展性问题。
跨链 不同的区块链之间的通信操作,目的是实现区块链世界的价值互连,解决扩展性问题。
账户(用户账户、合约账户) 一种本地或自定义权限的链上标识符。本地标识符称为用户账户,通常分配一个公钥和一个私钥;自定义权限的链上标识符称为合约账户,通常分配一个或多个密钥或多个账户。
密钥对(公钥、私钥) 私钥以及由私钥生成的对应的公钥。私钥通常用于签名,公钥通常用于验证。
地址 与用户数据挂钩的最小单元。地址可以是合约账户,也可以是由公钥生成的一个长度为33的字符串。
签名(普通签名、多重签名) 利用密码学哈希函数单向不可逆、抗碰撞特性,进行身份确认的一种机制。
共识 一种确认区块中交易集正确性以及交易上链顺序的机制。
委托权益证明 一种共识算法,通过选举出区块链网络中有限节点作为代表并轮流出块记账。
智能合约(系统级、用户合约) 一个由计算机处理、可执行合约条款的交易协议,其总体目标是满足协议既定的条件,例如支付、抵押、保密协议。
权限(许可) 一种安全机制,通过评估签名权限来验证一个或一组操作是否被正确授权。
虚拟机 智能合约的运行环境。通常包括合约上下文管理。
最长链 区块链中高度最大的分支。
DAG 有向无环图。
双重消费 同一份数据同时消费多次。
最终一致性 存在某个时刻,整个系统达成一致状态。区块链满足最终一致性。
发起人 发起交易的账号,通常为用户账户或合约账号。
见证人 当选为当前周期内出块节点。
节点 区块链网络中的一个节点。
周期(Epoch) 在委托权益证明共识算法中,一轮出块时间为一个周期。
提案 一种区块链系统级配置进行升级的机制。
查询 对区块链中的数据按照key进行查询。
对等网络 网络中的节点直接互联并交互信息,不需要借助第三方。
拜占庭(拜占庭问题、拜占庭容错) 拜占庭问题:在一个需要共识系统中,由于作恶节点导致的问题。拜占庭容错:在一个需要共识系统中,即使出现作恶节点也能保证系统正常运转的能力。
状态转移系统 一个维护状态变化的系统。区块链通常被认为是一种状态转移系统,其中的状态通常包括账本、余额、合约数据。
读写集 用于支持智能合约并发执行的一种技术。

Indices and tables