Skip to content

Commit

Permalink
Make SFC and NodeDriver(Auth) upgradable (#96)
Browse files Browse the repository at this point in the history
* Initial configuration

* Fix tests

* Disable solhint warning

* Add proxy test

* Replace Ownable and Initializable with OZ implementation

* Make NodeDriver upgradable

* Make NodeDriverAuth upgradable

* Add constructor for constants manager
  • Loading branch information
Mike-CZ authored Nov 11, 2024
1 parent 2c2a960 commit 8c700e0
Show file tree
Hide file tree
Showing 15 changed files with 818 additions and 237 deletions.
57 changes: 0 additions & 57 deletions contracts/common/Initializable.sol

This file was deleted.

96 changes: 0 additions & 96 deletions contracts/ownership/Ownable.sol

This file was deleted.

8 changes: 4 additions & 4 deletions contracts/sfc/ConstantsManager.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.27;

import {Ownable} from "../ownership/Ownable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Decimal} from "../common/Decimal.sol";

/**
* @custom:security-contact security@fantom.foundation
*/
contract ConstantsManager is Ownable {
contract ConstantsManager is OwnableUpgradeable {
// Minimum amount of stake for a validator, i.e., 500000 FTM
uint256 public minSelfStake;
// Maximum ratio of delegations a validator can have, say, 15 times of self-stake
Expand Down Expand Up @@ -47,8 +47,8 @@ contract ConstantsManager is Ownable {
*/
error ValueTooLarge();

function initialize() external initializer {
Ownable.initialize(msg.sender);
constructor(address owner) initializer {
__Ownable_init(owner);
}

function updateMinSelfStake(uint256 v) external virtual onlyOwner {
Expand Down
8 changes: 4 additions & 4 deletions contracts/sfc/NetworkInitializer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
pragma solidity 0.8.27;

import {ISFC} from "../interfaces/ISFC.sol";
import {NodeDriver, NodeDriverAuth} from "./NodeDriver.sol";
import {NodeDriver} from "./NodeDriver.sol";
import {NodeDriverAuth} from "./NodeDriverAuth.sol";
import {ConstantsManager} from "./ConstantsManager.sol";
import {Decimal} from "../common/Decimal.sol";

Expand All @@ -20,11 +21,10 @@ contract NetworkInitializer {
address _evmWriter,
address _owner
) external {
NodeDriver(_driver).initialize(_auth, _evmWriter);
NodeDriver(_driver).initialize(_auth, _evmWriter, _owner);
NodeDriverAuth(_auth).initialize(_sfc, _driver, _owner);

ConstantsManager consts = new ConstantsManager();
consts.initialize();
ConstantsManager consts = new ConstantsManager(address(this));
consts.updateMinSelfStake(500000 * 1e18);
consts.updateMaxDelegatedRatio(16 * Decimal.unit());
consts.updateValidatorCommission((15 * Decimal.unit()) / 100);
Expand Down
22 changes: 10 additions & 12 deletions contracts/sfc/NodeDriver.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.27;

import {Initializable} from "../common/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {NodeDriverAuth} from "./NodeDriverAuth.sol";
import {IEVMWriter} from "../interfaces/IEVMWriter.sol";
import {INodeDriver} from "../interfaces/INodeDriver.sol";
Expand All @@ -12,21 +13,13 @@ import {INodeDriver} from "../interfaces/INodeDriver.sol";
* @dev Methods with onlyNode modifier are called by Sonic internal txs during epoch sealing.
* @custom:security-contact security@fantom.foundation
*/
contract NodeDriver is Initializable, INodeDriver {
contract NodeDriver is OwnableUpgradeable, UUPSUpgradeable, INodeDriver {
NodeDriverAuth internal backend;
IEVMWriter internal evmWriter;

error NotNode();
error NotBackend();

event UpdatedBackend(address indexed backend);

/// NodeDriverAuth can replace itself
function setBackend(address _backend) external onlyBackend {
emit UpdatedBackend(_backend);
backend = NodeDriverAuth(_backend);
}

/// Callable only by NodeDriverAuth (which mediates calls from SFC and from admins)
modifier onlyBackend() {
if (msg.sender != address(backend)) {
Expand All @@ -44,12 +37,17 @@ contract NodeDriver is Initializable, INodeDriver {

/// Initialization is called only once, after the contract deployment.
/// Because the contract code is written directly into genesis, constructor cannot be used.
function initialize(address _backend, address _evmWriterAddress) external initializer {
function initialize(address _backend, address _evmWriterAddress, address _owner) external initializer {
__Ownable_init(_owner);
__UUPSUpgradeable_init();
backend = NodeDriverAuth(_backend);
emit UpdatedBackend(_backend);
evmWriter = IEVMWriter(_evmWriterAddress);
}

/// Override the upgrade authorization check to allow upgrades only from the owner.
// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address) internal override onlyOwner {}

function setBalance(address acc, uint256 value) external onlyBackend {
evmWriter.setBalance(acc, value);
}
Expand Down
18 changes: 9 additions & 9 deletions contracts/sfc/NodeDriverAuth.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.27;

import {Initializable} from "../common/Initializable.sol";
import {Ownable} from "../ownership/Ownable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {ISFC} from "../interfaces/ISFC.sol";
import {NodeDriver} from "./NodeDriver.sol";
import {INodeDriverExecutable} from "../interfaces/INodeDriverExecutable.sol";

/**
* @custom:security-contact security@fantom.foundation
*/
contract NodeDriverAuth is Initializable, Ownable {
contract NodeDriverAuth is OwnableUpgradeable, UUPSUpgradeable {
ISFC internal sfc;
NodeDriver internal driver;

Expand All @@ -23,11 +23,16 @@ contract NodeDriverAuth is Initializable, Ownable {

// Initialize NodeDriverAuth, NodeDriver and SFC in one call to allow fewer genesis transactions
function initialize(address payable _sfc, address _driver, address _owner) external initializer {
Ownable.initialize(_owner);
__Ownable_init(_owner);
__UUPSUpgradeable_init();
driver = NodeDriver(_driver);
sfc = ISFC(_sfc);
}

/// Override the upgrade authorization check to allow upgrades only from the owner.
// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address) internal override onlyOwner {}

/// Callable only by SFC contract.
modifier onlySFC() {
if (msg.sender != address(sfc)) {
Expand All @@ -44,11 +49,6 @@ contract NodeDriverAuth is Initializable, Ownable {
_;
}

/// Change NodeDriverAuth used by NodeDriver. Callable by network admin.
function migrateTo(address newDriverAuth) external onlyOwner {
driver.setBackend(newDriverAuth);
}

function _execute(address executable, address newOwner, bytes32 selfCodeHash, bytes32 driverCodeHash) internal {
_transferOwnership(executable);
INodeDriverExecutable(executable).execute();
Expand Down
13 changes: 9 additions & 4 deletions contracts/sfc/SFC.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.27;

import {Ownable} from "../ownership/Ownable.sol";
import {Initializable} from "../common/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Decimal} from "../common/Decimal.sol";
import {NodeDriverAuth} from "./NodeDriverAuth.sol";
import {ConstantsManager} from "./ConstantsManager.sol";
Expand All @@ -13,7 +13,7 @@ import {Version} from "../version/Version.sol";
* @notice The SFC maintains a list of validators and delegators and distributes rewards to them.
* @custom:security-contact security@fantom.foundation
*/
contract SFC is Initializable, Ownable, Version {
contract SFC is OwnableUpgradeable, UUPSUpgradeable, Version {
uint256 internal constant OK_STATUS = 0;
uint256 internal constant WITHDRAWN_BIT = 1;
uint256 internal constant OFFLINE_BIT = 1 << 3;
Expand Down Expand Up @@ -224,14 +224,19 @@ contract SFC is Initializable, Ownable, Version {
address _c,
address owner
) external initializer {
Ownable.initialize(owner);
__Ownable_init(owner);
__UUPSUpgradeable_init();
currentSealedEpoch = sealedEpoch;
node = NodeDriverAuth(nodeDriver);
c = ConstantsManager(_c);
totalSupply = _totalSupply;
getEpochSnapshot[sealedEpoch].endTime = _now();
}

/// Override the upgrade authorization check to allow upgrades only from the owner.
// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address) internal override onlyOwner {}

/// Receive fallback to revert transfers.
receive() external payable {
revert TransfersNotAllowed();
Expand Down
2 changes: 2 additions & 0 deletions contracts/test/UnitTestConstantsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity 0.8.27;
import {ConstantsManager} from "../sfc/ConstantsManager.sol";

contract UnitTestConstantsManager is ConstantsManager {
constructor(address owner) ConstantsManager(owner) {}

function updateMinSelfStake(uint256 v) external override onlyOwner {
minSelfStake = v;
}
Expand Down
5 changes: 2 additions & 3 deletions contracts/test/UnitTestSFC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,10 @@ contract UnitTestNetworkInitializer {
address _evmWriter,
address _owner
) external {
NodeDriver(_driver).initialize(_auth, _evmWriter);
NodeDriver(_driver).initialize(_auth, _evmWriter, _owner);
NodeDriverAuth(_auth).initialize(_sfc, _driver, _owner);

UnitTestConstantsManager consts = new UnitTestConstantsManager();
consts.initialize();
UnitTestConstantsManager consts = new UnitTestConstantsManager(address(this));
consts.updateMinSelfStake(0.3175000 * 1e18);
consts.updateMaxDelegatedRatio(16 * Decimal.unit());
consts.updateValidatorCommission((15 * Decimal.unit()) / 100);
Expand Down
3 changes: 2 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { HardhatUserConfig } from 'hardhat/config';
import * as dotenv from 'dotenv';
import '@nomicfoundation/hardhat-chai-matchers';
import '@nomicfoundation/hardhat-ethers';
import '@openzeppelin/hardhat-upgrades';
import '@typechain/hardhat';
import 'hardhat-contract-sizer';
import 'hardhat-gas-reporter';
import 'solidity-coverage';
import '@typechain/hardhat';

dotenv.config();

Expand Down
Loading

0 comments on commit 8c700e0

Please sign in to comment.