5. 合约管理

5.1. 简介

截至v2.0.n版本, ChainMaker中有如下系统合约:

// 对链配置获取与修改参数等操作
SystemContract_CHAIN_CONFIG
// 对区块或交易进行查询等操作
SystemContract_CHAIN_QUERY
// 对证书进行管理
SystemContract_CERT_MANAGE
// 治理合约
SystemContract_GOVERNANCE
// 进行多签操作调用合约
SystemContract_MULTI_SIGN
// 隐私计算合约
SystemContract_PRIVATE_COMPUTE
// dpos共识下标准ERC20转账合约
SystemContract_DPOS_ERC20
// 负责dpos共识的权益质押管理、验证人选举
SystemContract_DPOS_STAKE
// 对合约进行管理
SystemContract_CONTRACT_MANAGE
// 跨链事务合约
SystemContract_CROSS_TRANSACTION
// 公钥管理
SystemContract_PUBKEY_MANAGE

用户可以通过以上native合约查询区块,获取链配置等等。

在默认情况下,用户有权限通过sdk调用任何一种系统合约,这在一定程度上并不符合我们的设计原则。 比如 tbft 共识算法配置的链不应该对SystemContract_DPOS_ERC20dpos 算法类型才需要使用的方法使用权限。 本文将详细介绍ChainMaker中对系统合约的管理模块以及配置文件。

5.2. 设计思路

1.通过提供一个native合约权限的黑名单来控制对native合约权限的管理,即在此黑名单上的native合约用户没有权限进行调用。 为了减少用户的操作成本,在不修改链配置文件bc1.yml中相应字段的情况下(具体请看模块组成)默认为用户有权限调用所有 的native合约

2.为了维护和使用该黑名单在contract_manage模块中新增对此名单的增删查以及校验接口(具体请看模块组成)

5.3. 模块组成

native合约控制模块主要由在链的配置bc1.yml文件中新增字段disabled_native_contract以及针对这个列表在contract manage 中新增的增删查以及校验接口所组成

1.在bc1.yml文件中新增的字段示例

// 默认禁用 DPOS_ERC20, DPOS_STAKE两种native合约
// 如此字段列表为空,则默认用户有权限调用所有native合约
disabled_native_contract:
  - DPOS_ERC20
  - DPOS_STAKE

2.在contract manage模块中新增整个上述黑名单的增删查以及校验接口

func registerContractManagerMethods(log protocol.Logger) map[string]common.ContractFunc {
	methodMap := make(map[string]common.ContractFunc, 64)
    ...
	
	// 新增下列四个接口用于维护上述黑名单
	methodMap[syscontract.ContractManageFunction_GRANT_CONTRACT_ACCESS.String()] = runtime.grantContractAccess
	methodMap[syscontract.ContractManageFunction_REVOKE_CONTRACT_ACCESS.String()] = runtime.revokeContractAccess
	methodMap[syscontract.ContractManageFunction_VERIFY_CONTRACT_ACCESS.String()] = runtime.verifyContractAccess
	methodMap[syscontract.ContractQueryFunction_GET_DISABLED_CONTRACT_LIST.String()] = runtime.getDisabledContractList

	return methodMap

}

5.4. 主要流程

用户调用native合约的主要流程:

流程设计



若所调用的native合约为contract_manage模块中所提供的上述四种接口之一:

1.若所调用的native合约为contract_manage模块中所提供的上述四种接口中的增和删(分别对应GRANT_CONTRACT_ACCESS以及 REVOKE_CONTRACT_ACCESS),则对native合约黑名单做出相应的操作(增加,删除),并将改动后的名单在数据库中进行更新。
2.若所调用的native合约为contract_manage模块中所提供的上述四种接口中的查(对应GET_DISABLED_CONTRACT_LIST), 则从数据库中将native合约黑名单返回给用户。
3.若所调用的native合约为contract_manage模块中所提供的上述四种接口中的查(对应GET_DISABLED_CONTRACT_LIST), 则从数据库中将native合约黑名单返回给用户 4.3.若所调用的native合约为contract_manage模块中所提供的上述四种接口中的校验(对应VERIFY_CONTRACT_ACCESS), 则根据native合约黑名单判断用户是否有权限调用目标native合约并返回true或者false

5.5. 使用示例

以下将给出一些在chainMaker-go中通过sdk调用上述接口的示例 CMC示例参考

1.开放对某些native合约的调用权限

// 开放Native合约权限
func testAddNativeContract(t *testing.T, list ...string) string {
	txId := utils.GetRandTxId()
	require.True(t, len(txId) > 0)

	fmt.Println("============test add native contract============")
	val, _ := json.Marshal(list)
	pairs := []*commonPb.KeyValuePair{
		{
			Key:   "native_contract_name",
			Value: val,
		},
	}
	sk, member := native.GetUserSK(1)

	resp, err := native.UpdateSysRequest(sk, member, &native.InvokeContractMsg{TxId: txId, TxType: commonPb.TxType_INVOKE_CONTRACT, ChainId: CHAIN1,
		ContractName: syscontract.SystemContract_CONTRACT_MANAGE.String(), MethodName: syscontract.ContractManageFunction_GRANT_CONTRACT_ACCESS.String(), Pairs: pairs})
	processResults(resp, err)

	assert.Nil(t, err)
	fmt.Printf("\n\n ========end test add native contract======== \n ")
	return txId
}

2.关闭对某些native合约的调用权限

// Revoke Native合约权限
func testRevokeNativeContract(t *testing.T, list ...string) string {
    txId := utils.GetRandTxId()
    require.True(t, len(txId) > 0)

    fmt.Println("============test revoke native contract============")
    val, _ := json.Marshal(list)
    pairs := []*commonPb.KeyValuePair{
        {
            Key:   "native_contract_name",
            Value: val,
        },  
    }
    sk, member := native.GetUserSK(1)

    resp, err := native.UpdateSysRequest(sk, member, &native.InvokeContractMsg{TxId: txId, TxType: commonPb.TxType_INVOKE_CONTRACT, ChainId: CHAIN1,
    ContractName: syscontract.SystemContract_CONTRACT_MANAGE.String(), MethodName: syscontract.ContractManageFunction_REVOKE_CONTRACT_ACCESS.String(), Pairs: pairs})
    processResults(resp, err)

    assert.Nil(t, err)
    fmt.Printf("\n\n ========end test revoke native contract======== \n ")
    return txId
}
  1. 获取native合约控制黑名单

// Native合约list查询
func testGetDisabledNativeContractList(t *testing.T, expectedList []string) {
	fmt.Println("============ test get disabled contract list ===========")

	sk, member := native.GetUserSK(1)
	resp, err := native.QueryRequest(sk, member, &client, &native.InvokeContractMsg{TxType: commonPb.TxType_QUERY_CONTRACT, ChainId: CHAIN1,
		ContractName: syscontract.SystemContract_CONTRACT_MANAGE.String(), MethodName: syscontract.ContractQueryFunction_GET_DISABLED_CONTRACT_LIST.String(), Pairs: nil})
	processResults(resp, err)

	assert.Nil(t, err)
	disabledContractList := parseDisabledContractList(resp.ContractResult.Result)
	require.Equal(t, expectedList, disabledContractList)
	fmt.Printf("\n\n ========finished get disabled contract list======== \n ")
}