diff --git a/bsip-0040.md b/bsip-0040.md index 1db253d..43562a9 100644 --- a/bsip-0040.md +++ b/bsip-0040.md @@ -39,7 +39,7 @@ The above list of named keys is nothing that is known to the backend as the back # Rational -Custom active permission is a list of `custom active authorities`. A `custom active authority` contains an `operation_id`, an `authority` (just like with active permission) and `asserts` than can be used to restrict arguments and is only valid a certain time period (`valid_from` and `valid_to`). When handling incoming signed transactions, the backend checks for each operation if there is a `custom active authority` for any of its required accounts. Check for every required account of the transaction if all its belonging operations have at least one positively matched `custom active authority` (match means its `authority` is granted through present signatures, same `operationid`, now is within `valid_to` and `valid_from` and all `asserts` pass), and if so grant the active authority of the corresponding account. +Custom active permission is a list of `custom active authorities`. A `custom active authority` contains an `operation_id`, an `authority` (just like with active permission) and `restrictions` than can be used to restrict arguments and is only valid a certain time period (`valid_from` and `valid_to`). When handling incoming signed transactions, the backend checks for each operation if there is a `custom active authority` for any of its required accounts. Check for every required account of the transaction if all its belonging operations have at least one positively matched `custom active authority` (match means its `authority` is granted through present signatures, same `operation_id`, now is within `valid_to` and `valid_from` and none of the `restrictions` is violated), and if so grant the active authority of the corresponding account. # Specification @@ -47,43 +47,43 @@ All descriptions in this section are on a pseudo/prosa level and no recommendati ### Custom active permission and custom active authority -A `custom_active_permission` looks like follows (in JSON-like/pseudo for clarification): +A `custom active permission` contains a list of `custom active authorities` and looks like follows (in JSON-like/pseudo for clarification): ``` custom_active_permission = { account_id, // account that is assigned to this permission - authorities = list of custom_active_authority items + authorities = list of custom_active_authority objects } custom_active_authority = { valid_from, // timestamp when this is active, defaults to now valid_to, // timestamp when this is invalid, defaults to 1 year - operationid, // operationid of the target operation, + operation_id, // operation id of the target operation, authority, // same as for the existing authortities (multisig with weighted accounts or keys), - asserts // see below + restrictions // see below } ``` Note: This assumes `custom_active_permission` is stored in a separate index. Actual implementation details left to the implementer, as long as every `custom_active_permission` can be assigned to exactly one account. -A `custom active permission` contains a list of `custom active authority`. `Custom active authority` can match an operation of an incoming, signed transaction. The wording *matching* refers to: -- `operationid` is equal to the id of the incoming operation -- assigned account of parent `custom active permission` is in the required accounts of the operation +A `custom active authority` is matched against operations of an incoming, signed transaction. The wording *matching* refers to: +- `operation_id` is equal to the id of the incoming operation +- `account_id` is in the required accounts of the operation - the `authority` of the `custom_active_authority` is given by the signatures of the transaction -- `now` is within `valid_to` and `valid_from` -- all `asserts` pass positively +- timestamp `now` is within `valid_to` and `valid_from` +- all `restrictions` assert positively -### Asserts +### Restrictions -The `asserts` field is a list of restrictions consisting of argument to assert mappings. +The `restrictions` field is a list of restrictions consisting of argument to assert mappings. A dictionary-type object like ``` -assert_object = { +restriction = { function, // argument_identifier argument, // constant value, or pointer to a dynamic value (argument of the operation, or attribute when nested) data, // data specific to the function } ``` -is called a restriction. All asserts within one restriction are evaluated per default with `AND` logic. +is called a restriction, that can assert itself positively (passed) or negatively (violated). All restrictions are evaluated per default with `AND` logic to determine if the whole list has asserted positively. -List of possible asserts are: +List of possible restrictions are: | function | data | state | | ------------- |:-------------:| -----:| @@ -96,15 +96,16 @@ List of possible asserts are: | `logical` | list of restrictions | stateless | Following cases must hold for a restriction: -- if there is no value given (e.g. an optional argument, or nested value not given), the assert passes (no change, no violation) -- if the expected type of the argument does not match the given type (no implicit type conversion), assert fails -- if the assert fails, the restriction fails +- if there is no value given (e.g. an optional argument, or nested value not given), the restrictions is passed (no change, no violation) +- if the expected type of the argument does not match the given type (no implicit type conversion), the restriction is violated +- if the function of the restriction asserts negatively, the restriction is violated Note: - If required a field can be added that stores the assumed type of the argument - If arguments are given by the operation that have no restriction they can have any value -In the following we list possible `assert_objects`. Mentioning `argument value` refers to the value of the argument of the operation specified by `argument_identifier` of a restriction. . All asserts imply: If the `argument` is given, it must pass the `assert`. If the `argument` is not given, assert is implicitly passed. +In the following we list possible `restriction`s. Mentioning `argument value` in the text below refers to the value of +the argument of the operation specified by `argument` of a restriction. #### `any` Stateless assert, all argument types. `Argument value` must be equal to one of values in the data list @@ -126,18 +127,18 @@ The different asserts read as: #### `limit` Statefull assert, only `int` type arguments. When the authority is created, `interval_began` is set to `valid_from` from its custom active authority and `max_cumsum` to `0`. Incoming operations are first tried to match all stateless asserts, and if all passes continue with statefull asserts. If `now > interval_began + interval_in_sec`, then set `max_cumsum = 0` and set `interval_began = now`. -The assert that needs to pass is now `current_cumsum + incoming value <= max_cumsum`. If all asserts are passed, update `current_cumsum = current_cumsum + incoming value` of all involved statefull asserts. +The assert that needs to pass is now `current_cumsum + incoming value <= max_cumsum`. If all `asserts` of this `custom_active_authority` pass, update `current_cumsum = current_cumsum + incoming value`. #### `limit_monthly` Statefull assert, only `int` type arguments. Analogue to `limit`, but `interval_began` is initially set to `month(valid_from)` and set to `month(now)` on update, additionally the time assert is `month(now) >= interval_began + interval_in_months` (include logic for month overflow when year changes). #### `attribute_assert` -Stateless assert, only for dictionary type objects. The `attribute_to_assert` list contains restrictions that all must assert positively. Allows nesting of `attribute_assert`. +Stateless assert, only for dictionary type objects. The data list contains restrictions that all must pass, the reference for the `argument` of the child restriction is nested into the attributes of the parent dictionary type object. Allows nesting of `attribute_assert`. #### `logical` Stateless assert, only for dictionary type objects. The data is a list of restrictions, `argument` defines the logical link -- `OR`: If one of the restrictions in data asserts positively -- `AND`: If ALL restrictions in data assert positively +- `OR`: If one of the restrictions in data passes, this restriction passes +- `AND`: If ALL restrictions in data pass, this restriction passes #### Example: Nested arguments like `options` Assume `asset_update_operation`. All attributes of its `options` must be filled on update call. This assert can not be used to realize a "may only change attribute xzy of `options`". This would require that the logic knows which of the arguments are reflected on-chain and it knows how to query it for every operation that contains `options`. If `options` are to be restricted with this assert, all values that should not change would need be fixated by defining an `any` assert for those attributes, while having e.g. a `lt` assert for the one attribute that is allowed to change. @@ -148,7 +149,7 @@ Assume account A and B and some unrelated key K. Furthermore A has a custom acti custom active authority = { valid_from: 7.7.2018 00:00 valid_to: 8.7.2018 00:00 - operationid: transfer, + operation_id: transfer, authority: { threshold: 1 key_auth: [K, 1] @@ -215,7 +216,7 @@ Normal accounts can only create custom active authoritites with a duration of ma We propose do split the implmentation into two milestones. Each milestone will be voted on as a separate BSIP: 1. Implementation of basic functionaliy to allow custom active permissions and authorities, including `any`, `none` and `lt, le, gt, ge` and `attribute_assert` `asserts`. If deemed necessary by developpers, reduce to only allow one key or one account for every `custom active authority` -2. Evaluation of stateful asserts `limit` and `limit_monthly` in terms of performance. If positively evaluated, implement +2. Implement stateful asserts `limit` and `limit_monthly` and `logical` This approach allows as well to add other asserts at a later stage (with a new BSIP).