Hacker News new | ask | show | jobs
by jude- 1661 days ago
> At the same time, smart contracts have some deeply problematic constraints: > Smart contracts can’t be upgraded. Smart contracts are deployed once and run forever; their code cannot be changed. The software development industry has literally zero experience with such a deployment model.

I stopped reading here. First, this is factually wrong -- the software industry has tons of experience dealing with software that can't be upgraded. Ever try to upgrade the firmware on a chip with no I/O facility for doing so? The answer is you don't; you instead focus on getting the code correct the first time, and possibly you build out a way to recall the product and replace it with a fixed version and price in the risk of needing to do so into the product itself. It can be done; it just takes discipline.

Second, if you don't understand why smart contracts being immutable is a necessary and desirable feature of the system, not a bug, then you're not going to understand much of web3. Like, think about it for five minutes -- if your smart contracts can be upgraded by default, and this code manages valuable digital assets, then their code can be replaced with code that steals those assets. Making this very, very, very hard is deliberate.

4 comments

Original author here. I strongly disagree.

While it’s certainly true that firmware on many devices can’t be or simply isn’t updated, it’s also the case that bugs ship. Because of this, engineers design and ship their more complex devices with the facility to upgrade firmware. Even my ages old stereo receiver is upgradable (although painfully so).

But this is about smart contracts: code that in many cases moves money. I’d put that in the category of code that really could benefit from carefully controlled upgradability.

I’m not the only one. For instance, the primary USDC contract on Ethereum is a proxy contract — it’s upgradable by design. I think that makes a lot of sense, and apparently so do the engineers managing those many billions of dollars: they’ve weighed the balance of “trust” in the abstract with “make sure it doesn’t break” in the real and made their decision.

Beyond that, a guiding principle of some newer blockchains (like Tezos) is that code will need to evolve over time. Like many things Web3, it’s too soon to tell how things will shake out in the long run, and a variety of approaches seems desirable.

> I’d put that in the category of code that really could benefit from carefully controlled upgradability.

First, if you're saying that you need a way to upgrade your money-managing code periodically because you will likely ship versions of it with show-stopping bugs (such as those that enable the destruction or theft of the users' funds), then why should I trust that you will ship flawless code for upgrades?

Second, if the combined security budget of the people who can carry out the upgrade is less than that of the majority of the block producers on the chain, then why build an upgrade procedure at all? Why risk it? Instead, just deploy a new version of the smart contract, and ask users to use that one instead. If it gets confirmed, then an honest majority of block producers will ensure that it stays confirmed. This takes no code at all. You simply sign the new code with the same key that deployed the old code to demonstrate that it originates from the same author(s). Let users decide on their own whether or not to use your upgraded code -- after all, it might introduce new bugs, and the "bugs" you are fixing might be features to other people. It's not your place to tell users what version of the code should be used, and what should not be used.

> why should I trust that you will ship flawless code

You shouldn’t, because no code of sufficient complexity is flawless.

> just deploy a new version of the smart contract

On the Ethereum blockchain (for instance) storage and smart contracts are tightly coupled. If you move to a new smart contract you may well lose your state. This is fine in many cases and preferable in some. But not in others!

> You shouldn’t, because no code of sufficient complexity is flawless.

So why build an upgrade procedure at all, when you don't have to?

> On the Ethereum blockchain (for instance) storage and smart contracts are tightly coupled.

Sounds like an unforced error on these smart contracts' authors parts, and should not be used as an excuse to compromise the principle of code immutability.

If the possibility existed that they need to replace the business logic at a later date, then they should factor the storage logic so as to avoid this coupling. It can be done -- for example, a smart contract dapp can leverage a shared contract that only implements a public key/value store, where the keys are prefixed by the calling contract's address. Then, when the business logic contract changes, it can access any old versions' state with the old versions' contract address, apply any migrations on-the-fly, and store new state that will not overwrite old state due to this key prefixing.

In the Stacks blockchain (which I work on), we go one step further by making it so you can run an arbitrary read-only code snippet on the state of the blockchain at any point in the past (as given by a block hash). Then, you don't even need to do any migrations -- you can just query the historic state of your old contract and only store new data once it's necessary.

> So why build an upgrade procedure at all, when you don't have to?

Flaws, once known, can be remediated. I don’t think macOS has bulletproof security but I’m sure glad Apple keeps updating it.

> In the Stacks blockchain (which I work on), we go one step further by making it so you can run an arbitrary read-only code snippet on the state of the blockchain at any point in the past (as given by a block hash).

That’s a super interesting design point; I’m excited to see where that leads.

Out of curiosity, how does this typically get exposed in dApps (or wallet UIs) built on Stacks?

> Flaws, once known, can be remediated. I don’t think macOS has bulletproof security but I’m sure glad Apple keeps updating it.

My point is that a piece of software does not need an upgrade procedure if there exists a way to install a newer copy without touching the old one. Trying to build an upgrade procedure when there is always the option to install a newer version this way (especially if it can seamlessly access the older version's state) is at best over-engineering.

> Out of curiosity, how does this typically get exposed in dApps (or wallet UIs) built on Stacks?

This is being done right now with Stacks' on-chain naming system, which is realized as a smart contract. The new naming system does not need to import any state from the old system in order to resolve pre-existing names, nor does the existing naming system need to be disabled, because the new system is instead able to call the name resolution method via this "run read-only code on the chainstate as of this block" feature. The past is immutable, so future changes to the state of the old system beyond a predetermined sunset block (defined in the new contract) will not be visible to the users of the new system.

Consider this example. Suppose the name "alice.btc" was registered at block 1000 (hash 0x123) in the old system, and suppose the new system was deployed to use all the state in the old system up to block 1001 (hash 0xabc). Resolving alice.btc in the new system runs code to the effect of:

  (let (
    (alice-rec (at-block 0xabc
      (contract-call? 'SP000000000000000000002Q6VF78.bns name-lookup? "alice.btc")))
    )
    ;; Do something with alice-rec
  )
Internally, the (at-block) function runs the given code body with access to the system state as it was as of the end of block 0xabc. The system state is represented internally as a set of key/value pairs indexed by a forest of authenticated hash tries which make it efficient to query a key as of a particular block (see https://github.com/stacksgov/sips/blob/main/sips/sip-004/sip... for details).

Suppose bob.btc was registered in block 1002 (hash 0xdef). The above (at-block) call will not resolve bob.btc in the old contract, because its state was written after the sunset block 0xabc.

Being able to query arbitrary past blockchain history is kind of gross, because you can neither partition nor elide any of the data under consensus- your verifiers need to have a copy of everything. Leads to expensive nodes cause disk space and RAM use explodes. Not an uncommon complaint about immutable programming techniques generally. Idk, kinda seems like a bad tradeoff to me. But then again if you're building it on top of the literal money fire that is bitcoin, who cares about scalability and resource use!
> verifiers need to have a copy of everything.

Hate to break it to you, but if your consensus rules permit forks, then there's no getting around this. A fork can be mined arbitrarily far in the future that builds off of a block arbitrarily far in the past.

We intend to partition state by application, for applications that are willing to trade smart contract composibility for more block space: https://gist.github.com/jcnelson/c982e52075337ba75e00b799421...

> But then again if you're building it on top of the literal money fire that is bitcoin, who cares about scalability and resource use!

Show me a more resilient blockchain and we'll build on that instead. Popular systems see lots of usage; news at 11.

> just deploy a new version of the smart contract, and ask users to use that one instead. If it gets confirmed, then an honest majority of block producers will ensure that it stays confirmed.

I can't see how the operation you describe here is defined or possible for any blockchain that hosts more than one smart contract. Just for a start, how do you decide whether to include a smart contract upgrade that the majority of verifiers doesn't care about either way (surely the most common case)? It's like saying that legislation doesn't matter because we have elections. The elections (consensus) produce the legislation (allowed transactions). You can squish those two layers together, but starts to break the premises of the underlying system (e.g. DAO fork).

I would love to know what software system you were thinking of when you wrote this. Or were you? Sincere question.

Uniswap comes to mind. V2 is superseded by V3, and V3 is the default option of the web UI, but you can still use V2. There are no plans to sunset V2, and I’m not certain that V2 could be terminated at this point, as the signing keys were burned IIUC.
I think that's what's interesting about "blockchains" versus on specific chain.

1. polygon is a cheap(as in transaction fees) knock off of Ethereum, competition good. bad from the perspective of capture value in the protocol.

2. as far as I can tell as long as one oracle doesn't become dominate that provides competition

3. Defi is enabling users value across other networks. uniswap, etc.

4. On a side note, not sure why there's not an api that encrypts an nft on ipfs and only returns the watermarked version unless you send it a fee. or your the owner.

because it's not a real problem. The same way you can buy a mona lisa t-shirt in the gift shop and that doesn't steal any of the value away from the owner.

What you describe could be rather easily implemented, but why would anyone build or use that?

I guess was thinking like a CDN/istockphoto type marketplace.
Most (any?) smart contract platforms can theoretically bake in a concept of proxy upgrading or even decentralized governance over triggering the proxy into an upgraded contract. Sure it's complex now, but this is still very very new tech and those problems can easily be built upon and abstracted away.
Yes, 100%. It’s one reason why I mentioned Tezos: it has upgradability and upgrade governance built in from day one, which — if nothing else — I think is an interesting design point.
For instance, the primary USDC contract on Ethereum is a proxy contract — it’s upgradable by design.

By whom?

"Upgrading" a contract should require the approval of all parties to the contract.

Right. In the case of USDC, whoever has the private key to their proxy contract’s owner account. You’re trusting their engineering team to do the right thing.

(EDIT: if I’m reading my etherscan correctly, it’s account 0xfcb19e6a322b27c06842a71e8c725399f049ae3a with upgrade rights. Sorry, I’m in transit so tapping on my phone.)

USDC is upfront about being centralized.
"Centralized" is not the same as "terms alterable at sole discretion".

(Obvious Darth Vader reference).

In the case of USDC, it is: https://www.circle.com/en/legal/us-user-agreement

See 25. Amendments.

So you deploy a new contract and tell users why they should update. Then they can choose to use the new version or not. I don't think you really understand this idea.
What you’re describing does indeed happen — and in many cases I think it’s a good approach — but it’s not the same as what I’m talking about.

You might find these two blog posts useful:

1. OpenZeppelin’s post on the Ethereum proxy pattern: https://blog.openzeppelin.com/proxy-patterns/

2. USDC’s adventure in upgrading their contract: https://blog.coinbase.com/usdc-v2-upgrading-a-multi-billion-...

As a user who might prefer the first version of the code to the second, why should I be excited about a software system that can force me to use the upgraded version? That's a design flaw in the current Web -- when a Web service I rely on changes its behavior and goes out of business, I'm SOL. Web3, with immutable smart contracts that stay online, has the potential to fix this.
> ...instead focus on getting the code correct the first time

This is a threshold that real-world contracts don't typically have to achieve. They leave lots of details out, or vaguely defined, because the odds are low that that part of the contract will have to come into play, and it's not worth the extraordinary effort and time and expense to negotiate every tiny very-unlikely-to-happen case.

To quote Lawrence Lessig [1]: "Often obscurity is a real value. Obscurity is what you want... In principle we should be negotiating all of these [possible things that could happen to our deal]... What contracts do all the time [instead] is they create these fuzzy or vague or ambiguous places as a gamble... And if it turns out [that this .002% occurrence does] happen, we'll ask... a judge to figure them out.

Also, if you're just going to replace one immutable contract with another, you're back to meatspace and re-negotiation, which can be time consuming and expensive.

[1] MIT 15.S12 Blockchain and Money, Fall 2018. "Smart Contracts and DApps" https://youtu.be/JPkgJwJHYSc?t=3543

> This is a threshold that real-world contracts don't typically have to achieve

And boy does it show!

How do you debug your smart contract? Your users tell you their money got stolen out of it. I wish that was a joke.

> Also, if you're just going to replace one immutable contract with another, you're back to meatspace and re-negotiation, which can be time consuming and expensive.

Is upgrading them in place somehow better? At least by keeping the old systems around, the people who still get mileage out of them aren't sold up-river.

I think here you need to draw the comparison to an Irrevocable Letter of Credit (LOC) and those are intentionally set in stone.
It's not very, very hard currently though. It's just slightly more complex and more error prone. Most complex DeFi protocols are upgradeable by design these days.
Isn’t the proxy setup on ethereum a loophole in this tho? You just deploy a new contract and redirect the proxy