-
Couldn't load subscription status.
- Fork 48
Refactor proofs contracts to allow reusing contract implementations #153
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say we need to be specific about the args that we're passing in from the factory. There are a lot of game configuration options that should be hard coded (but are immutables to support testing), some that are chain specific but generic enough for the factory to know about and some that are chain specific and very specific to a particular game type which the factory shouldn't know about.
protocol/immutable-flattening.md
Outdated
|
|
||
| # Proposed Solution | ||
|
|
||
| In order to elegantly resolve this, the two sets of immutable args should be flattened into a single set of immutable args deployed alongside the proxy from the factory. The factory will instead store all of the different per-chain values, and pass in all arguments as a top level proxy with immutable args. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The factory can pass through the L2 chain ID but I don't think it's appropriate for the DisputeGameFactory to know of details of specific fault proof implementations like the absolute prestate. Not all game types will need that and DisputeGameFactory should be generic enough to work with any game implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible to store all constructor args as just generic bytes, which while kind of messy because it hides the actual constructor args (we just store Implementations and byte arrays to pass in as "base" constructor args) I think its the only way to have generic chain-indifferent implementation contracts to clone from each type, but still be adaptable to multiple L2 deployments.
Is there another approach you had in mind for flattened immutable args on games?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all game types will need that and DisputeGameFactory should be generic enough to work with any game implementation
What if the constructor args were stored within the DGF, in a flexible way. So, the current gameImpls mapping just holds the addresses of the impls:
mapping(GameType => IDisputeGame) public gameImpls;but could be updated to hold the constructor args too:
struct GameData {
IDisputeGame gameImpl;
bytes constructorArgs;
}
mapping(GameType => GameData) public gameImpls;Note that this breaks the gameImpls() interface, but gives an idea of one approach that avoids baking in game specific details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a huge fan of "here's a random binary blob that configures critical aspects of the dispute game", but this is logically very close to the approach that we're planning where the DisputeGameFactory holds a list of contracts that know how to create specific dispute game types (#153 (comment)). So rather than creating directly it calls those contracts to create the new game contract. Then they can hold whatever game specific configuration they need in an understandable format. Things like absolutePrestate can easily be written to storage instead of being an immutable and so we can follow the MCP pattern and hard forks could be done by just calling setAbsolutePrestate and not need to deploy any new contracts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, the DisputeGameFactory will make reference to a set of game proposal type specific factories?
I find that somewhat confusing, but I can get over it if it means that the proposal factories are MCP compatible, and we don't actually need to worry about the proposals being proxied to some single implementation (right?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I found the comment here. Noting that this means that N game types will mean N implementations and N proxies per system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, the
DisputeGameFactorywill make reference to a set ofgameproposal type specific factories?I find that somewhat confusing, but I can get over it if it means that the proposal factories are MCP compatible, and we don't actually need to worry about the proposals being proxied to some single implementation (right?).
Do you think this is too much for OPCM? It's a lot of contracts/implementations/deployments but I think it ultimately makes it easier to reason about a lot of chains and should simplify our upgrade process from here a lot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been going back and forth on this but I do ultimately think that the Creator pattern is the most flexible while also being the safest. It's much easier to verify things about a contract than it is to verify things about a data blob.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I think about it the more I realize that the Creator pattern is roughly equivalent in complexity to the gameImpl CWIA pattern. Each game has either a gameImpl or a Creator that the DGF uses for game creation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the DisputeGameFactory is the right place for these contract addr references to be held.
The factory is meant to be a high-level abstraction that knows nothing about the underlying games. The AnchorStateRegistry + DelayedWETH contracts are too low-level for the factory to know about.
For ex, the AnchorStateRegistry assumes that what is being disputed over is an output root. The DelayedWETH contract assumes a delay in bond payouts. These two things are not always true, especially for extensions to the protocol (i.e. kailua, op-succinct [once Succinct adheres to the IDisputeGame interface...]). The DGF should have no opinions on this, and only perform accounting work for CWIA proxies created.
Holding this abstraction is also particularly useful for us, as we venture into working on interop proofs + multiproofs. What if we want to do something like swap out the AnchorStateRegistry for something more friendly to the super root, and preserve backwards compat while the feature is in progress? The IAnchorStateRegistry interface might be insufficient.
The DGF should continue serving its usecase as a generic middleware for the creation of disputes over arbitrary claims, and allow for arbitrary underlying rulesets, so long as they conform to the IDisputeGame interface.
|
@clabby wdyt about storing arbitrary bytes for the base constructor to each CWIA? It keeps each deployment agnostic to the actual underlying solution, but still allows the flattening which simplifies the deployment process greatly. The main downside is just the information loss onchain as base constructor args just become a bytestring |
I think that could work out well, nice idea. You mean just appending some opaque If so, that would be nice. Though it would be really great if this were dynamic, i.e. giving the interface IExtraDataFiller {
/// @notice Fills the initial `extraData` for a dispute game type.
/// @param _gameType The dispute game type that the `extraData` is being crafted for.
/// @returns extraData_ The base `extraData` for the dispute game type. Can be expanded by user input.
function fill(GameType _gameType) external view returns (bytes memory extraData_);
}Then, we could also remove the The implementor of that interface could store the L2 chain ID, ASR, & DelayedWETH for the given game type(s), and format accordingly. When upgrading these, it would be a bit easier than the opaque One downside of this would be increasing costs of the proposal route. It probably won't be much, but it's an extra |
|
Do we want an we'd just do: and then I think the That probably even removes the need for the |
|
I'll move the proposal to be more in line with @ajsutton, primarily because abstracting at the creation level is really nice in that we can simplify weird interfaces at the creator level, not the Realistically both implementations end up being basically the same (calling ExtraDataFiller -> create, vs calling Creator, who then creates) |
|
Made a design review meeting to finalize and discuss all of this, tried to fit it within everyone's calendar |
protocol/immutable-flattening.md
Outdated
|
|
||
| # Proposed Solution | ||
|
|
||
| In order to elegantly resolve this, the two sets of immutable args should be flattened into a single set of immutable args deployed alongside the proxy from the factory. The factory will instead store all of the different per-chain values, and pass in all arguments as a top level proxy with immutable args. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been going back and forth on this but I do ultimately think that the Creator pattern is the most flexible while also being the safest. It's much easier to verify things about a contract than it is to verify things about a data blob.
Nice - yes, this is great. Also is much more workable for extensions to the protocol that require different proposal semantics. |
|
Not allowed to make this link public public for some reason, but link for people in OP Labs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM just worth updating the summary.
protocol/immutable-flattening.md
Outdated
|
|
||
| # Summary | ||
|
|
||
| The `DisputeGameFactory.sol` should be upgraded to contain the arguments needed for every new `DisputeGame.sol`, and all of the constructor argument/logic should be moved into proxy which is actually pointed toward the `DisputeGame.sol` implementation, so that a single implementation of `DisputeGame.sol` can be used across each rollup. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this got updated to match the creator contract approach - DisputeGameFactory won't contain the arguments need to create each new dispute game now.
|
Merging with updated changes + some formatting fixes, had approval from design meeting (which is now fully reflected in the doc) + review fixes are now merged in |
Description
This PR aims to flatten the constructor arguments in
DisputeGame.solso it uses just ProxyWithImmutableArgs rather than Immutable + Immutable variables in its constructor, allowing different factories to all point to the same implementationTests
No tests needed for the design doc
Additional context
Currently we need to re-deploy the
DisputeGame.solimplementation the factory uses for each new rollup due to its immutable constructor arguments, this PR aims to fix that.Metadata