Enable support for off-chain processes to react to parameter changes asynchronously.
Origin Document
Observations made while working on #517. In order for off-chain processes to use the correct governance params in the appropriate places in the protocol, they MUST be aware of the most recent param values. In some cases, off-chain processes MUST also know the historical values of some params.
Assumptions
It doesn't make sense for changes to protocol parameters to take effect during a session.
We SHOULD limit the amount of historical data that is persisted to only what is necessary; let external indexers support non-essential indexing.
Example
A param update message is committed in the middle of a session, modifying num_blocks_per_session & min_relay_difficulty_bits by the time a proof is submitted & validated on-chain. The num_blocks_per_session and min_relay_difficulty_bits at the time of the session to which the proof corresponds (i.e. will not be the current session) MUST be used. There may also be off-chain use cases (presently or in the future) which require previous values for params.
sequenceDiagram
participant SessionQueryClient as Supplier
participant Poktrolld
participant PNF
Note over SessionQueryClient,PNF: Session N Start
PNF->>Poktrolld: Update params
break
SessionQueryClient-->Poktrolld: (wait for sesison end)
end
Note over SessionQueryClient,PNF: Session N End
break
SessionQueryClient-->Poktrolld: (wait for grace period end)
end
Note over SessionQueryClient,PNF: Session N Grace Period End
SessionQueryClient ->> Poktrolld: Create claim
break
SessionQueryClient-->Poktrolld: (wait for next grace period end)
end
Note over SessionQueryClient,PNF: Session N+1 Grace Period End
SessionQueryClient ->> Poktrolld: Submit proof
Poktrolld->>Poktrolld: Validate proof
Goals
To support off-chain reactivity to parameter updates (in-between sessions).
Support on-chain persistence (& pruning) of historical param values.
Design Choices
On-chain persistence of historical params
When a param update message is committed:
The corresponding on-chain params value(s) are updated on commit.
The previous param value(s), the current session number, & the height at which the param update message is committed are stored in the on-chain state. There are a couple of options for how to persist historical params that effect what their usage would look like:
Add a cache of previous values to the existing params data structure
History: map[updateHeight]Params{}
Persist historical param values in a new multistore store (w/ height & param name indices)
i. When an off-chain client queries for params, the response includes the current param values, as well as previous values, the last session number that each previous was set, and the height at which each previous value was updated (no longer valid after). An additional parameter would be introduced to determine the "retention block count" for (each or all?) param(s).
Pros
Does not require additional on-chain query logic but would support it.
Consistent with gateaway undelegation logic
Cons
Increases the size of all params query responses which support historical values.
ii. An off-chain client could query would make distinct queries whether querying for current params or historical params. Historical param queries would take a height argument & MAY return an error if the requested height is older than the "retention block count"
Pros
Could be more easily encapsulated (e.g. in a params module) to consolidate common behavior across modules.
Cons
Requires additional multistore store, key prefixes, etc.
Another degree of freedom is open regarding whether or not to add methods for querying param values a specific height, and if so, whether those methods should be on-chain, off-chain, or both.
On-chain only (e.g. aroundKeeper#Params() & QueryParamsRequest)
Could apply to both i or ii.
Off-chain only (e.g. on the Params go protobuf struct itself)
Only applies in i, where historical params are present on the Params data structure.
ModuleParamsClient
Use the respective module's QueryClient to query for the params initially, followed by subscribing to block events via a BlockClient (or its EventsReplayClient deconstruction used with block.UnmarshalNewBlockEvent()) such that updated params may be emitted by an observable & a cache is accumulated over time. Params at height which are not in this cache MUST be queried for on-chain.
Pros:
Easy to use & versatile
Compatible & consistent with other async client behavior
Cons:
May beg refactoring in the block client. It would be convenient to have a method that returns an observable of block.CometNewBlockEvents (see: session_steps_test.go:131).
Use a BlockClient & the respective module's QueryClient to query for the params at the start height of each session.
Pros:
Reduces frequency (as compared to naive implementation) of queries
Cons:
More imperative
Increased query overhead
Likely reduces to 1 just without the cache
Deliverables
...
Non-goals / Non-deliverables
...
General deliverables
[ ] Comments: Add/update TODOs and comments alongside the source code so it is easier to follow.
[ ] Testing: Add new tests (unit and/or E2E) to the test suite.
[ ] Makefile: Add new targets to the Makefile to make the new functionality easier to use.
[ ] Documentation: Update architectural or development READMEs; use mermaid diagrams where appropriate.
Objective
Enable support for off-chain processes to react to parameter changes asynchronously.
Origin Document
Observations made while working on #517. In order for off-chain processes to use the correct governance params in the appropriate places in the protocol, they MUST be aware of the most recent param values. In some cases, off-chain processes MUST also know the historical values of some params.
Assumptions
Example
A param update message is committed in the middle of a session, modifying
num_blocks_per_session
&min_relay_difficulty_bits
by the time a proof is submitted & validated on-chain. Thenum_blocks_per_session
andmin_relay_difficulty_bits
at the time of the session to which the proof corresponds (i.e. will not be the current session) MUST be used. There may also be off-chain use cases (presently or in the future) which require previous values for params.Goals
ModuleParamsClient
(see: notion -ModuleParamsClient
)Design Choices
On-chain persistence of historical params
When a param update message is committed:
The previous param value(s), the current session number, & the height at which the param update message is committed are stored in the on-chain state. There are a couple of options for how to persist historical params that effect what their usage would look like:
History: map[updateHeight]Params{}
i. When an off-chain client queries for params, the response includes the current param values, as well as previous values, the last session number that each previous was set, and the height at which each previous value was updated (no longer valid after). An additional parameter would be introduced to determine the "retention block count" for (each or all?) param(s).
Pros
Cons
ii. An off-chain client could query would make distinct queries whether querying for current params or historical params. Historical param queries would take a height argument & MAY return an error if the requested height is older than the "retention block count"
Pros
params
module) to consolidate common behavior across modules.Cons
Another degree of freedom is open regarding whether or not to add methods for querying param values a specific height, and if so, whether those methods should be on-chain, off-chain, or both.
Keeper#Params()
&QueryParamsRequest
)Params
go protobuf struct itself)ModuleParamsClient
Use the respective module's
QueryClient
to query for the params initially, followed by subscribing to block events via aBlockClient
(or itsEventsReplayClient
deconstruction used withblock.UnmarshalNewBlockEvent()
) such that updated params may be emitted by an observable & a cache is accumulated over time. Params at height which are not in this cache MUST be queried for on-chain.Pros:
Cons:
block.CometNewBlockEvent
s (see: session_steps_test.go:131).Use a
BlockClient
& the respective module'sQueryClient
to query for the params at the start height of each session.Pros:
Cons:
Deliverables
Non-goals / Non-deliverables
General deliverables
Creator: @bryanchriswhite