复杂系统中的链上参数管理

在一个产品的协议设计中,往往存在一些全局的参数,对整个系统至关重要,比如一个借贷应用的清算率、抵押率。这些参数呢,会被系统中的各种交易频繁的引用,从而进行合约的验证,比如清算率会在清算用户资产的交易中被引用,参与计算以验证特定用户是否触发了清算条件。同时,这些参数的更新频率往往不高,更新的权限也往往和一些治理机制耦合在一起,比如抵押率可能通过多签的办法或者其他更为复杂的治理机制来决定是否更新。

对于一个简单系统而言,可以一个参数存在一个 cell 里,同时该参数 cell 可以采用 token ID script 来保证该参数 cell 的唯一性。构造交易的时候直接在 cell_deps 里引用该参数 cell 即可:

 // 简单系统下的参数管理
 configuration cell(conf cell)
        capacity
        data
            value: parameter value
        type
            code_hash: token ID script
            args: input.cell[0].Outpoint
        lock
            user defined

但是对于复杂系统而言,例如一个借贷应用,每一个代币对,都有着各自的清算率,抵押率,应用会使用到大量的参数 cell,再采用上面的这种办法,在构造交易,引用参数的时候,就面临着比较大的复杂度。我们可以提出一种方法,通过引入一个索引 cell (index cell),为每个参数 cell 赋予一个索引编号,同时使得这些参数 cell 的 type 保持相同,这将大大的方便索引与引用, 运营方也可以更好的管理和维护这些参数 cell (configuration cell)。

索引 cell (index cell) 和 参数 cell(configuration cell)的创建和更新会涉及到三种类型的交易。

    1. 首次创建编号为 0001 的索引 cell 和参数 cell
    1. 后续不断累加编号,创建更多的参数 cell
    1. 更新参数 cell

下面我们将通个这三种交易来详解这两种 cell 的结构和这其中的 script 的验证条件。

1. 首次创建编号为 0001 的 index cell 和 configuration cell

// 一个参数存一个 cell
Input
    capacity
    
Output
    index cell
        capacity
        data
            index: 0001 // uint32
        type
            code_hash: index_type_script
            args:input.cell[0].Outpoint
        lock
            user defined
        
    configuration cell(conf cell)
        capacity
        data
            index: 0001 // uint32
            value: parameter value // uint128
        type
            code_hash: conf_type_script
            args: index_type_hash20
        lock
            user defined
    
    

Index cell

  • data:
    • 前 4 个字节:存储索引编号
    • 4 个字节之后存储其他数据不做限制
  • type:
    • rule 1 - 如果 count(input.type.code_hash = index_type_script)=0 && count(input.type.code_hash = conf_type_script)=0 可以判断此交易为创建编号为 0001 的 conf_cell,进行下面的验证:
      • count(output.type.code_hash = index_type_script) = 1
      • count(output.type.code_hash = conf_type_script) = 1
      • index_cell.data[0…3] = 0001
      • conf_cell.data[0…3] = index_cell.data[0…3]
      • index_cell.type.args = input.cell[0].Outpoint
  • lock:运营方自定义

Configuration cell

  • data:
    • 前 4 个字节:存储索引编号
    • 5-20 字节:存储参数的数值
    • 后面存储其他数据不做限制
  • type:
    • rule1: 如果 count(input.type.code_hash = index_type_script)=0 && count(input.type.code_hash = conf_type_script)=0 可以判断此交易为创建编号为 0001 的 参数cell,进行下面的验证:
      • conf_cell.type.args 为 index_type_hash20,output 中有该 index_type
  • lock: 运营方自定义

Note:index_cell 和 conf_cell 的 lock 不一定要相同,可以根据治理的需要灵活设定。比如说 index_cell 可以设置成运营方自己的多签 lock,conf_cell 可以设置成具有某种特定治理机制的 lock,以方便社区后续对参数数值的更新。

2. 新增编号为 0002 的 参数 cell

// 一个参数存一个 cell
Input
    index cell
        capacity
        data
            index: 0001
        type
            code_hash: index_type_script
            args:outpointA
        lock
            user defined
    
Output
    index cell
        capacity
        data
            index: 0002
        type
            code_hash: index_type_script
            args:outpointA
        lock
            user defined
        
    configuration cell(conf cell)
        capacity
        data
            index: 0002
            value: parameter value 
        type
            code_hash: conf_type_script
            args: index_type_hash20
        lock
            user defined

index cell

  • type:
    • rule 2: 如果 count(input.type.code_hash = index_type_script)=1 && count(input.type.code_hash = conf_type_script)=0 可以判断此交易为新增 conf_cell 的交易,进行下面的验证:
      • count(output.type.code_hash = index_type_script) = 1
      • count(output.type.code_hash = conf_type_script) >= 1
      • index_cell.output.data[0…3] = index_cell.input.data[0…3] + 1
      • conf_cell.data[0…3] = index_cell.data[0…3]
      • index_cell.output.type.args = index_cell.input.type.args

configuration cell

  • type:
    • rule2: 如果 count(input.type.code_hash = index_type_script)=1 && count(input.type.code_hash = conf_type_script)=0 可以判断此交易为新增 conf_cell 的交易,进行下面的验证:
      • conf_cell.type.args 为 index_type_hash20,output 中有该 index_type

3. 更新编号为 0002 的参数 cell

// 一个参数存一个 cell
Input
    configuration cell(conf cell)
        capacity
        data
            index: 0002
            value: valueA
        type
            code_hash: conf_type_script
            args: index_type_hash20
        lock
            user defined
    
Output        
    configuration cell(conf cell)
        capacity
        data
            index: 0002
            value: valueB 
        type
            code_hash: conf_type_script
            args: index_type_hash20
        lock
            user defined

Configuration cell

  • type:
    • rule3: 如果 count(input.type.code_hash = index_type_script)=0 && count(input.type.code_hash = conf_type_script)=1 可以判断此交易为更新 conf_cell 的交易,进行下面的验证:
      • count(output.type.code_hash = conf_type_script)=1
      • conf_cell.output.data[0…3] = conf_cell.input.data[0…3]
      • conf_cell.output.type = conf_cell.input.type
1 Like

在这一部分中,输入的 index cell 的 data 中的 index:0001 是直接被删除,然后替换为了 index:0002 吗?按照我的理解,感觉应该是新增 index:0002。

Configuration Cell 中的存储索引编号在 Index Cell 中已经有了,为什么需要再存一遍呢?

在 Index Cell 的存储索引编号中,完全可以规定每个 value 的长度。这样,Configuration Cell 只需要按照 Index Cell 的规则去进行读取,几位到几位是 index 0001,几位到几位是 index 0002。

编号存在Configuration cell的data中,依赖的script只需要读取这个cell就可以完成判断。
如果不存编号,作恶者有可能通过依赖其他的cell来伪造交易。
如果script要检验这种情况就很麻烦

index:0001应该是使用了,就不存在了。否则可能重复创建index:0002的情况
所以index应该都是更新操作,live cell永远只有一个。

如果是这样的设计话,没太理解 index cell 存在意义。

在大型项目中,会有非常多的配置。配置拥有统一的type,统一的创建者,比较可信,同时创建交易时,也比较容易。多个script读取、验证配置也方便。
index cell只是为了保证对应序号的config不会随意创建。
配置的type_hash和序号都可以写在代码中,增加可信度。这样也可以降低依赖的复杂度。