GithubHelp home page GithubHelp logo

Comments (9)

awmuncy avatar awmuncy commented on August 21, 2024 1

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.

sanderpick avatar sanderpick commented on August 21, 2024 1

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.

awmuncy avatar awmuncy commented on August 21, 2024

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.

awmuncy avatar awmuncy commented on August 21, 2024

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.

sanderpick avatar sanderpick commented on August 21, 2024

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.

sanderpick avatar sanderpick commented on August 21, 2024

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.

joewagner avatar joewagner commented on August 21, 2024

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.

awmuncy avatar awmuncy commented on August 21, 2024

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.

sanderpick avatar sanderpick commented on August 21, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.