Comments (9)
I like it. This will retain some tech debt from the upgrade (unlike option 2, which would be clean stroke), but it is much simpler and maintains full backward compatibility.
Just one other note; while versioning like this may make it less likely, it doesn't totally save us from breaking changes in the future. For example, if we update the Policy struct such that the new version contains a property which can't be inferred from previous controllers' Policy response, there will be breaking changes. No way around that, just a call out.
from evm-tableland.
Is the idea that all future policy contracts will have a
version
function that returns a uint(or whatever type makes sense)?
yep, or an enum, but then implementations would have to pull in an additional interface / contract that holds that enum. a uint if probably cleanest.
from evm-tableland.
There's a bit of an issue here with backward compatibility. We can maintain backward compatibility of the function call with a try/catch block, like so:
try ITablelandControllerV2(
controller
).getPolicy{value: msg.value}(caller, tableId) returns (Policy memory policy) {
return policy;
} catch {
return ITablelandControllerV1(
controller
).getPolicy{value: msg.value}(caller);
}
With this implementation, custom errors thrown by ITablelandControllerV2 are being caught and we lose valuable information in the process. While a try/catch block could maintain backward compatibility with the function call, we need to find a solution to preserve the original error message.
Unfortunately, there is currently no way to check if a function exists on another smart contract without attempting to call it. This means that if we call a function that doesn't exist, we need to catch the resulting error. However, if the error is a custom error, we cannot simply re-throw it without losing important information. We have been exploring potential solutions to this issue, but have not found a suitable workaround at this time.
If you have any suggestions or ideas for how to address this problem, let me know. Without a way around this, we either lose backward compatibility, or we lose the ability to return customer errors from the new getPolicy function call to the ultimate caller.
from evm-tableland.
In addition to the approach above, I've come up with two more, for a total of 3 approaches.
1. Maintain old method in perpetuity, lose Custom Errors
(Outlined in previous comment)
Pros: Simplest to execute.
Drawbacks: Not fully backward compatible; we lose custom errors. We also incur tech-debt, supporting an old, useless method signature forever. Carrying over tech-debt from testnet into the future seems like a bad idea.
2. Manual migration with custom contract and controller overrides
We override all existing controllers, pointing to a new controller which we create. This new controller contract has a getPolicy
method with the new call signature. It then calls the old signature on the original controller contract. This will require A) A manual override function which allows the owner of the TablelandTables contract to use the "setController" method for all existing tables with controllers. B) We reconstruct the existing controller mappings in this new pass through contract manually (by looking at old setController events).
Pros: Complete backward compatibility, users don't have to upgrade anything, no vestigial logic in the primary contract.
Drawbacks:: This would be moderately complex to execute. It might also seem concerning to users if they see their controller set without their input (though since this is testnet, it might be less jarring.)
3. Drop backward compatibility, give upgrade window
We're still in testnet, and breaking changes are expected. We announce something like; "We will be upgrading controller logic (getPolicy method) in 2 months. If your team is actively using custom controllers, you must upgrade. The simplest way to upgrade is to redeploy your controller contract with a single added function;
function getPolicy(address caller, uint256) public returns (Policy memory) {
return this.getPolicy(caller);
}
and set the controller for your table to your new contract"
Pros: Simple to execute. Simplest code-wise, but it does require a little community work. No tech debt from old function whatsoever.
Drawbacks: Not backward compatible.
from evm-tableland.
The controller / policy logic is likely going to evolve a number of times before mainnet. I think asking users to take some action every time is going to be quite a drag.
That drops option 3. Option 2 puts the annoyance of upgrade on us, which is better, but still a time suck. I agree also that force changing a controller for a user feels quite bad.
Why not add another method to ITablelandController
called version
that returns some version. If that errors in a try catch, than you know it's the original signature. Otherwise, use the version that it tells you. You'd need a mapping of versions and also a const that is the current version, which maps to the latest signature. This gives us some freedom to make more than one breaking change as we iterate w/o having to make some big community effort every time.
from evm-tableland.
Just one other note; while versioning like this may make it less likely, it doesn't totally save us from breaking changes in the future. For example, if we update the Policy struct such that the new version contains a property which can't be inferred from previous controllers' Policy response, there will be breaking changes. No way around that, just a call out.
yep, I think I mentioned in another comment though that we should also version Policy
. ie, just keep it as a child of ITablelandController
:
interface ITablelandController {
/**
* @dev Object defining how a table can be accessed.
*/
struct Policy {
from evm-tableland.
Is the idea that all future policy contracts will have a version
function that returns a uint(or whatever type makes sense)?
from evm-tableland.
Despite being identical, Solidity seems to not want to implicitly convert policies across the two TablelandController interface. I think this is why I pulled it out into it's own struct in the first place. If I can't find a way around that, I'll keep that change; Policy struct separate from contract interface.
If we do update the policy struct, we'll have to do explicit transforms of the data in the contract from any old versions.
Return argument type struct ITablelandControllerV1.Policy memory is not implicitly convertible to expected type (type of first return variable) struct ITablelandController.Policy memory.
from evm-tableland.
Ah, makes sense. We could create some mappings, ie, ITablelandControllerV1.Policy
-> ITablelandController.Policy
since the validator would always expect the latest Policy
.
from evm-tableland.
Related Issues (20)
- [REVM-15] Upgrade to ethers v6 HOT 3
- [REVM-9] Test issue
- [REVM-10] Test linear issue
- [REVM-11] Test GH issue HOT 2
- [REVM-12] Test linear issue
- [REVM-13] Test with Aaron HOT 4
- [REVM-4] Deploy TablelandTables to FEVM
- [REVM-2] Dynamic policies
- [EVM-19] getPolicy should take tableId
- [EVM-26] Create a single runSQL endpoint that can handle multiple writes and creates
- [EVM-22] Create contract based GRANT and REVOKE HOT 1
- [EVM-15] Upgrade to ethers v6 HOT 5
- [EVM-39] Drop support for Goerli
- [EVM-38] Deploy TablelandTables to Sepolia
- [EVM-2] Dynamic policies
- [EVM-36] Update solidity docs to match latest NatSpec
- [EVM-18] [EVM-41] Use indexes in CreateTable and RunSQL events
- [EVM-42] Update README
- [EVM-43] Github test workflow fails intermittently
- [EVM-45] The publish action no longer works HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from evm-tableland.