# Dappnode APM Surgery ## Context These are the two repos to upgrade: - [besu.public.dappnode.eth](https://etherscan.io/address/0x2914045962C6ea7706311D47937Be373b71A6060) (PM: [nabsku.eth](https://etherscan.io/address/0x9cbBCD3B4129B1c00F0cd851BAf118ebb0c4F168)) - [Kernel](https://etherscan.io/address/0x0F3b8eC182DEee2381A9041e30f65f10098A3B91) (PM: [baylina.eth](https://etherscan.io/address/0x1dba1131000664b884a1ba238464159892252d3a)) - [ACL](https://etherscan.io/address/0xFCb2C44E61031AE29e5c54A700FB6B4FB430dA4C) (PM: [baylina.eth](https://etherscan.io/address/0x1dba1131000664b884a1ba238464159892252d3a)) - [goerli-besu.dnp.dappnode.eth](https://etherscan.io/address/0xf508C8Fc3F121B18794489659a5e2CcC255E9CA2) (PM: [nabsku.eth](https://etherscan.io/address/0x9cbBCD3B4129B1c00F0cd851BAf118ebb0c4F168)) - [Kernel](https://etherscan.io/address/0xFCdEDC0397603346788b2567fb5E6d9Fd2AEdF4C) (PM: [baylina.eth](https://etherscan.io/address/0x1dba1131000664b884a1ba238464159892252d3a)) - [ACL](https://etherscan.io/address/0x89d0A07b792754460Faa49e57437B40aA33FB757) (PM: [baylina.eth](https://etherscan.io/address/0x1dba1131000664b884a1ba238464159892252d3a)) ## Actors Three actors will have to intervene in order to change the Besu repo admin: - **Jordi Baylina** (0x1dba...2d3a) as the deployer of the Dappnode Package Manager. - **DappNode multisig** (0x5339...5efc4) signers as the future owners of the Dappnode Package Manager. - **New Besu Repo Admin** (0xf359...bf1c1) as the new owner of the repo. ## Steps 1. **Jordi Baylina** transfers the Dappnode Package Manager permissions to the **DappNode multisig**. - **Jordi Baylina** should execute the following script: [Transfer DappNode Package Manager Ownership](https://evmcrispr.com/#/terminal/QmbUhbzGqviChgWK3SUFiQL9kzP3FrfktoiWERo2c88fVd). 2. The **Dappnode Multisig** executes a batch transaction in which: - For each public.dappnode.eth and dnp.dappnode.eth: - As permission manager of the Kernel and the ACL permissions, it gives itself permission to execute functions in them. - It upgrades the ACL to the Surgery contract. - It sets a bypass with the previous ACL implementation. - It modifies an ACL storage slot so the permission manager of the Besu Repo is the **DappNode Multisig**. - It downgrades the ACL so it does not point to the Surgery anymore. - It revokes the permissions from the previous admin **nabsuku.eth**. - It sets the **New Besu Repo Admin** as the permission manager. 3. The **New Besu Repo Admin** should now be able to publish new versions of Besu in both DappNode Package Managers. - This is tested with EVMcrispr + Tenderly: [Dappnode Surgery](https://evmcrispr.com/#/terminal/Qmd3Yg2i6bnqdrrbLP9bBkaxeHgNqER42qGFr7APGnkMXP). ## Update: May 1, 2024 Jordi executed the script so the repos now belong to the DappNode Safe. We decided to separate the script in two batches, one for each repo: - [Besu Repo script](https://evmcrispr.com/#/terminal/QmdtffGxJLrTVDQ4ewecbVMWULCxDtBqtV73AyVSko8Gpo) - [Simulation](https://dashboard.tenderly.co/explorer/fork/39893525-2a4f-4e88-89b6-244342d1730a/transactions) - [Txs Batch](https://ipfs.blossom.software/ipfs/QmfEs6QtGXZENoG4fHU4qsQD3TpNBZ2KrWjAJ1qBeajVt8/dappnode-surgery.json) - [Goerli Besu Repo script](https://evmcrispr.com/#/terminal/QmYRURG7wVwkjfB6xt9bPzoLWPbzC2uzjTyqn9r75irYeM) - [Simulation](https://dashboard.tenderly.co/explorer/fork/a20c7581-53da-4432-bbdc-7934ff0abd04/transactions) - [Txs Batch](https://ipfs.blossom.software/ipfs/QmfEs6QtGXZENoG4fHU4qsQD3TpNBZ2KrWjAJ1qBeajVt8/dappnode-surgery-goerli.json) ## Surgery Smart Contract The following smart contract [deployed on mainnet](https://etherscan.io/address/0xfb0c6eac700d5ba5e5c7ccf13a2b38d2f31776bc#code) is used to make the surgery: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract Surgery { // The storage slot where the address of the bypass contract is stored // uint private constant BYPASS_SLOT = uint(keccak256("surgery.bypass")); uint private constant BYPASS_SLOT = 0x53edf09d858352d72f64ef79097f9ffaee955e2757de83b69ff2afe447984250; event Operation(uint256 slot, uint256 value, uint256 offset, uint256 size); event BypassSet(address target); function operate(uint256 slot, uint256 value) external { assembly { sstore(slot, value) } emit Operation(slot, value, 0, 32); } function getBypass() public view returns (address bypass) { assembly { bypass := sload(BYPASS_SLOT) } } function setBypass(address _target) public { assembly { sstore(BYPASS_SLOT, _target) } emit BypassSet(_target); } function _delegate(address _implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. // calldatacopy(t, f, s) - copy s bytes from calldata at position f to mem at position t // calldatasize() - size of call data in bytes calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. // delegatecall(g, a, in, insize, out, outsize) - // - call contract at address a // - with input mem[in…(in+insize)) // - providing g gas // - and output area mem[out…(out+outsize)) // - returning 0 on error (eg. out of gas) and 1 on success let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0) // Copy the returned data. // returndatacopy(t, f, s) - copy s bytes from returndata at position f to mem at position t // returndatasize() - size of the last returndata returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { // revert(p, s) - end execution, revert state changes, return data mem[p…(p+s)) revert(0, returndatasize()) } default { // return(p, s) - end execution, return data mem[p…(p+s)) return(0, returndatasize()) } } } function _fallback() private { _delegate(getBypass()); } fallback() external payable { _fallback(); } receive() external payable { _fallback(); } } ```