Skip to content
This repository has been archived by the owner on Aug 13, 2024. It is now read-only.

Commit

Permalink
Update Registry
Browse files Browse the repository at this point in the history
  • Loading branch information
hyeonLewis authored and Lewis committed Sep 27, 2023
1 parent fa942df commit 2d89f63
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 64 deletions.
83 changes: 19 additions & 64 deletions KIPs/kip-149.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,14 @@ The smart contract will have the following features:

#### 1. Registry

Since it needs data for pre-deployed system contracts right after deployment, all addresses for pre-deployed system contracts must be hard-coded in the Registry and need logic in the `getActiveContract` function to return them properly. The Registry is divided into base and network-specific parts to implement this. The `BaseRegistry` handles common logic for all networks, and each network-specific implementation has to manage pre-deployed system contracts, as shown in the following figure.
The registry should have data for the pre-deployed system contracts as soon as it is deployed. It will be done by state injection, which injects data into the Registry directly using the `state.SetState`. The implementation for this is discussed in more detail [here](#backward-compatibility).

![](../assets/kip-149/RegistryStructure.png)

#### Interface of BaseRegistry
#### Interface of Registry

```solidity
pragma solidity ^0.8.0;
interface IBaseRegistry {
interface Registry {
/* ========== TYPES ========== */
/// @dev Struct of system contracts
struct Record {
Expand All @@ -73,18 +71,11 @@ interface IBaseRegistry {
}
/* ========== EVENTS ========== */
/// @dev Emitted when the Registry is constructed by `constructContract`.
event ConstructContract(address indexed owner);
/// @dev Emitted when the contract owner is updated by `transferOwnership`.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @dev Emitted when a new system contract is registered.
event Registered(string name, address indexed addr, uint256 indexed activation);
/* ========== CONSTRUCTOR ========== */
/// @dev Constructs the Registry after deployment.
function constructContract(address governance) external;
/* ========== MUTATORS ========== */
/// @dev Registers a new system contract.
function register(string memory name, address addr, uint256 activation) external;
Expand All @@ -103,10 +94,10 @@ interface IBaseRegistry {
function getActiveAddr(string memory name) external returns (address);
/// @dev Returns all system contracts registered as name.
function getAllContracts(string memory name) external view returns (Record[] memory);
function getAllRecords(string memory name) external view returns (Record[] memory);
/// @dev Returns all names of registered system contracts.
function getAllContractNames() external view returns (string[] memory);
function getAllNames() external view returns (string[] memory);
/// @dev Returns owner of contract.
function owner() external view returns (address);
Expand Down Expand Up @@ -161,7 +152,7 @@ When upgrading system contracts, its logic contract will be changed by governanc

#### Replace System Contracts

If current system contract updates can’t be done by upgrading the logic contract, it must be replaced with the newly deployed system contract. The predecessor doesn’t need to be explicitly deprecated since it will be implicitly replaced and deprecated by a new system contract.
If current system contract updates can’t be done by upgrading the logic contract, it must be replaced with the newly deployed system contract. The predecessor doesn’t need to be explicitly deprecated since a new system contract will implicitly replace and deprecate it.

![](../assets/kip-149/ReplacementProcess.png)

Expand All @@ -177,7 +168,8 @@ In the Chain Config, the following field is introduced. All node operators in a

#### Execution

The Registry deployment is executed at the `Finalize` function, which means the end of the block processing process. It reads the reserved address and runtime bytecode and deploys the Registry by the bytecode injection.
The Registry deployment is executed at the `Finalize` function, which means the end of the block processing process. It reads the reserved address and runtime bytecode and deploys the Registry by the bytecode injection. Also, it injects the state for pre-deployed system contracts here.


```go
// Deploy the registry contract if the block is the kip-149 fork block
Expand All @@ -202,6 +194,16 @@ A Klaytn node will have a resolver to read the active addresses of system contra

In the future, all system contracts will be registered in the Registry, and a Klaytn node will read the active addresses of system contracts from the Registry. Not only for a Klaytn node but also for other ecosystem participants who will use the registry to read the system contracts they need, meaning the registry should be registered at an easily accessible reserved address.

### State Injection for Pre-deployed System Contracts

The Registry should hold data for pre-deployed system contracts as soon as it is deployed. However, pre-deployed system contracts have been deployed and managed differently by networks. (e.g., the Voting contract on the Klaytn cypress network is currently not used on the Klaytn baobab network.) To handle this, there are two main approaches:

1. Use different Registry contract, which holds different hard-coded pre-deployed system contracts.

2. Use state injection, which injects state for pre-deployed system contracts by the `state.SetState`.

In the first approach, the Registry contract should have different codes by the network, requiring direct code modification(e.g., add/remove hard-coded system contracts, modify if-else statement). It can cause potential security vulnerabilities. On the other hand, the second approach is much safer because it's more structured and doesn’t require modifying any code. It can also set the necessary configuration without working with the additional contract's constructor.

### Separate Data and Logic Contract

This proxy pattern simplifies the process of system contract update because the existing data can be used even if the logic contract is changed. The main issue of the proxy pattern is the centralization and potential private key loss. But delegating ownership to upgrade a logic contract to Governance can solve those problems.
Expand All @@ -210,54 +212,7 @@ This proxy pattern simplifies the process of system contract update because the

### Deployed System Contracts

As explained in [Registry](#1-registry) section, it consists of base and network-specific registries. The below code is an example of `RegistryCypress`.

```solidity
// Pre-deployed system contracts
address public constant ADDRESS_BOOK = 0x0000000000000000000000000000000000000400;
address public constant VOTING = 0xcA4Ef926634A530f12e55A0aEE87F195A7B22Aa3;
address public constant TREASURY_REBALANCE = 0xD5ad6D61Dd87EdabE2332607C328f5cc96aeCB95;
address public constant STAKING_TRACKER = 0x9b8688d616D3D5180d29520c6a0E28582E82BF4d;
bytes32 public constant ADDRESS_BOOK_KEY = keccak256(abi.encodePacked("AddressBook"));
bytes32 public constant VOTING_KEY = keccak256(abi.encodePacked("Voting"));
bytes32 public constant TREASURY_REBALANCE_KEY = keccak256(abi.encodePacked("TreasuryRebalance"));
bytes32 public constant STAKING_TRACKER_KEY = keccak256(abi.encodePacked("StakingTracker"));
/**
* @dev Returns the address of contract if active at current block including pre-deployed system contract.
* @param name Name of the system contract.
*/
function getActiveContract(string memory name) public view override returns (address) {
bytes32 key = keccak256(abi.encodePacked(name));
uint256 activation;
uint256 length = records[name].length;
if (length > 0) {
activation = records[name][length - 1].activation;
}
address addr = super.getActiveAddr(name);
if (addr != address(0)) {
return addr;
} else {
if (key == ADDRESS_BOOK_KEY) {
return activation != 0 && activation <= block.number ? address(0) : ADDRESS_BOOK;
} else if (key == VOTING_KEY) {
return activation != 0 && activation <= block.number ? address(0) : VOTING;
} else if (key == TREASURY_REBALANCE_KEY) {
return activation != 0 && activation <= block.number ? address(0) : TREASURY_REBALANCE;
} else if (key == STAKING_TRACKER_KEY) {
return activation != 0 && activation <= block.number ? address(0) : STAKING_TRACKER;
} else {
return addr;
}
}
}
```

Since pre-deployed system contracts are different by the network (Baobab, ServiceChains), each implementation for the network-specific registry is different by the network. If there's no pre-deployed system contract, using `BaseRegistry` is enough.
It needs to calculate storage slots for variables in the Registry contract for state injection. It will follow the [solidity layout rule](https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html).

## Implementation

Expand Down
Binary file removed assets/kip-149/RegistryStructure.png
Binary file not shown.

0 comments on commit 2d89f63

Please sign in to comment.