openlang

Upgradeable means the storage layout is forever

People hear proxy pattern and think the immutability problem went away. It moved. The proxy holds the state and forwards calls with delegatecall, so the implementation's code runs against the proxy's storage. Every later implementation has to agree with every earlier one about which slot holds what.

State variables are laid out in declaration order, packed into 32-byte slots where they fit, with inherited variables first. Insert a field in the middle, reorder base contracts, or widen a packed uint128 to uint256, and the new code reads old bytes with a new meaning. The compiler has no idea an old layout exists. It compiles the new source as if this were the first deployment.

Constructors don't help. A constructor runs when the implementation itself is deployed, against the implementation's own storage, so proxies rely on an initializer function that must be guarded to run only once.

My rule is append only. Review compares the storage layouts of the old and new versions before anyone reads the logic diff.

后续讨论 3

顺着彼此的依据读下去
  1. 固定链接

    I come from ABAP, where nearly everything can be changed in place, and that is not better, for what it is worth. Change a domain or data element in the dictionary and every program that typed its variables against it with TYPE picks up the new definition the next time it is generated. That sounds kind until an old report that copied the field into a hand-written TYPE c LENGTH 10 starts cutting values off without a word.

    So I agree with append only, and I would add one thing to the review. Search for every place where the old shape was copied by hand. Those copies don't follow the dictionary. For a contract I would expect them in off-chain scripts that read raw slot numbers.

  2. 固定链接

    Never used Solidity, but the habit I'd bring is keeping the previous caller beside the new version, except here the previous caller is the stored state itself. A layout comparison tells you nothing moved. It can't tell a future reader why a slot that looks unused has to stay exactly where it is, so I'd write that down next to the code at every release.

  3. 固定链接

    One exception to the constructor paragraph, and it cuts both ways. An immutable assigned in the implementation's constructor is not in storage at all. The compiler writes its value into the runtime code, so a delegatecall from the proxy runs code that already carries it. The value does come through the proxy. It is also shared by every proxy that points at that implementation, and it changes only when the implementation is redeployed.

    So an immutable is fine for something that belongs to the code version, such as a fixed dependency address. For anything that should differ per proxy it is the wrong place, and a storage layout diff will not show it, because it never had a slot. Does your review read only the storageLayout output, or the immutables as well?

同一个问题的其他角度

  1. My function returns an array when all I asked for was a path

    Writing a PowerShell function from scratch for once (rather than squinting at someone else's), and it misbehaves in a way I c…

    回复 4最近活动 powershell
  2. My init set fill-column for exactly one buffer

    Had (setq fill-column 72) in my init for ages and only just noticed that M-q in a fresh buffer still wraps at 70. (In *scratc…

    回复 8最近活动 emacs-lisp
  3. SilentlyContinue is On Error Resume Next with better spelling

    Went from inherited workbooks to inherited PowerShell scripts and found my old friend in a new coat. -ErrorAction SilentlyCon…

    回复 0最近活动 powershell