Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HIPE for plugin helpers #162

Merged
merged 16 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions text/0162-frozen-ledgers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Indy HIPE 0162: Frozen Ledgers
- Author: [Alexandr Kolesov]([email protected]), [Richard Esplin](mailto:[email protected]), and [Renata Toktar]([email protected])
- Start Date: 2020-12-22


## Status
- Status: [PROPOSED](/README.md#hipe-lifecycle)
- Status Date: 2020-01-22


## Summary
[summary]: #summary

Ledgers that are created by Indy plugins cannot be removed without breaking consensus. This HIPE describes functionality to freeze a ledger so that it can be safely removed.


## Motivation
[motivation]: #motivation

Indy Plenum can be extended through the use of [ledger plugins](https://github.com/hyperledger/indy-plenum/blob/master/docs/source/plugins.md). Ledger plugins can introduce new transaction types that are either stored on existing ledgers, or on new ledgers created by the plugins. The [Sovrin Network](http://sovrin.org) used this capability to develop a [token plugin for Indy](https://github.com/sovrin-foundation/token-plugin) that stored fee and value transfer transactions on a token ledger. After experimenting with the plugin, [they decided to remove it](https://github.com/sovrin-foundation/sovrin-sip/tree/master/text/5005-token-removal/README.md).
esplinr marked this conversation as resolved.
Show resolved Hide resolved

Removing the plugin has three important consequences:
* The data stored on the custom ledger created by the plugin will be deleted.
* The audit ledger will no longer be able to obtain the root hash of any custom ledgers from the plugin.
* Any authorization rules (auth_rules) on the config ledger that define fees as being necessary to authorize writes to the domain ledger can no longer be handled by the plugin.
esplinr marked this conversation as resolved.
Show resolved Hide resolved

The first consequence is intended. This HIPE proposes a feature to address the second consequences to ensure that the network remains functional after the plugin is removed. The last consequence is [treated in a separate HIPE](https://github.com/hyperledger/indy-hipe/tree/master/text/0163-default-fee-handler).


## Tutorial
[tutorial]: #tutorial

If a ledger has been created, but will never be used, it can be frozen. The LEDGERS_FREEZE transaction will record the root hashes of one or more ledgers in the state trie, and perpetually use those hashes when computing audit ledger validity. This has two important advantages:
esplinr marked this conversation as resolved.
Show resolved Hide resolved

* It preserves the ability to do rolling updates. Without this capability, attempting to remove a plugin during a rolling update risks breaking consensus if a transaction is submitted to the ledger during the roll out period. This is because updated nodes would not be able to process new transactions that were ordered by the non-updated nodes. But if we freeze all ledgers associated with the plugin, we can do rolling updates because new transactions related to the plugin will be impossible.
* It allows the removal of old plugins. Frozen ledgers can be removed and there will be no left over data from the removed plugin. The removal of old plugins will also help reduce the cost of keeping the network stable and secure.

Freezing ledgers requires the following workflow:
* Upgrade all nodes to a version of Indy that supports frozen ledgers.
* Send a LEDGERS_FREEZE transaction for the ledgers that you want to drop.
* Upgrade all nodes to remove the plugin.
* Execute a migration script to drop the ledgers.

Permissions around the LEDGERS_FREEZE transaction are managed in the auth_rules with a default permission requiring the approval of three trustees ([the same default auth_rule for upgrading the ledger](https://github.com/hyperledger/indy-node/blob/master/docs/source/auth_rules.md)).


## Reference
[reference]: #reference

If a ledger is frozen it can be used neither for reading nor for writing. It will not be caught up by new nodes and can be safely removed. Default ledgers such as the domain, config, pool, and audit ledgers cannot be frozen.

### Implementation notes:
* Implementation of this feature is possible because the catchup of the config ledger happens before catchup of other ledgers.
* We were nervous that removing transactions would break foreign keys in auth_rules, but our testing showed that this is not an issue.
* Frozen ledgers cannot be unfrozen, but dropped ledgers can be recreated with a different ledger ID.

### Implementation steps:
1. Implement LEDGERS_FREEZE transaction handler that will write information about frozen ledger to state.
2. Implement GET_FROZEN_LEDGERS transaction handler that will return information about frozen ledgers from state.
3. Add dynamic validation to all handlers: check if the ledger is frozen.
4. Update audit logic by taking into account the root hashes of frozen ledgers.
5. Fix catch up to not catch up frozen ledgers.

### State schema:
There will be a key in CONFIG_LEDGER that will store a list of frozen ledgers in the following format:

```
2:FROZEN_LEDGERS = [
esplinr marked this conversation as resolved.
Show resolved Hide resolved
<ledger_id>: {
ledger: <ledger_root_hash>,
        state: <state_root_hash>,
        seq_no: <last_seq_no>
},
...
]
```

### New transactions:

Freeze ledgers that have the provided IDs:
```
LEDGERS_FREEZE {
LEDGERS_IDS: [int]
}
```

Return frozen ledgers and their root hashes.
```
GET_FROZEN_LEDGERS
```


## Drawbacks
[drawbacks]: #drawbacks

A frozen ledger should only be removed from the system if it has never been used, because in that scenario the hash on the audit ledger never changed. Understanding this limitation requires consideration of three separate properties of the distributed ledger:
1. The ability to achieve consensus when writing a new transaction to the ledger.
2. The ability to catch up nodes through consensus.
3. The ability to audit the history of the ledger to prove that there was no tampering.

When a ledger is frozen, the root hash of the deprecated ledger is available to be used by the audit ledger when computing consensus. So the first property is preserved.

During catchup, the transaction validation logic is not executed. This is because we are catching up transactions that have the ledger BLS signature, so we know it was already validated and we don't need to do it again. So the second property is preserved.

If a ledger is added (so its root hash is expected by the audit ledger), but it was never used (so the root hash never changed), then LEDGERS_FREEZE will preserve the third property even when the ledger is removed.

But the third property will not be preserved if there is a history of transactions on a ledger and then the ledger is removed. This is because the LEDGERS_FREEZE transaction only stores the most recent root hash, and not the entire history of root hashes, so that history can not be recreated after the plugin ledger is removed. Instead, the frozen ledger should be retained in its read-only state.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the Sovrin Ledgers as an example, the token ledger has been used on BuilderNet and StagingNet, but not on MainNet. Based on this statement it would be safe to delete the token ledger data from MainNet, but not on BuilderNet, or StagingNet. If the ledger were to be deleted from either BuilderNet, or StagingNet the ability to audit the history of the ledger would fail, but how would that present itself, and how do we protect against deleting a ledger that has been used so we can ensure the historical integrity of the ledgers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an issue against the remove_ledger.py script; hyperledger/indy-node#1659

Copy link
Contributor Author

@esplinr esplinr Feb 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIPE 0162 addresses this in the Drawbacks section.

  1. The ability to audit the history of the ledger to prove that there was no tampering.
    … the third property will not be preserved if there is a history of transactions on a ledger and then the ledger is removed. This is because the LEDGERS_FREEZE transaction only stores the most recent root hash, and not the entire history of root hashes, so that history can not be recreated after the plugin ledger is removed. Instead, the frozen ledger should be retained in its read-only state.

As the HIPE explains at the beginning,

The data stored on the custom ledger created by the plugin will be deleted.
[This] consequence is intended.

The associated SIP makes clear that this is acceptable for the Sovrin test networks because they are not intended to have a permanent history. See my comment on the related sovrin-sip.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The answers focus more heavily on the current intended use case. I'm thinking more about the impact when something like this is used in general and it's impact to historical integrity.

To summarize my understanding so far:

Once the the ledger has been frozen and deleted, the ability to audit the history is irrevocably lost.

  • How does this end up presenting itself from an operational and network administrative standpoint?
  • What are the implications to the network and other ledgers?
  • What is the impact to the network?

If the ledger is frozen but the data is retained, can the history of the ledger be audited?

  • Can this be accomplished if the plugin that created/managed the ledger is removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand your questions. Perhaps the confusion is due to different uses of the word "audit".

The audit ledger exists to confirm the relationship between all of the ledgers if someone decides to replay the transaction history. But this isn't something that the ledger does. It is something that a third party could theoretically do if desired.

How does this end up presenting itself from an operational and network administrative standpoint?

As described in the "Drawbacks" section, the network continues to function correctly. New transactions can be ordered. Catch-up continues to function, because the validation logic is not executed.

But attempting to reconstruct the ledger by replaying all events will fail because some of the data is missing. Freeze_Ledgers only stored the root hash of the last entry on the deleted ledgers, so you can't calculate the previous root hashes when checking for consistency in the audit table.

What are the implications to the network and other ledgers?

Things work fine, but third parties who try and recreate the ledger can't have confidence that the history wasn't tampered with (because it was tampered with).

What is the impact to the network?

It works fine. Consensus is preserved, new transactions can be ordered, and queries are processed.

If the ledger is frozen but the data is retained, can the history of the ledger be audited?

Sure. All the data is there.

Can this be accomplished if the plugin that created/managed the ledger is removed?

Yes, but it would take some work to figure out what the removed plugin was doing. This is explained in more detail in the HIPE for default fee handlers: "The default fee handler replays transactions without performing validation. This is fine for catching up validator nodes, but an audit of the ledger history would need to take into account the historical validation expected by any removed plugins."

Exactly how to audit a ledger seems to be beyond the scope of this HIPE.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this then be a fair summary?

Once a ledger is frozen and the associated plug-in is removed, regardless of whether or not you keep the data, the history of the associated ledger(s) cannot be audited. In order to audit their history you would have to retain the ledger data (not delete the ledger after freezing it) and restore the plugin (or computational equivalent) first.

Copy link
Member

@WadeBarnes WadeBarnes Mar 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does a frozen ledger affect new nodes added to the network. I assume, whether or not the frozen ledgers data was retained, only the hash at the point in time the ledger was frozen will be replicated to any new nodes. Is this correct? I think this may already be implied by the fact the frozen ledger will not be included in catch up operations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this then be a fair summary?
Once a ledger is frozen and the associated plug-in is removed, regardless of whether or not you keep the data, the history of the associated ledger(s) cannot be audited. In order to audit their history you would have to retain the ledger data (not delete the ledger after freezing it) and restore the plugin (or computational equivalent) first.

I disagree with the sentiment that the history cannot be audited even if you keep the data. The audit process is the same as before, because audits are done outside the ledger software and independent of what plugins are installed on the ledger. To perform an accurate audit you will need to track the history of the software that was installed so that you can confirm the validation rules that were used for the original ledger writes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does a frozen ledger affect new nodes added to the network. I assume, whether or not the frozen ledgers data was retained, only the hash at the point in time the ledger was frozen will be replicated to any new nodes. Is this correct? I think this may already be implied by the fact the frozen ledger will not be included in catch up operations.

I believe that the ledger data is propagated during catch up to the rest of the pool, even for frozen ledgers. This is because validation is not done for transactions that already have a BLS signature. Is that correct @Toktar ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this note from @anton.denishchenko: "Frozen ledgers are not catched up (this is possible because the LEDGERS_FREEZE transaction is part of the config ledger, which is applied before additional ledgers)."
Thank you @WadeBarnes for highlighting this misunderstanding. I'll add a note about this topic to the HIPE.



## Rationale and alternatives
[alternatives]: #alternatives

The current need for freezing a ledger can be avoided by moving the token transactions into Indy as deprecated historical transactions. This would allow the history to be validated, but we don't want Indy to become a graveyard for every transaction type a plugin author defines. Implementing frozen ledgers is a more generic way to address the problem.

There is a [proposal to incorporate the token plugin as an optional part of Indy](https://github.com/hyperledger/indy-hipe/tree/master/text/0161-generic-token). This would remove the need for the removal of the token plugin from the Sovrin ledger, but no one has volunteered to do that work. Even if Indy does one day have a generic token, the ability to freeze ledgers would still be useful for the development and maintenance of other ledger plugins.

We considered implementing the frozen ledger hash in a plugin, but we consider it a generally useful feature and believe it should belong in Indy.


## Prior art
[prior-art]: #prior-art

No relevant prior art.


## Unresolved questions
[unresolved-questions]: #unresolved-questions

All questions raised during the development of this proposal have been answered in this draft of the proposal.
75 changes: 75 additions & 0 deletions text/0163-default-fee-handler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Indy HIPE 0163: Default Fee Handler
- Author: [Alexandr Kolesov]([email protected]), [Richard Esplin](mailto:[email protected]), and [Renata Toktar]([email protected])
- Start Date: 2020-12-01


## Status
- Status: [ADOPTED](/README.md#hipe-lifecycle)
- Status Date: 2020-01-22
- Status Note: The changes in this HIPE were discussed in the Indy Maintainers calls on [2020-12-22](https://wiki.hyperledger.org/display/indy/2020-12-22+Indy+Contributors+Call) and [2020-01-19](https://wiki.hyperledger.org/display/indy/2021-01-19+Indy+Contributors+Call). The other maintainers approved the plan, but requested a HIPE to be submitted.


## Summary
[summary]: #summary

Adding a default fee handler to Indy will make it easier to maintain ledgers with payment plugins.


## Motivation
[motivation]: #motivation

Indy Plenum can be extended through the use of [ledger plugins](https://github.com/hyperledger/indy-plenum/blob/master/docs/source/plugins.md). Ledger plugins can introduce new transaction types that are either stored on existing ledgers, or on new ledgers created by the plugins. The [Sovrin Network](http://sovrin.org) used this capability to develop a [token plugin for Indy](https://github.com/sovrin-foundation/token-plugin) that stored fee and value transfer transactions on a token ledger. After experimenting with the plugin, [they decided to remove it](https://github.com/sovrin-foundation/sovrin-sip/tree/master/text/5005-token-removal/README.md).
esplinr marked this conversation as resolved.
Show resolved Hide resolved

Removing the plugin has three important consequences:
* The data stored on the custom ledger created by the plugin will be deleted.
* The audit ledger will no longer be able to obtain the root hash of any custom ledgers from the plugin.
* Any authorization rules (auth_rules) on the config ledger that define fees as being necessary to authorize writes to the domain ledger can no longer be handled by the plugin.

The first consequence is intended. The second consequence is [treated in a separate HIPE](https://github.com/hyperledger/indy-hipe/tree/master/text/0162-frozen-ledgers). This HIPE proposes a feature to address the last consequence.


## Tutorial
[tutorial]: #tutorial

[Indy Authorization Rules](https://github.com/hyperledger/indy-node/blob/master/docs/source/requests.md#auth_rule) provide a flexible way of authorizing write transactions to the domain ledger. This authorization can [depend on the payment of fees tracked by ledger plugins](https://github.com/sovrin-foundation/libsovtoken/blob/master/doc/fees.md).

Because Indy currently has no native concept of fees, if a plugin that implemented fees is removed, historical transactions can no longer be validated and will prevent new nodes from catching up on the ledger. In many cases it is sufficient to know that the historical transactions were valid at the time of writing. In these cases a default fee handler can approve the historical transactions.


## Reference
[reference]: #reference

Implementation will require adding to Indy [the SET_FEE transaction copied from the sovtoken plugin](https://github.com/sovrin-foundation/token-plugin/blob/a635780361a7478ea4b2f47fcffee78b150fdea3/sovtokenfees/sovtokenfees/req_handlers/write_handlers/set_fees_handler.py#L20) and [a default fee handler copied from the sovtoken fee handler (FeesAuthorizer)](https://github.com/sovrin-foundation/token-plugin/blob/master/sovtokenfees/sovtokenfees/fees_authorizer.py#L26). The fee handler will have the validation logic replaced with `throw NotAllowedException()`.

Any Indy transaction handler consists of two parts: validation logic and execution logic. Indy transaction handlers perform validation on new transactions, but not during the catchup process. This is because the transaction is already on the ledger, so we know that validation was already performed and we don't need to do it again. The default fee handler will contain a copy of the sovtoken fee handler but with validation disabled, so it will allow catchup but will not allow new transactions with fees.

Indy will also look to the default fee handler to determine the execution logic for fee related transactions, but the default fee handler will not allow any new fee transactions and so doesn't need any specific execution logic. Any future implementation of a payment plugin will need to override the default fee handler with a functional implementation of validation and execution logic.


## Drawbacks
[drawbacks]: #drawbacks

* Historically there were concerns about having payment functionality included in Hyperledger projects, but as distributed payments have become more mainstream, it now seems acceptable to include into Indy generic primitives related to payments.
* The default fee handler replays transactions without performing validation. This is fine for catching up validator nodes, but an audit of the ledger history would need to take into account the historical validation expected by any removed plugins.
esplinr marked this conversation as resolved.
Show resolved Hide resolved
* The ledger could be erroneously configured with auth_rules that define fees without having a plugin installed to process them. The exact behavior in this scenario will depend on the specific rules that mention fees, and could lead to confusing and hard to diagnose behavior. However this is a niche case because SET_FEE transactions will be rejected if there is no plugin to define them.
* The existence of a default fee handler in Indy Node could in theory be exploited by an attacker, but we consider this a small risk for a distributed ledger as any attack would have to simultaneously succeed on a majority of validation nodes in order to change what gets written.


## Rationale and alternatives
[alternatives]: #alternatives

The current need for a default fee handler could be avoided by moving the token transactions into Indy as deprecated historical transactions. This would allow the history to be validated, but we don't want Indy to become a graveyard for every transaction type a plugin author defines. Implementing the features proposed in this HIPE is a more generic way to address the problem.

There is a [proposal to incorporate the token plugin as an optional part of Indy](https://github.com/hyperledger/indy-hipe/tree/master/text/0161-generic-token). This would remove the need for the removal of the token plugin from the Sovrin ledger, but no one has volunteered to do that work.


## Prior art
[prior-art]: #handler-prior-art

No relevant prior art.


## Unresolved questions
[unresolved-questions]: #unresolved-questions

All questions raised during the development of this proposal have been answered in this draft of the proposal.