An Update on the Contract Upgrade Feature

Hi all :wave:, I want (or: “have been asked by @ertemann”) to share a few notes about the code hash mechanism.

The code hash mechanism is crucial for input senders to determine which code is permitted to decrypt the input. In 2020, we were confronted with the challenge of preventing replay attacks, where malicious actors could replay an honest user’s input into a malicious contract, potentially exposing the plaintext input. Our solution was to include the code hash of the recipient contract within the encrypted input.

Here’s how it works: the enclave decrypts the input and computes sha256(wasm_bytes). If the code hash within the decrypted input doesn’t match the calculated code hash, the enclave rejects the call with an error. This security measure also applies to contract-to-contract calls, as they are also encrypted.

It’s essential to understand that the enclave cannot directly access a contract’s information from the chain’s storage. This is because such information can be manipulated, as demonstrated by @amiller. As a result, the code hash mechanism remains an implementation detail that we unfortunately couldn’t abstract away for users.

This mechanism can be somewhat inconvenient, especially when dealing with UIs. Additionally, it becomes more complex during contract-to-contract calls, where discovering the other contract’s code hash isn’t always straightforward, leading to convoluted code patterns.

When we announced the hardcoded admins feature as part of the upcoming v1.11 a few weeks ago, @baedrik smartly pointed out a potential issue. If an old contract A has contract B’s code hash stored in its state, it would fail to call B if B undergoes an upgrade. This is because old contracts assumed that pre-v1.11 contracts couldn’t be upgraded, making their code hash immutable.

To address this, we introduced an edge case. All contracts in the hardcoded admins list can also be called with their initial code hash. While this solution resolves the problem, it introduces an attack vector where an old input can be replayed on newer code, even if it was intended to run with the initial code. We’ve decided to accept this as a potential risk for two primary reasons:

  1. We consider contracts on the hardcoded admins list low-risk for such attacks since governance has approved all the admins.
  2. In a future upgrade (ideally v1.12 :crossed_fingers:) we’ll add merkle proofs in the enclave. This will allow the enclave to verify any data stored on the chain’s storage. This mechanism is mainly intended for verification of read_db() operations by contracts, but another key benefit is that transaction inputs will no longer need to include the code hash. Instead, the enclave will query the chain for it. For instance, a user signs a transaction calling contract A, and the enclave can prove that sha256(wasm_bytes) matches the contract’s info stored on-chain.

@baedrik raised another valid point regarding the removal of the code hash requirement in inputs. In the future, contracts may wish to enforce the code hash of the callee contract during contract-to-contract calls. This measure prevents further communication in case the callee has been upgraded, potentially affecting the caller’s behavior unexpectedly. This feature will be added when we remove the requirement to specify the code hash in inputs.

I hope this clarifies things and also provides a glimpse into the future. :blush:

Anyway, back to coding… :ninja:

4 Likes