From 15a051489460dcac2c97fbad1a5e578f36ddef38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schie=C3=9Fl?= Date: Mon, 30 Jul 2018 08:34:41 +0200 Subject: [PATCH] more details on attribute_assert and where permission is stored --- bsip-0040.md | 53 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/bsip-0040.md b/bsip-0040.md index 7e21838..4d9d459 100644 --- a/bsip-0040.md +++ b/bsip-0040.md @@ -49,7 +49,10 @@ All descriptions in this section are on a pseudo/prosa level and no recommendati A `custom_active_permission` looks like follows (in JSON-like/pseudo for clarification): ``` -custom_active_permission = list of custom_active_authority items +custom_active_permission = { + account_id, // account that is assigned to this permission + authorities = list of custom_active_authority items +} custom_active_authority = { valid_from, // timestamp when this is active, defaults to now valid_to, // timestamp when this is invalid, defaults to 1 year @@ -58,9 +61,7 @@ custom_active_authority = { asserts // see below } ``` -Note: This assumes `custom_active_permission` is stored within `account_object`. Actual implementation details left to the implementer, as long as every `custom_active_permission` can be assigned to exactly one account. - -#### Wording +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 @@ -71,10 +72,12 @@ A `custom active permission` contains a list of `custom active authority`. `Cust ### Asserts -The `asserts` is a list of `assert_objects` that are all together evaluated with `and` logic to evaluate a match +The `asserts` field is a list of restrictions consisting of argument to assert mappings. +An tuple of `(argument_identifier, assert_object[, logical_link])` is called a restriction on an argument. +All asserts within one restriction are evaluated per default with `and` logic, `or` logic can be put by specifying the `logical_link`. The `asserts` field is specified as follows: ``` -asserts = list of (argument_identifier, assert_object) tuples -argument_identifier = // target unknown, can be argument of operation, or in a nested dictionary like struct +asserts = list of (argument_identifier, [list of assert_object], logical_link) tuples +argument_identifier = // target variable, can be argument of operation, or attribute in case of nesting assert_object = { function, // functionid to do the assert data, // stores data specific to the chosen function @@ -90,11 +93,18 @@ List of possible asserts are: | `lt, le, gt, ge` | `comparative` | stateless | | `limit` | [`max_cumsum`, `interval_in_sec`] | [`current_cumsum`, `interval_began`] | | `limit_monthly` | [`max_cumsum`, `interval_in_months`] | [`current_cumsum`, `interval_began`] | -| `attribute_assert` | `attribute_to_assert` = [(`attribute_identifier`, `any assert_objects`)] | stateless | +| `attribute_assert` | `attribute_to_assert` = [list of restrictions] | stateless | -There is no implicit type conversion when attempting to assert, incompatible type means assert failure. If required, a field can be added that stores the assumed type of the argument (if conversion fails, assert fails). If an argument has no asset, it is unrestricted and may be given (or not for optional arguments) with any value. +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 -In the following we list possible `asserts`. Mentioning `argument value` refers to the value of the argument of the operation specified `argument` in `assert_object`. The logic does not differentiate if an `argument` is optional or mandatory. All asserts imply: If the `argument` is given, it must pass the `assert`. If the `argument` is not given, assert is implicitly passed. +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. #### `any` Stateless assert, all argument types. `Argument value` must be equal to one of values in the data list @@ -123,10 +133,10 @@ The assert that needs to pass is now `current_cumsum + incoming value <= max_cum 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 mappings between attributes and asserts that they must fulfill, if present in the dictionary. Allows nesting of `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`. -Note: -- 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 a `any` assert for those attributes, while having e.g. a `lt` assert for the one attribute that is allowed to change. +#### 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. #### Example: Simple transfer Assume account A and B and some unrelated key K. Furthermore A has a custom active authority in the following way: @@ -152,12 +162,6 @@ That has the consquence now that a transfer transaction sending funds away from Note: This is just an illustration of a possible serialization, not a specification of the serialized format. -### Economics - -Adding a custom active authority means increased effort for the backend, and with a stateful one also the need for more storage. -We propose that the transaction fee for the creation is tied to the duration of the custom active authority. -Furthermore, normal accounts can only create custom active authoritites with a duration of maximum 1 year. LTM can do any duration and also unlimited, but the transaction fee is capped at duration of 2 years. - ### Outline of handling incoming transactions When a signed transaction arrives and before the backend evaluates if all necessary authorities are present through the signatures, do the following: @@ -185,14 +189,21 @@ Since the required accounts is Account A, and the given accounts is also Account the transaction is executed. ### Modification to the backend -* Extend the account object to store custom active permission that includes a list of custom active authorities. Multiple custom active authority entries are possible for one operation +* Add a new index or extend the account object to store custom active permission are assigned to an account and contain a list of custom active authorities. Multiple custom active authority entries are possible for one operation * If the active authority of the account is updated, all custom active authorities need to be confirmed in the update. Every unconfirmed one is deleted otherwise -* Extend `account_update` or create a new operation to allow changing the custom active permission +* Provide operations: `install_custom_active_authority`, `update_custom_active_authority`, `delete_custom_active_authority` to allow changing the custom active permission (3 operation to allow custom transaction fees and avoid having to send the complete list of all authorities for every update) * Operation-specific authorities (if present) must be evaluated in incoming transactions * Additional committee parameters may be needed to limit the extend of usage of this feature Notes: The implementation must not differentiate on which operation the custom active authority is applied, all operations are treated in same fashion +### Economics + +Adding a custom active authority means increased effort for the backend, and with a stateful one also the need for more storage. Proposed transaction fees: +- `install_custom_active_authority`: Tied to the duration of the custom active authority. +Normal accounts can only create custom active authoritites with a duration of maximum 1 year. LTM can do any duration and also unlimited, but the transaction fee is capped at duration of 2 years. +- `update_custom_active_authority` and `delete_custom_active_authority`: Similar to `account_update` + # Milestones We propose do split the implmentation into two milestones. Each milestone will be voted on as a separate BSIP: