Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-2746: 规则引擎标准

Authors Aaron Kendall (@jaerith), Juan Blanco (@juanfranblanco)
Created 2020-06-20
Discussion Link https://ethereum-magicians.org/t/eip-2746-rules-engine-interface/4435

简述

一个使用智能合约作为规则引擎的接口。单个已部署的合约可以注册一个数据域,创建一组对该域执行操作的规则,然后作为一个原子事务调用该集合。

概要

本标准提出一个接口,该接口允许创建分层规则集(即 RuleTrees),可以调用这些规则集来评估和操作注册的数据域。在本草案撰写之时,所有在区块链上插入额外功能的意图都需要编码和创建一个新部署的合约。但是,本标准将允许用户仅部署一次合约,然后该合约将允许他们在该合约中创建(和调用)命令管道。

动机

在本草案撰写之时,以太坊的所有开发都需要编写构成智能合约的代码,然后将这些合约部署到以太坊。为了创建一个合适的合约,在设计和实施代码时必须考虑许多因素,尤其是在效率(即 gas 成本)和安全性方面。即使是最简单的合约,在部署前后也需要一定程度的警惕和检查。这些要求适用于所有情况,即使是检查值和/或更改值的简单情况。

这些技术挑战可能会对许多希望围绕以太坊创建软件的人构成障碍。技术含量较低的公司和用户可能也希望在链上配置和部署简单的功能,而无需了解相关的语言或必要的细节。通过将数据域和预定义的操作(即规则类型)与此接口一起实现,这种规则引擎合约的已部署实例可以为无代码或低代码客户端提供高效且安全的功能,从而允许更多具有各种技术能力的用户与以太坊生态系统进行交互。

规范

为了澄清术语,属性是数据域中注册的数据点,表示存在于规则引擎合约中或别处的数据。规则是在预定义数据域中的单个数据点(即属性)上发生的预定义操作。例如,规则可以检查属性“TokenAmt”的值是否小于 10 的 RHL(即右侧值)。规则集是规则的集合,它们的集合调用会创建一个布尔结果,该结果决定了规则集之间执行的导航流程。规则树是在层次结构中组织的规则集集合,其中规则集可以包含其他规则集。

pragma solidity ^0.6.0;

/**
    @title ERC-2746 Rules Engine Standard
    @dev See https://eips.ethereum.org/EIPS/eip-2746
 */
 interface ERCRulesEngine {

    /**
        @dev Should emit when a RuleTree is invoked.
        @dev 应该在 RuleTree 被调用时发出。
        The `ruler` is the ID and owner of the RuleTree being invoked.  It is also likely msg.sender.
        `ruler`是被调用的 RuleTree 的 ID 和所有者。它也可能是 msg.sender。
    */
    event CallRuleTree(
        address indexed ruler
    );

    /**
        @dev Should emit when a RuleSet is invoked.
        @dev 应该在 RuleSet 被调用时发出。
        The `ruler` is the ID and owner of the RuleTree in which the RuleSet is stored.  It is also likely msg.sender.
        `ruler`是存储 RuleSet 的 RuleTree 的 ID 和所有者。它也可能是 msg.sender。
        The 'ruleSetId' is the ID of the RuleSet being invoked.
        'ruleSetId'是被调用的 RuleSet 的 ID。
    */
    event CallRuleSet(
        address indexed ruler,
        bytes32 indexed tmpRuleSetId
    );

    /**
        @dev Should emit when a Rule is invoked.
        @dev 应该在 Rule 被调用时发出。
        The `ruler` is the ID and owner of the RuleTree in which the RuleSet is stored.  It is also likely msg.sender.
        `ruler`是存储 RuleSet 的 RuleTree 的 ID 和所有者。它也可能是 msg.sender。
        The 'ruleSetId' is the ID of the RuleSet being invoked.
        'ruleSetId'是被调用的 RuleSet 的 ID。
        The 'ruleId' is the ID of the Rule being invoked.
        'ruleId'是被调用的 Rule 的 ID。
        The 'ruleType' is the type of the rule being invoked.        
        'ruleType'是被调用的规则的类型。
    */
    event CallRule(
        address indexed ruler,
        bytes32 indexed ruleSetId,
        bytes32 indexed ruleId,
        uint ruleType
    );

    /**
        @dev Should emit when a RuleSet fails.
        @dev 应该在 RuleSet 失败时发出。
        The `ruler` is the ID and owner of the RuleTree in which the RuleSet is stored.  It is also likely msg.sender.
        `ruler`是存储 RuleSet 的 RuleTree 的 ID 和所有者。它也可能是 msg.sender。
        The 'ruleSetId' is the ID of the RuleSet being invoked.
        'ruleSetId'是被调用的 RuleSet 的 ID。
        The 'severeFailure' is the indicator of whether or not the RuleSet is a leaf with a 'severe' error flag.
        'severeFailure'是指示 RuleSet 是否是具有“严重”错误标志的叶子的指标。
    */
    event RuleSetError (
        address indexed ruler,
        bytes32 indexed ruleSetId,
        bool severeFailure
    );	

    /**
        @notice Adds a new Attribute to the data domain.
        @dev 向数据域添加新属性。
        @dev Caller should be the deployer/owner of the rules engine contract.  An Attribute value can be an optional alternative if it's not a string or numeric.
        @dev 调用者应为规则引擎合约的部署者/所有者。如果属性值不是字符串或数字,则它可以是可选的替代值。
        @param _attrName    Name/ID of the Attribute
        @param _attrName    属性的名称/ID
        @param _maxLen      Maximum length of the Attribute (if it is a string)
        @param _maxLen      属性的最大长度(如果它是字符串)
        @param _maxNumVal   Maximum numeric value of the Attribute (if it is numeric)
        @param _maxNumVal   属性的最大数值(如果它是数字)
        @param _defaultVal  The default value for the Attribute (if one is not found from the source)
        @param _defaultVal  属性的默认值(如果从源中找不到)
        @param _isString    Indicator of whether or not the Attribute is a string
        @param _isString    指示属性是否为字符串
        @param _isNumeric   Indicator of whether or not the Attribute is numeric
        @param _isNumeric   指示属性是否为数字
    */    
    function addAttribute(bytes32 _attrName, uint _maxLen, uint _maxNumVal, string calldata _defaultVal, bool _isString, bool _isNumeric) external;

    /**
        @notice Adds a new RuleTree.
        @notice 添加新的 RuleTree。
        @param _owner          Owner/ID of the RuleTree
        @param _owner          RuleTree 的所有者/ID
        @param _ruleTreeName   Name of the RuleTree
        @param _ruleTreeName   RuleTree 的名称
        @param _desc           Verbose description of the RuleTree's purpose
        @param _desc           RuleTree 用途的详细描述
    */
    function addRuleTree(address _owner, bytes32 _ruleTreeName, string calldata _desc) external;

    /**
        @notice Adds a new RuleSet onto the hierarchy of a RuleTree.
        @notice 在 RuleTree 的层次结构上添加新的 RuleSet。
        @dev RuleSets can have child RuleSets, but they will only be called if the parent's Rules execute to create boolean 'true'.
        @dev RuleSet 可以有子 RuleSet,但只有在父级的规则执行结果为布尔值“true”时才会调用它们。
        @param _owner           Owner/ID of the RuleTree
        @param _owner           RuleTree 的所有者/ID
        @param _ruleSetName     ID/Name of the RuleSet
        @param _ruleSetName     RuleSet 的 ID/名称
        @param _desc            Verbose description of the RuleSet
        @param _desc            RuleSet 的详细描述
        @param _parentRSName    ID/Name of the parent RuleSet, to which this will be added as a child
        @param _parentRSName    父 RuleSet 的 ID/名称,将作为子 RuleSet 添加到其中
        @param _severalFailFlag Indicator of whether or not the RuleSet's execution (as failure) will result in a failure of the RuleTree.  (This flag only applies to leaves in the RuleTree.)
        @param _severalFailFlag 指示 RuleSet 的执行(作为失败)是否会导致 RuleTree 失败。 (此标志仅适用于 RuleTree 中的叶子。)
        @param _useAndOp        Indicator of whether or not the rules in the RuleSet will execute with 'AND' between them.  (Otherwise, it will be 'OR'.)
        @param _useAndOp        指示 RuleSet 中的规则是否将使用“AND”运算符执行。 (否则,它将是“OR”。)
        @param _failQuickFlag   Indicator of whether or not the RuleSet's execution (as failure) should immediately stop the RuleTree.
        @param _failQuickFlag   指示 RuleSet 的执行(作为失败)是否应立即停止 RuleTree。
    */    
    function addRuleSet(address _owner, bytes32 _ruleSetName, string calldata _desc, bytes32 _parentRSName, bool _severalFailFlag, bool _useAndOp, bool _failQuickFlag) external;

    /**
        @notice Adds a new Rule into a RuleSet.
        @notice 将新规则添加到规则集。
        @dev Rule types can be implemented as any type of action (greater than, less than, etc.)
        @dev 规则类型可以实现为任何类型的操作(大于、小于等)
        @param _owner           Owner/ID of the RuleTree
        @param _owner           RuleTree 的所有者/ID
        @param _ruleSetName     ID/Name of the RuleSet to which the Rule will be added
        @param _ruleSetName     将添加规则的规则集的 ID/名称
        @param _ruleName        ID/Name of the Rule being added
        @param _ruleName        要添加的规则的 ID/名称
        @param _attrName        ID/Name of the Attribute upon which the Rule is invoked
        @param _attrName        调用规则的属性的 ID/名称
        @param _ruleType        ID of the type of Rule
        @param _ruleType        规则类型的 ID
        @param _rightHandValue  The registered value to be used by the Rule when performing its action upon the Attribute
        @param _rightHandValue  规则在对属性执行操作时使用的已注册值
        @param _notFlag         Indicator of whether or not the NOT operator should be performed on this Rule.
        @param _notFlag         指示是否应对此规则执行 NOT 运算符。
    */    
    function addRule(address _owner, bytes32 _ruleSetName, bytes32 _ruleName, bytes32 _attrName, uint _ruleType, string calldata _rightHandValue, bool _notFlag) external;

    /**
        @notice Executes a RuleTree.
        @notice 执行规则树。
        @param _owner           Owner/ID of the RuleTree
        @param _owner           RuleTree 的所有者/ID
    */
    function executeRuleTree(address _owner) external returns (bool);
    
    /**
        @notice Retrieves the properties of a Rule.
        @notice 检索规则的属性。
        @param _owner           Owner/ID of the RuleTree
        @param _owner           RuleTree 的所有者/ID
        @param _ruleSetName     ID/Name of the RuleSet where the Rule resides
        @param _ruleSetName     规则所在的 RuleSet 的 ID/名称
        @param _ruleIdx         Index of the rule in the RuleSet's listing 
        @param _ruleIdx         规则在 RuleSet 列表中的索引
        @return bytes32         ID/Name of Rule
        @return bytes32         规则的 ID/名称
        @return uint            Type of Rule
        @return uint            规则类型
        @return bytes32         Target Attribute of Rule
        @return bytes32         规则的目标属性
        @return string          Value mentioned in Rule
        @return string          规则中提到的值
        @return bool            Flag for NOT operator in Rule
        @return bool            规则中 NOT 运算符的标志
        @return bytes32[]       Values that should be provided in delegated call (if Rule is custom operator)
        @return bytes32[]       应在委托调用中提供的值(如果规则是自定义运算符)
    */
    function getRuleProps(address _owner, bytes32 _ruleSetName, uint _ruleIdx) external returns (bytes32, uint, bytes32, string memory, bool, bytes32[] memory);

    /**
        @notice Retrieves the properties of a RuleSet
        @notice 检索 RuleSet 的属性
        @param _owner        Owner/ID of the RuleTree
        @param _owner        RuleTree 的所有者/ID
        @param _ruleSetName  ID/Name of the RuleSet
        @param _ruleSetName  RuleSet 的 ID/名称
        @return string       Verbose description of the RuleSet
        @return string       RuleSet 的详细描述
        @return bool         Flag that indicates whether this RuleSet's failure (if a leaf) will cause the RuleTree to fail
        @return bool         指示此 RuleSet 的失败(如果是叶子)是否会导致 RuleTree 失败的标志
        @return bool         Flag that indicates whether this RuleSet uses the AND operator when executing rules collectively
        @return bool         指示此 RuleSet 在集体执行规则时是否使用 AND 运算符的标志
        @return uint         Indicates the number of rules hosted by this RuleSet
        @return uint         指示此 RuleSet 托管的规则数
        @return uint         The quantity of child RuleSets
        @return uint         子规则集的数量
        @return bytes32[]    The list of RuleSets that are children of this RuleSet
        @return bytes32[]    作为这个 RuleSet 的子 RuleSet 列表
    */
    function getRuleSetProps(address _owner, bytes32 _ruleSetName) external returns (string memory, bool, bool, uint, uint, bytes32[] memory);

    /**
        @notice Retrieves the properties of a RuleSet
        @notice 检索 RuleSet 的属性
        @param _owner        Owner/ID of the RuleTree
        @param _owner        RuleTree 的所有者/ID
        @return bytes32      Name of the RuleTree
        @return bytes32      RuleTree 的名字
        @return string       Verbose description of the RuleTree
        @return string       RuleTree 的详细描述
        @return bytes32      ID/Name of the RuleSet that serves as the root node for the RuleTree
        @return bytes32      作为 RuleTree 根节点的 RuleSet 的 ID/名称
    */
    function getRuleTreeProps(address _owner) external returns (bytes32, string memory, bytes32);
    
    /**
        @notice Removes a RuleTree.
        @notice 移除一个 RuleTree。
        @param _owner           Owner/ID of the RuleTree
        @param _owner           RuleTree 的所有者/ID
    */
    function removeRuleTree(address _owner) external returns (bool);    
}

考虑因素

可以为接口函数提出一个论点,该函数允许 RuleTree 的所有者包括其他用户作为 RuleTree 的执行者。

还可以为接口函数提出一个论点,该函数允许管理员配置属性的来源点,例如属性的值是来自数据结构(规则引擎合约内部),还是来自调用合约方法(如 Diamond Standard 的实现)。

还可以为接口函数提出另一论点,该函数允许管理员通过允许将其他合约的方法添加为规则操作来扩展规则引擎提供的功能目录。

此外,可以为计算和报告调用 RuleTree 的潜在成本范围的函数提出一个论点。与合约方法的正常执行不同,调用 RuleTree 的以太坊交易成本更具动态性,具体取决于其深度/广度以及调用期间的导航流程。由于 RuleTree 的一般成本在调用时未知,因此这些函数可以报告交易的最小 gas 量(即,不调用 RuleTree 中的任何规则)和交易的最大 gas 量(即,调用 RuleTree 中的所有规则)。

示例

一家公司希望部署一个具有数据点和功能的合约,这些数据点和功能是预定义的和/或受管理员控制的,并且它旨在构建一个无代码客户端,该客户端将允许技术水平较低的用户在规则引擎合约中定义操作。在本示例中,该公司希望其用户之一以专有标记语言编写规则,以便确定增值税的计算。为了透明起见,这些规则 发布到 IPFS 上,以便审计师和可能的政府官员可以访问它们。然后,无代码客户端将知道如何从标记中解析规则并与规则引擎合约进行通信,从而建立稍后将由公司的用户或链下程序调用的 RuleTree。

为了计算增值税的值,这些提供的规则调用可以执行计算的简单数学运算。但是,规则引擎合约的实现可能具有规则调用的其他功能,这些功能可以执行更复杂的逻辑或调用其他合约的方法。

理由

属性

数据点被抽象化,以便让实现提供检索/填充数据的机制。数据可以由内部数据结构、另一个合约的方法或任意数量的其他选项保存。

事件

指定的事件将在执行后帮助 RuleTree 的调用者,以便他们可以确定 RuleTree 中 RuleSet 执行的导航流程,并且以便他们可以了解哪些 RuleSet 失败。

右侧值

在函数 addRule() 中,右侧值的数据类型为“string”,因为规则的操作取决于其类型,这意味着必须以通用形式提供该值。在执行数值运算的规则的情况下,提供的值可以在存储在规则中时转换为数字。

实现

Wonka 实现支持此提议的接口,并且还实现了上面提到的所有其他考虑因素。

安全注意事项

合约的部署者应该是所有者和管理员,允许添加属性和 RuleTree。由于 RuleTree 由特定的 EOA(或合约地址)拥有,因此只有能够执行 RuleTree 的帐户应该是其所有者或合约的所有者/管理员。如果定义属性以作为其他合约中的数据存在,则实现必须考虑到 RuleTree 所有者必须具有访问这些合约中数据的安全性的可能性。

参考

标准

版权

版权和相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Aaron Kendall (@jaerith), Juan Blanco (@juanfranblanco), "ERC-2746: 规则引擎标准 [DRAFT]," Ethereum Improvement Proposals, no. 2746, June 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2746.