治理手册¶
这一节主要讲解如何治理权限合约。如果您是业务方,无需阅读接下来的内容;如果您是治理方,想了解如何部署权限合约、配置权限规则,则需要继续阅读本节。
关键概念¶
业务合约与权限合约¶
权限治理涉及的合约分为两类:业务合约、权限合约。
业务合约是用户自己开发的合约,不属于本组件。业务合约通过访问权限合约的canCallFunctoin函数来拦截非法调用。
权限合约是本组件提供的AuthManager合约,用于配置哪些账户可以访问哪些合约的哪些函数。可以治理多个业务合约的权限。
组¶
组定义了哪些账户可以访问哪些函数。组包含的信息如下:
账户列表
模式。包含两个可选择的模式:
黑名单模式
白名单模式
函数列表
通过上面的配置,即可确定哪些账户可以访问哪些函数:
配置了一个黑名单组,意味着仅组内账户不能访问这些函数
配置了一个白名单组,意味着仅组内账户可以访问这些函数
治理模式¶
权限治理合约有两种模式:管理员模式、委员会模式
管理员模式下,由单一管理员修改组配置; 还可以转让管理员权限。
委员会模式下,所有操作均通过投票进行。委员会成员可以修改组配置。还可以修改委员会列表、投票规则等。
一个权限治理合约的模式在部署时即确定,一旦确定一种模式,就无法更改。
投票模式¶
投票包含两种规则:多签模式和阈值权重模式。
多签模式下,当投票数达到最小签名数时,投票即通过。
阈值权重模式下,每个委员均配有对应的权重,当已投票的总权重达到最小阈值时,投票即通过。
多签是阈值权重模式下的一个特例,即所有委员会的权重为1。
AuthManager合约接口列表¶
合约部署
合约部署时需要决定是管理员模式还是治理委员会模式。以下为管理员模式下权限配置接口:
createGroup 创建组
addAccountToGroup 将账户添加到组
addFunctionToGroup 将合约函数关联到组
removeAccountFromGroup 将账户从组内移除
removeFunctionFromGroup 将合约函数与组的关联取消
以下为治理委员会模式下的权限配置接口:
requestCreateGroup 请求创建组
requestAddAccountToGroup 请求将账户添加到组
requestAddFunctionToGroup 请求将合约函数关联到组
requestRemoveAccountFromGroup 请求将账户从组内移除
requestRemoveFunctionFromGroup 请求将合约函数与组的关联取消
approveSingle 投票请求
deleteSingle 删除请求
getRequestSingle 查看请求
executeCreateGroup 创建组
executeAddAccountToGroup 执行将账户添加到组
executeAddFunctionToGroup 执行将合约函数关联到组
executeRemoveAccountFromGroup 执行将账户从组内移除
executeRemoveFunctionFromGroup 执行将合约函数与组的关联取消
以下为通用的查询接口
containsAccount
containsFunction
getGroup
canCallFunction
以下为管理员模式下的治理接口:
transferAdminAuth
isAdmin
以下是治理委员会模式下的治理接口:
requestSetThreshold
requestResetGovernors
executeSetThreshold
executeResetGovernors
requestAddGovernor
deleteAddGovernorReq
approveAddGovernorReq
getGovernorsToAdd
executeAddGovernorReq
requestRemoveGovernor
deleteRemoveGovernorReq
approveRemoveGovernorReq
getGovernorsToRemove
executeRemoveGovernorReq
合约部署¶
说明:部署权限治理合约AuthManager。
参数说明:
uint mode:1-管理员模式,2-治理委员会模式。如果选择管理员模式,则当前账户为管理员。
address[] accounts: 初始的治理委员会列表。仅mode为2时有效,如果mode为1,则本参数传空数组即可。
uint16[] weights: 初始治理委员会权重。仅mode为2时有效,如果mode为1,则本参数传空数组即可。
uint16 threshold: 投票有效的最小权重。仅mode为2时有效,如果mode为1,则本参数传0即可。
示例1:部署管理员模式下的权限治理合约。后面三个参数如本例一样传空即可。
deploy AuthManager 1 [] [] 0
示例2:部署委员会模式的权限治理合约,并且采用多签投票方式。比如委员会包含三个地址,分别为”0x1”,”0x2”,”0x3”,要求至少3票投票才能通过,则部署方式为:
deploy AuthManager 2 ["0x1", "0x2", "0x3"] [1,1,1] 3
此处[1,1,1]表示每个委员权重为1。
示例3:部署委员会方式的权限治理合约,并且采用基于阈值投票方式。比如委员会包含三个地址,分别为”0x1”,”0x2”,”0x3”,权重分别为1,2,3。要求总权重为4,投票才算有效。则部署方式为:
deploy AuthManager 2 ["0x1","0x2", "0x3"] [1,2,3] 4
createGroup¶
说明:创建一个组。
调用要求:当前调用者为管理员。该组必须存在。
参数说明:
string group: 组名
uint8 mode:组是黑名单还是白名单。1-白名单,2-黑名单
addAccountToGroup¶
说明:将账户地址拉入到组中。
调用要求:要求当前调用者为管理员。要求组已被创建,且账户尚未在这个组中。
参数说明:
address account: 账户地址
string group: 组名
addFunctionToGroup¶
说明:将某合约的某函数关联到组中。调用后,若当前组为白名单组,则仅有组内账户可以访问该函数;若当前组为黑名单组,则仅有组内账户不允许访问该函数。
调用要求:当前调用者为管理员。要求组已被创建,且函数未被关联到任何组中(无论是本组,还是其他组)
参数说明:
address contractAddr: 业务合约地址
string func: 业务合约中待关联函数的签名字符串,如”add(uint256,uint256)”,”hello()”等。
string group: 组名
removeAccountFromGroup¶
说明:将账户地址从组中移除。
调用要求:当前调用者为管理员。要求组已被创建,且账户在这个组中。
参数说明:
address account: 账户地址
string group: 组名
removeFunctionFromGroup¶
说明:取消某合约某函数与组的关联。
调用要求:当前调用者为管理员。要求组已被创建,且函数已被关联到组。
参数说明:
address contractAddr: 业务合约地址
string func: 业务合约中待关联函数的签名字符串,如”add(uint256,uint256)”,”hello()”等。
string group: 组名
requestCreateGroup¶
说明:请求创建一个组。只能同时存在一个同类请求。参数会被缓存,直到请求被执行或删除。
调用要求:当前调用者为治理委员会成员;不存在其他未关闭的同类请求。
参数说明:
string group: 组名
uint8 mode:组是黑名单还是白名单。1-白名单,2-黑名单
requestAddAccountToGroup¶
说明:请求将账户添加到组。只能同时存在一个同类请求。参数会被缓存,直到请求被执行或删除。
调用要求:当前调用者为治理委员会成员;不存在其他未关闭的同类请求。
参数说明:
address account: 账户地址
string group: 组名
requestAddFunctionToGroup¶
说明:请求将函数关联到组。只能同时存在一个同类请求。参数会被缓存,直到请求被执行或删除。
调用要求:当前调用者为治理委员会成员;不存在其他未关闭的同类请求。
参数说明:
address contractAddr: 业务合约地址
string func: 业务合约中待关联函数的签名字符串,如”add(uint256,uint256)”,”hello()”等。
string group: 组名
requestRemoveAccountFromGrop¶
说明:请求将账户地址从组中移除。只能同时存在一个同类请求。参数会被缓存,直到请求被执行或删除。
调用要求:当前调用者为治理委员会成员;不存在其他未关闭的同类请求。
参数说明:
address account: 账户地址
string group: 组名
requestRemoveFunctionFromGroup¶
说明:请求取消某合约某函数与组的关联。只能同时存在一个同类请求。参数会被缓存,直到请求被执行或删除。
调用要求:当前调用者为治理委员会成员;不存在其他未关闭的同类请求。
参数说明:
address contractAddr: 业务合约地址
string func: 业务合约中待关联函数的签名字符串,如”add(uint256,uint256)”,”hello()”等。
string group: 组名
getRequestSingle¶
说明:获取某个未关闭的投票信息。
调用要求:投票还未关闭。
参数说明:
uint8 txType:请求类型。见《请求类型列表》一节。
返回值:
uint256:请求id(可以忽略)
address:请求的发起地址
uint256:请求的投票总阈值
address:(可以忽略)
uint256:目前投票的总权重
uint8:请求类型。见《请求类型列表》一节。
uint8:(可以忽略)
containsFunction¶
说明:查询某一合约函数是否已关联到组。
调用要求:无
参数说明:
string group:组名
address contractAddr:业务合约地址
string func:业务合约中待关联函数的签名字符串,如”add(uint256,uint256)”,”hello()”等。
getGroup¶
说明:查询某一个组的信息
调用要求:无
参数说明:
string group:组名
返回值说明:
uint8:组的模式。1-白名单,2-黑名单
uint256:组包含的账户数目
uint256:组关联的函数数目
canCallFunction¶
说明:查询某一账户是否有权限调用某一合约函数
调用要求:无
参数说明:
address contractAddr:业务合约地址
bytes4 sig:业务函数的签名字节,由sha3(函数签名字符串)的前4字节得来。和msg.sig一致。
address caller:调用者
requestSetThreshold¶
说明:请求重设投票权重阈值。只能同时存在一个同类请求。参数会被缓存,直到请求被执行或删除。
调用要求:当前调用者为治理委员会成员;不存在其他未关闭的同类请求。
参数说明:
uint16 newThreshold:新的权重阈值
requestResetGovernors¶
说明:请求重设治理委员会列表与权重。只能同时存在一个同类请求。参数会被缓存,直到请求被执行或删除。
调用要求:当前调用者为治理委员会成员;不存在其他未关闭的同类请求。
参数说明:
address[] governors: 新的治理委员会名单
uint16[] weights: 新的治理委员会对应权重
requestAddGovernor¶
说明:请求向治理委员会中添加一个成员
调用要求:当前调用者为治理委员会成员;待添加账户还未没有过相关添加请求
参数说明:
address account: 待添加成员
deleteAddGovernorReq¶
说明:删除添加治理委员会成员请求
调用要求:当前调用者为治理委员会成员;待添加账户已经生成了添加请求
参数说明:
address account: 待添加成员
requestRemoveGovernor¶
说明:请求从治理委员会中删除一个成员
调用要求:当前调用者为治理委员会成员;待移除账户还未没有过相关移除请求
参数说明:
address account: 待移除成员
deleteRemoveGovernorReq¶
说明:删除删除治理委员会成员请求
调用要求:当前调用者为治理委员会成员;待移除账户已经生成了移除请求
参数说明:
address account: 待移除成员
approveRemoveGovernorReq¶
说明:同意删除治理委员会成员
调用要求:当前调用者为治理委员会成员;待移除账户已经生成了移除请求
参数说明:
address account: 待移除成员
集成Sdk¶
在前面示例中,我们以控制台来操作部署、操作权限合约。除了控制台外,本组件也支持通过java代码来执行这些操作。推荐使用集成SDK的方式来进行权限治理,这样可以在没有控制台的情况下进行权限合约调用。
源码下载¶
先前章节中,已经通过git下载了源码:
git clone git@github.com:WeBankBlockchain/Governance-Authority.git
cd auth-manager
这个源码包含:
权限治理合约
权限治理的java sdk
编译¶
方式一:如果服务器已安装Gradle
gradle build -x test
方式二:如果服务器未安装Gradle,则使用gradlew编译。Linux环境:gradlew。Windows环境:gradlew.bat。 以Linux环境为例:
chmod +x ./gradlew && ./gradlew build -x test
编译过后,得到jar包:dist/auth-manager.jar。
集成¶
在编译好该jar包后,将它引入到一个示例项目中进行调用,以操作权限合约。
新建项目¶
新建一个java项目
引入auth-manager sdk¶
前文中,编译auth-manager项目后得到了dist\auth-manager.jar。可以将auth-manager.jar导入到自己的项目中,例如拷贝到libs目录下,然后进行依赖配置,再对自己的项目进行编译。推荐gradle配置如下,
repositories {
maven { url "http://maven.aliyun.com/nexus/content/groups/public/"}
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
maven { url "https://dl.bintray.com/ethereum/maven/" }
mavenLocal()
mavenCentral()
}
dependencies {
compile ('org.fisco-bcos.java-sdk:java-sdk:2.7.0')
compile fileTree(dir:'libs',include:['*.jar'])
}
链连接配置¶
配置存放在config.toml,参考java sdk配置
使用方式示例¶
接下来设置黑名单,将一个账户拉黑,使得它无法调用hello函数。
package com.webank.authmanager.demo;
import com.webank.authmanager.constant.AuthConstants;
import com.webank.authmanager.contract.AuthManager;
import com.webank.authmanager.factory.AuthManagerFactory;
import com.webank.authmanager.service.AuthByAdminService;
import com.webank.authmanager.utils.HashUtils;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.client.Client;
/**
* @author aaronchu
* @Description
* @data 2021/01/15
*/
public class SDKDemo {
public static void main(String[] args) throws Exception{
BcosSDK bcosSDK = BcosSDK.build("conf/config.toml");
Client client = bcosSDK.getClient(1);
AuthManagerFactory authManagerFactory = new AuthManagerFactory(client);
//Deploy contract
AuthManager authManager = authManagerFactory.createAdmin();
//Build facade
AuthByAdminService authByAdminService = new AuthByAdminService(authManager);
//Create group
String group = "badGroup";
authByAdminService.createGroup(group, AuthConstants.ACL_BLACKLIST_MODE);
String blackAccount = "0x01";
//Relate function
String bizContractAddress = "0x2";
String function = HashUtils.hash("add(uint256,uint256)");//业务函数签名
authByAdminService.addFunctionToGroup(bizContractAddress, function, group);
//Verify
boolean canCall = authByAdminService.canCallFunction(bizContractAddress, function, blackAccount);
System.out.println("Before blocking this account, can the account access the function?:"+canCall);
//Add black account
authByAdminService.addAccountToGroup(blackAccount, group);
//Verify
canCall = authByAdminService.canCallFunction(bizContractAddress, function, blackAccount);
System.out.println("After blocking this account, can the account access the function?:"+canCall);
//Remove black account
authByAdminService.removeAccountFromGroup(blackAccount, group);
//Verify
canCall = authByAdminService.canCallFunction(bizContractAddress, function, blackAccount);
System.out.println("After unblocking, can the account access the function?:"+canCall);
}
}
常见问题¶
jvm崩溃¶
现象:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (sharedRuntime.cpp:834), pid=17781, tid=140031174805248
# fatal error: exception happened outside interpreter, nmethods and vtable stubs at pc 0x00007f5c1d05406f
#
# JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode linux-amd64 compressed oops)
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#
--------------- T H R E A D ---------------
Current thread (0x00007f5bac160800): JavaThread "nioEventLoopGroup-3-12" [_thread_in_Java, id=18017, stack(0x00007f5b8c5e8000,0x00007f5b8c6e9000)]
Stack: [0x00007f5b8c5e8000,0x00007f5b8c6e9000], sp=0x00007f5b8c6e6150, free space=1016k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0xaac99a] VMError::report_and_die()+0x2ba
V [libjvm.so+0x4f2de9] report_fatal(char const*, int, char const*)+0x59
V [libjvm.so+0x9ab5ba] SharedRuntime::continuation_for_implicit_exception(JavaThread*, unsigned char*, SharedRuntime::ImplicitExceptionKind)+0x33a
V [libjvm.so+0x914f1a] JVM_handle_linux_signal+0x48a
V [libjvm.so+0x90b493] signalHandler(int, siginfo*, void*)+0x43
C [libpthread.so.0+0xf100]
J 10415 C2 com.sun.crypto.provider.GCTR.doFinal([BII[BI)I (130 bytes) @ 0x00007f5c1e10c096 [0x00007f5c1e10be40+0x256]
这是jdk的bug,需要将jdk版本升级到jdk8u51版本。
函数签名是什么¶
函数签名是函数的标识符,格式为“函数名(参数类型列表)”。例如,有下面这个solidity函数:
function add(uint256 a, uint256 b) public pure returns(uint256){}
那么它的函数签名为:
add(uint256,uint256)