Skip to content

Commit

Permalink
Merge branch 'main' into feature/batch-revert-protection
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha authored Dec 16, 2024
2 parents c3951cb + ec5f599 commit 4ceef93
Show file tree
Hide file tree
Showing 15 changed files with 229 additions and 48 deletions.
Binary file added packages/bridge-ui/static/coins/crvUSD.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/bridge-ui/static/coins/curve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/bridge-ui/static/coins/scrvUSD.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ This means that you are not able to connect to the RPC endpoint. Check if you ha

### No contract code at given address

Please ensure your node is running on the correct network and is fully up to date. If the problem still persists, your underlying L1 node is still syncing. Please wait until your L1 node is fully synced before starting your L2 node.
Please ensure your node is running on the correct network and is fully up-to-date. If the problem still persists, your underlying L1 node is still syncing. Please wait until your L1 node is fully synced before starting your L2 node.

### Database contains incompatible genesis

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: This guide will help you start up a Taiko node.

import { Aside, Card, Steps, Tabs, TabItem } from "@astrojs/starlight/components";

This tutorial explains how to run an Taiko node for Mainnet from source code.
This tutorial explains how to run a Taiko node for Mainnet from source code.

## Building the Source Code

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ This guide will help you start up a Taiko RPC node using [simple-taiko-node](htt

It's recommended to [run a local L1 node](/guides/node-operators/run-an-ethereum-testnet-node) but you browse around for other RPC Providers. Keep in mind they will **eventually rate limit your node** and it will stop syncing, so a local L1 node is recommended for a proper setup.

For Mainnet, the L1 Node will need to be an Ethereum node; for Testnet, the L1 Node will need to be a Etherem testnet (Holesky) node.
For Mainnet, the L1 Node will need to be an Ethereum node; for Testnet, the L1 Node will need to be an Etherem testnet (Holesky) node.
</Aside>

Next, you will set the L1 node endpoints. If you are running a local L1 node, you cannot reference the L1 endpoints as `http://127.0.0.1:8545`, `ws://127.0.0.1:8546` and `http://127.0.0.1:5052` because that is local to inside the simple-taiko-node Docker networking. Instead you can try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: This guide will help you start up a Testnet (Hekla) Taiko node.

import { Aside, Card, Steps, Tabs, TabItem } from "@astrojs/starlight/components";

This tutorial explains how to run an Taiko node for our testnet Hekla from source code.
This tutorial explains how to run a Taiko node for our testnet Hekla from source code.

## Building the Source Code

Expand Down
21 changes: 21 additions & 0 deletions packages/protocol/contract_layout_layer1.md
Original file line number Diff line number Diff line change
Expand Up @@ -1855,3 +1855,24 @@
╰-------------------------+-------------------------------------------------+------+--------+-------+------------------------------------------------------------╯


## ForkManager

╭---------------+-------------+------+--------+-------+---------------------------------------------------╮
| Name | Type | Slot | Offset | Bytes | Contract |
+=========================================================================================================+
| _initialized | uint8 | 0 | 0 | 1 | ForkManager |
|---------------+-------------+------+--------+-------+---------------------------------------------------|
| _initializing | bool | 0 | 1 | 1 | ForkManager |
|---------------+-------------+------+--------+-------+---------------------------------------------------|
| __gap | uint256[50] | 1 | 0 | 1600 | ForkManager |
|---------------+-------------+------+--------+-------+---------------------------------------------------|
| _owner | address | 51 | 0 | 20 | ForkManager |
|---------------+-------------+------+--------+-------+---------------------------------------------------|
| __gap | uint256[49] | 52 | 0 | 1568 | ForkManager |
|---------------+-------------+------+--------+-------+---------------------------------------------------|
| _pendingOwner | address | 101 | 0 | 20 | ForkManager |
|---------------+-------------+------+--------+-------+---------------------------------------------------|
| __gap | uint256[49] | 102 | 0 | 1568 | ForkManager |
╰---------------+-------------+------+--------+-------+---------------------------------------------------╯


70 changes: 70 additions & 0 deletions packages/protocol/contracts/layer1/fork/ForkManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

/// @title ForkManager
/// @custom:security-contact security@taiko.xyz
/// @notice This contract serves as a base contract for managing up to two forks within the Taiko
/// protocol. By default, all function calls are routed to the newFork address.
/// Sub-contracts should override the shouldRouteToOldFork function to route specific function calls
/// to the old fork address.
/// These sub-contracts should be placed between a proxy and the actual fork implementations. When
/// calling upgradeTo, the proxy should always upgrade to a new ForkManager implementation, not an
/// actual fork implementation.
/// It is strongly advised to name functions differently for the same functionality across the two
/// forks, as it is not possible to route the same function to two different forks.
///
/// +--> newFork
/// PROXY -> FORK_MANAGER --|
/// +--> oldFork
contract ForkManager is UUPSUpgradeable, Ownable2StepUpgradeable {
address public immutable oldFork;
address public immutable newFork;

error ForkAddressIsZero();
error InvalidParams();

constructor(address _oldFork, address _currFork) {
require(_currFork != address(0) && _currFork != _oldFork, InvalidParams());
oldFork = _oldFork;
newFork = _currFork;
}

fallback() external payable virtual {
_fallback();
}

receive() external payable virtual {
_fallback();
}

function isForkManager() public pure returns (bool) {
return true;
}

function _fallback() internal virtual {
address fork = shouldRouteToOldFork(msg.sig) ? oldFork : newFork;
require(fork != address(0), ForkAddressIsZero());

assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), fork, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())

switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}

function _authorizeUpgrade(address) internal virtual override onlyOwner { }

/// @notice Determines if the call should be routed to the old fork.
/// @dev This function is intended to be overridden in derived contracts to provide custom
/// routing logic.
/// @param _selector The function selector of the call.
/// @return A boolean value indicating whether the call should be routed to the old fork.
function shouldRouteToOldFork(bytes4 _selector) internal pure virtual returns (bool) { }
}
10 changes: 10 additions & 0 deletions packages/protocol/contracts/layer1/hekla/HeklaTaikoToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ contract HeklaTaikoToken is EssentialContract, ERC20SnapshotUpgradeable, ERC20Vo
return super.transferFrom(_from, _to, _amount);
}

function clock() public view override returns (uint48) {
return SafeCastUpgradeable.toUint48(block.timestamp);
}

// solhint-disable-next-line func-name-mixedcase
function CLOCK_MODE() public pure override returns (string memory) {
// See https://eips.ethereum.org/EIPS/eip-6372
return "mode=timestamp";
}

function name() public pure override returns (string memory) {
return "Taiko Token";
}
Expand Down
3 changes: 2 additions & 1 deletion packages/protocol/deployments/hekla-contract-logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@
### taiko_token

- proxy: `0x6490E12d480549D333499236fF2Ba6676C296011`
- impl: `0x01BB2fD6D80942CE95B43c1322530fe690F2bc0e`
- impl: `0xD826bb700EAEdD6E83C32681f95a35Ac7F290104`
- owner: `0x1D2D1bb9D180541E88a6a682aCf3f61c1605B190`
- logs:
- deployed on Mar 29, 2024 at commit `b341a68d5`
- upgraded on Jun 18, 2024, added `batchTransfer` method.
- transferred ownership on Jul 8, 2024
- upgraded on Dec 16, 2024 at commit `ccc9500`

### signal_service

Expand Down
1 change: 1 addition & 0 deletions packages/protocol/script/gen-layouts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ contracts_layer1=(
"contracts/layer1/team/tokenunlock/TokenUnlock.sol:TokenUnlock"
"contracts/layer1/provers/ProverSet.sol:ProverSet"
"contracts/layer1/provers/GuardianProver.sol:GuardianProver"
"contracts/layer1/fork/ForkManager.sol:ForkManager"
)

# Layer 2 contracts
Expand Down
69 changes: 69 additions & 0 deletions packages/protocol/test/layer1/fork/ForkManager.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "../TaikoL1Test.sol";
import "src/layer1/fork/ForkManager.sol";

contract Fork is EssentialContract {
bytes32 private immutable __name;

constructor(bytes32 _name) {
__name = _name;
}

function init() external initializer {
__Essential_init(msg.sender);
}

function name() public view returns (bytes32) {
return __name;
}
}

contract ForkManager_RouteToOldFork is ForkManager {
constructor(address _fork1, address _fork2) ForkManager(_fork1, _fork2) { }

function shouldRouteToOldFork(bytes4 _selector) internal pure override returns (bool) {
return _selector == Fork.name.selector;
}
}

contract TestForkManager is TaikoL1Test {
address fork1 = address(new Fork("fork1"));
address fork2 = address(new Fork("fork2"));

function test_ForkManager_default_routing() public {
address proxy = deployProxy({
name: "main_proxy",
impl: address(new ForkManager(address(0), fork1)),
data: abi.encodeCall(Fork.init, ())
});

assertTrue(ForkManager(payable(proxy)).isForkManager());
assertEq(Fork(proxy).name(), "fork1");

// If we upgrade the proxy's impl to a fork, then alling isForkManager will throw,
// so we should never do this in production.
Fork(proxy).upgradeTo(fork2);
vm.expectRevert();
ForkManager(payable(proxy)).isForkManager();

Fork(proxy).upgradeTo(address(new ForkManager(fork1, fork2)));
assertEq(Fork(proxy).name(), "fork2");
}

function test_ForkManager_routing_to_old_fork() public {
address proxy = deployProxy({
name: "main_proxy",
impl: address(new ForkManager_RouteToOldFork(fork1, fork2)),
data: abi.encodeCall(Fork.init, ())
});

assertTrue(ForkManager(payable(proxy)).isForkManager());
assertEq(Fork(proxy).name(), "fork1");

Fork(proxy).upgradeTo(address(new ForkManager(fork1, fork2)));
assertTrue(ForkManager(payable(proxy)).isForkManager());
assertEq(Fork(proxy).name(), "fork2");
}
}
18 changes: 17 additions & 1 deletion packages/taiko-client/pkg/rpc/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rpc
import (
"context"
"errors"
"fmt"
"math/big"
"time"

Expand Down Expand Up @@ -164,7 +165,7 @@ func (c *EthClient) BatchHeadersByNumbers(ctx context.Context, numbers []*big.In
for i, blockNum := range numbers {
reqs[i] = rpc.BatchElem{
Method: "eth_getBlockByNumber",
Args: []interface{}{blockNum, false},
Args: []interface{}{toBlockNumArg(blockNum), false},
Result: &results[i],
}
}
Expand All @@ -180,6 +181,21 @@ func (c *EthClient) BatchHeadersByNumbers(ctx context.Context, numbers []*big.In
return results, nil
}

func toBlockNumArg(number *big.Int) string {
if number == nil {
return "latest"
}
if number.Sign() >= 0 {
return hexutil.EncodeBig(number)
}
// It's negative.
if number.IsInt64() {
return rpc.BlockNumber(number.Int64()).String()
}
// It's negative and large, which is invalid.
return fmt.Sprintf("<invalid %d>", number)
}

// TransactionByHash returns the transaction with the given hash.
func (c *EthClient) TransactionByHash(
ctx context.Context,
Expand Down
Loading

0 comments on commit 4ceef93

Please sign in to comment.