Proposal 22: Patch Governance & Rebuild Infrastructure

We are announcing the launch of Proposal 22!


PROPOSAL 22 IS THE ONLY LEGITIMATE PROPOSAL.

CONSIDER ANY OTHER CURRENT PROPOSAL THAT WE HAVE NOT ANNOUNCED MALICIOUS.


Context

This proposal seeks to patch the vulnerability which allowed the recent exploit to occur and also rebuilds infrastructure which was incapacitated by it (READ THE DESCRIPTION BELOW).

Familiarize yourself with the full context here:


Resources

Once you have familiarized yourself with the former, find resources regarding the Proposal below:

Proposal Repository

Proposal Contract Etherscan Link

New RelayerRegistry Implementation Etherscan Link

We will already state here in forward that relayers do not need to worry, their balances are preserved. The new RelayerRegistry implementation logic is equal to the logic of the “old” implementation.

New TornadoStakingRewards AdminUpgradeableProxy Etherscan Link

The new AdminUpgradeableProxy logic is equal to the logic of the “old” proxy.

New TornadoStakingRewards Implementation Etherscan Link

The new TornadoStakingRewards implementation logic is equal to the logic of the “old” implementation.

TornadoStakingRewards and RelayerRegistry Contract diff in comparison to Etherscan deployed

Display of the minimal (import, type, and not resolving ENS names in the constructor and rather just using addresses) changes.


Also find the inlined code of the top-level Proposal (meaning, only the proposal contract), below.

Proposal Contract Code
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import { LoopbackProxy } from "../v1/LoopbackProxy.sol";
import { AdminUpgradeableProxy } from "./proxy/AdminUpgradeableProxy.sol";

import { GovernancePatchUpgrade } from "./GovernancePatchUpgrade.sol";
import { TornadoStakingRewards } from "./TornadoStakingRewards.sol";
import { RelayerRegistry } from "./RelayerRegistry.sol";

/**
 * @notice Proposal which should patch governance against the metamorphic contract replacement vulnerability and also fix several issues which have appeared as a result of the attack.
 */
contract PatchProposal {
  // Address of the old staking proxy
  address public constant oldStakingProxyAddress = 0x2FC93484614a34f26F7970CBB94615bA109BB4bf;

  // Address of the registry proxy
  address public constant registryProxyAddress = 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2;

  // Address of the gas compensation vault
  address public constant gasCompensationVaultAddress = 0xFA4C1f3f7D5dd7c12a9Adb82Cd7dDA542E3d59ef;

  // Address of the user vault
  address public constant userVaultAddress = 0x2F50508a8a3D323B91336FA3eA6ae50E55f32185;

  // Address of the governance proxy
  address payable public constant governanceProxyAddress = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce;

  // Torn token
  IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);

  // The staking proxy (pointing to a new implementation (with same code)) that we've deployed
  address public immutable deployedStakingProxyContractAddress;

  // The registry implementation (with same code) that we've deployed
  address public immutable deployedRelayerRegistryImplementationAddress;

  constructor(
    address _deployedStakingProxyContractAddress,
    address _deployedRelayerRegistryImplementationAddress
  ) public {
    deployedStakingProxyContractAddress = _deployedStakingProxyContractAddress;
    deployedRelayerRegistryImplementationAddress = _deployedRelayerRegistryImplementationAddress;
  }

  /// @notice Function to execute the proposal.
  function executeProposal() external {
    // Get the old staking contract
    TornadoStakingRewards oldStaking = TornadoStakingRewards(oldStakingProxyAddress);

    // Get the small amount of TORN left
    oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking)));

    // Upgrade the registry proxy
    AdminUpgradeableProxy(payable(registryProxyAddress)).upgradeTo(
      deployedRelayerRegistryImplementationAddress
    );

    // Now upgrade the governance implementation to the vulnerability resistant one
    LoopbackProxy(governanceProxyAddress).upgradeTo(
      address(
        new GovernancePatchUpgrade(
          deployedStakingProxyContractAddress,
          gasCompensationVaultAddress,
          userVaultAddress
        )
      )
    );

    // Transfer TORN in compensation to the staking proxy
    TORN.transfer(deployedStakingProxyContractAddress, 94_092 ether);
  }
}

Description

The Proposal contents can be divided into two parts, patching the Governance contract (from GovernanceStakingUpgrade to GovernancePatchUpgrade and repairing infrastructure by redeploying 3 contracts and compensating Staking contract losses (see below).

In [0] you will find the code of the GovernancePatchUpgrade, which essentially introduces extra checks which prevent attacks by metamorphic contract deployments with self-destructs. This does not mean that Governance can in future not vote for a malicious proposal, any proposal can be malicious, but this makes it harder for a future proposal to hide it, and impossible to exploit this vulnerability.

The above upgrade is designed such, that it allows regular execution of all pre and post conditions, and respectively introduces extra stores and checks at the appropriate locations. The resistance against metamorphic contract attacks is proven by forked tests using the on-chain permissionless MetamorphicContractFactory.

Otherwise, and more importantly this Proposal REGISTERS three new contracts (which can be found in the links above), which we have deployed at the above mentioned addresses.

These contracts are minimally modified versions of the original contracts. I will now outline the reasoning for why they have to be redeployed.

The TornadoStakingRewards proxy must be redeployed because of broken state, because of this the RelayerRegistry must be redeployed because it depends on the TornadoStakingRewards AdminUpgradeableProxy as an immutable.

BUT, since the TornadoStakingRewards implementation depends on the RelayerRegistry in a circular fashion, also with an immutable, then the TornadoStakingRewards implementation must be too, redeployed.

I encourage the reader to verify assigned immutables for each contract for themselves.

The diffs (the differences of the contracts from the current Etherscan deployed versions) can be found in [1], the contracts (their code) can be found in [2]. If one want to compare the contracts, one could use the Linux patch utility, and apply the .diff files as patch files to the Etherscan deployed versions, and one should end up with the versions in the repository.

Part of repairing the infrastructure is ALSO moving (in [3]) 94,092 TORN from the Governance contract to the TornadoStakingRewards AdminUpgradeableProxy contract (the state contract, meaning, what you call the “Staking Contract”). This is to compensate rewards which have been also lost during the initial exploit.

The end result of these changes will be, as follows:

  • Governance will be patched against the vulnerability.
  • The staking infrastructure including rewards distribution will be repaired.

This proposal purposely does not include further actions, because we want to proposals as atomized as possible and fix the necessities.

I hope that with this we can restore the trust of the community in the security of our contracts, and we also state that we have learned any lessons to be learned including the necessity of full rigor in contract auditing and non-delegation of this work to anyone but ourselves, since no one can be counted on.

Footnotes

Links: [0], [1], [2], [3]


Creators, developers: T-Hax, Theo.

3 Likes

Can the staking rewards my address was supposed to receive before the hack be recorded in the new stake contract? In other words, can I withdraw my previous rewards?

1 Like

The community discussed a governance plan: change the stake contract to destroy the contract, the recharge torn behavior of the repeater is changed to burn, and the administrator abandons the reward. Do you agree? Our holders agree, and it is also a plan we have discussed together. In this way The code changes are very small. I just need to send the recharged torn of the stake contract relayer to 0x000000. Is it ok? No other changes are needed. If it is technically feasible, I hope you and the community can discuss and implement it together. Looking forward to reply

Why is burning necessary when the total issuance is far less than other coins?

The total supply of Bitcoin is 21 million, while the total supply of TORN is 10 million. The total supply is already too small, so burning them would be pointless

For example, rare gems are valuable because of their scarcity. However, too rare gems can lose value because demand decreases.

Burning does not always increase value

Currently, there is no demand for buying TORN tokens, so burning tokens will only further decrease its value. At this point, we need to find ways to increase the use of tokens and market them in various ways.

1 Like