美文网首页
长安链源码学习-智能合约 (八)

长安链源码学习-智能合约 (八)

作者: 明神特烦恼 | 来源:发表于2021-06-15 08:28 被阅读0次

    智能合约包括系统合约、业务合约,这节将阐述长安链合约是如何分类的,以及智能合约执行流程。

    1. 合约分类

    系统合约

    1) 系统合约名称:

    var ContractName_value = map[string]int32{
        "SYSTEM_CONTRACT_CHAIN_CONFIG": 0,
        "SYSTEM_CONTRACT_QUERY":        1,
        "SYSTEM_CONTRACT_CERT_MANAGE":  2,
        "SYSTEM_CONTRACT_GOVERNANCE":   3,
        "SYSTEM_CONTRACT_MULTI_SIGN":   4,
        "SYSTEM_CONTRACT_STATE":        5,
    }
    

    2) 系统合约支持的类型:

    commonPb.TxType_QUERY_SYSTEM_CONTRACT,
    commonPb.TxType_INVOKE_SYSTEM_CONTRACT,
    commonPb.TxType_UPDATE_CHAIN_CONFIG:
    
    用户合约

    支持的类型:

    commonPb.TxType_MANAGE_USER_CONTRACT,
    commonPb.TxType_INVOKE_USER_CONTRACT,
    commonPb.TxType_QUERY_USER_CONTRACT:
    

    2. 合约执行流程

    智能合约的执行在提案以及区块验证期间都会调用,用以生成读写集。

    1. txSimContext
         在节点提案过程中,会并发生成读写集。调用函数runVM(tx *commonpb.Transaction, txSimContext protocol.TxSimContext), 变量txSimContext一直贯穿着智能合约执行流程,需要优先分析。
         TxSimContext是一个接口主要实现以下方法。Get方法优先从读写集读取变量,如果不存在从数据库中进行读取。Put方法用于构造读写集map,存储在缓存。runVM函数传入的txSimContext表示本次提案的交易集合中已经生成读写集的集合。
    type TxSimContext interface {
        // Get key from cache
        Get(name string, key []byte) ([]byte, error)
        // Put key into cache
        Put(name string, key []byte, value []byte) error
    }
    
    1. 接下来分析runVM函数。
      1)判断参数tx.Header.TxType是否是上述的交易类型之一
      2)交易参数不得多于50个
      3)交易参数每个Key不得长于64个字节,Value不得长于1M
      4)运行RunContract执行智能合约,将执行结果返回

    PS: runVM即使返回有err 也会直接记录提案中,并且会清空读写集。

    1. 分析RunContract函数。
      1)如果智能合约状态为Revoke,返回执行失败
      2)如果是Native合约(目前系统合约注册为Native合约),则执行调用合约框架的Invoke方法,Invoke方法将找到调用方法并执行(这里用的并不是反射,而是在合约中直接注册方法),RunContract执行结束。例如:
    func (c *BlockContact) getMethod(methodName string) ContractFunc {
        return c.methods[methodName]
    }
    
    func registerBlockContactMethods(log *logger.CMLogger) map[string]ContractFunc {
        queryMethodMap := make(map[string]ContractFunc, 64)
        blockRuntime := &BlockRuntime{log: log}
        queryMethodMap[commonPb.QueryFunction_GET_BLOCK_BY_HEIGHT.String()] = blockRuntime.GetBlockByHeight
        queryMethodMap[commonPb.QueryFunction_GET_BLOCK_WITH_TXRWSETS_BY_HEIGHT.String()] = blockRuntime.GetBlockWithTxRWSetsByHeight
        queryMethodMap[commonPb.QueryFunction_GET_BLOCK_BY_HASH.String()] = blockRuntime.GetBlockByHash
        queryMethodMap[commonPb.QueryFunction_GET_BLOCK_WITH_TXRWSETS_BY_HASH.String()] = blockRuntime.GetBlockWithTxRWSetsByHash
        queryMethodMap[commonPb.QueryFunction_GET_BLOCK_BY_TX_ID.String()] = blockRuntime.GetBlockByTxId
        queryMethodMap[commonPb.QueryFunction_GET_TX_BY_TX_ID.String()] = blockRuntime.GetTxByTxId
        queryMethodMap[commonPb.QueryFunction_GET_LAST_CONFIG_BLOCK.String()] = blockRuntime.GetLastConfigBlock
        queryMethodMap[commonPb.QueryFunction_GET_LAST_BLOCK.String()] = blockRuntime.GetLastBlock
        queryMethodMap[commonPb.QueryFunction_GET_CHAIN_INFO.String()] = blockRuntime.GetChainInfo
        queryMethodMap[commonPb.QueryFunction_GET_NODE_CHAIN_LIST.String()] = blockRuntime.GetNodeChainList
        return queryMethodMap
    }
    

    3)如果是用户合约,则判断该合约是否已经冻结,如果冻结则返回。判断合约版本是否存在,不存在则返回。获取合约类型(native、wasmer、wxvm、gasm、evm等)。逻辑在runUserContract函数。
    4)如果执行的方法是init合约、update合约,则存储合约字节码、合约版本、合约类型等字段。如果是冻结、解冻合约、Revoke合约,则执行设置合约状态,并返回。逻辑在runUserContract函数。
    5)如果不是第四步的方法,则调用invokeUserContractByRuntime执行合约方法。

    1. 分析invokeUserContractByRuntime函数
      1)在执行智能合约方法前,先植入系统参数parameters:包括交易发送者、合约创建者、txid、区块高度,此处可以进行扩展,填补参数。
    if senderMember, err := m.AccessControl.NewMemberFromProto(sender); err != nil {
            contractResult.Message = fmt.Sprintf("failed to unmarshal sender %q", runtimeType)
            return contractResult, commonPb.TxStatusCode_UNMARSHAL_SENDER_FAILED
        } else {
            parameters[protocol.ContractSenderOrgIdParam] = senderMember.GetOrgId()
            parameters[protocol.ContractSenderRoleParam] = string(senderMember.GetRole()[0])
            parameters[protocol.ContractSenderPkParam] = hex.EncodeToString(senderMember.GetSKI())
        }
    
        // Get three items in the certificate: orgid PK role
        if creatorMember, err := m.AccessControl.NewMemberFromProto(creator); err != nil {
            contractResult.Message = fmt.Sprintf("failed to unmarshal creator %q", creator)
            return contractResult, commonPb.TxStatusCode_UNMARSHAL_CREATOR_FAILED
        } else {
            parameters[protocol.ContractCreatorOrgIdParam] = creator.OrgId
            parameters[protocol.ContractCreatorRoleParam] = string(creatorMember.GetRole()[0])
            parameters[protocol.ContractCreatorPkParam] = hex.EncodeToString(creatorMember.GetSKI())
        }
    
        parameters[protocol.ContractTxIdParam] = txId
        parameters[protocol.ContractBlockHeightParam] = strconv.FormatInt(txContext.GetBlockHeight(), 10)
    

    2)调用虚拟机的runtimeInstance.Invoke方法,不同的虚拟机执行引擎都将要实现Invoke方法,从Invoke方法为分界线,Invoke方法后都属于虚拟机执行引擎的范畴,后面章节将以gasm为例,分析虚拟机执行原理。

    结尾思考:

    用户交易类型中包含TxType_MANAGE_USER_CONTRACT类型,该类型包括合约冻结、解冻、revoke等,不在智能合约中执行,属于合约管理动作。目前该管理动作放在invokeUserContractByRuntime流程中散落执行,是否可以抽象成一个系统合约,合约的管理动作由这个系统合约进行处理。

    相关文章

      网友评论

          本文标题:长安链源码学习-智能合约 (八)

          本文链接:https://www.haomeiwen.com/subject/gyvzeltx.html