Skip to content

Commit

Permalink
refactor(protocol): use Bridge to send cross-chain owned contract tra…
Browse files Browse the repository at this point in the history
…nsactions (#15368)

Co-authored-by: David <david@taiko.xyz>
  • Loading branch information
dantaik and davidtaikocha authored Dec 11, 2023
1 parent 5e06cd9 commit 9ef2dd2
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 119 deletions.
71 changes: 71 additions & 0 deletions packages/protocol/contracts/L2/CrossChainOwned.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: MIT
// _____ _ _ _ _
// |_ _|_ _(_) |_____ | | __ _| |__ ___
// | |/ _` | | / / _ \ | |__/ _` | '_ (_-<
// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/

pragma solidity 0.8.20;

import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

import "../common/EssentialContract.sol";
import "../bridge/IBridge.sol";

/// @title CrossChainOwned
/// @notice This contract's owner can be a local address or one that lives on another chain and uses
/// signals for transaction approval.
/// @dev Notice that when send the message on the owner chain, the gas limit of the message must not
/// be zero, so on this chain, some EOA can help execute this transaction.
abstract contract CrossChainOwned is EssentialContract {
uint64 public ownerChainId; // slot 1
uint64 public nextTxId;
uint256[49] private __gap;

event TransactionExecuted(uint64 indexed txId, bytes4 indexed selector);

error XCO_INVALID_TX_ID();
error XCO_INVALID_OWNER_CHAINID();
error XCO_PERMISSION_DENIED();
error XCO_TX_REVERTED();

function executeCrossChainTransaction(uint64 txId, bytes calldata txdata) external {
if (txId != nextTxId) revert XCO_INVALID_TX_ID();

if (msg.sender != resolve("bridge", false)) revert XCO_PERMISSION_DENIED();

IBridge.Context memory ctx = IBridge(msg.sender).context();
if (ctx.srcChainId != ownerChainId || ctx.from != owner()) {
revert XCO_PERMISSION_DENIED();
}

(bool success,) = address(this).call(txdata);
if (!success) revert XCO_TX_REVERTED();

emit TransactionExecuted(nextTxId++, bytes4(txdata));
}

/// @notice Initializes the contract.
/// @param _addressManager The address of the address manager.
/// @param _ownerChainId The owner's deployment chain ID.
// solhint-disable-next-line func-name-mixedcase
function __CrossChainOwned_init(
address _addressManager,
uint64 _ownerChainId
)
internal
virtual
{
__Essential_init(_addressManager);

if (_ownerChainId == 0 || _ownerChainId == block.chainid) {
revert XCO_INVALID_OWNER_CHAINID();
}
ownerChainId = _ownerChainId;
}

function _checkOwner() internal view virtual override {
if (msg.sender != owner() && msg.sender != address(this)) {
revert XCO_PERMISSION_DENIED();
}
}
}
33 changes: 18 additions & 15 deletions packages/protocol/contracts/L2/TaikoL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ pragma solidity 0.8.20;

import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

import "../common/EssentialContract.sol";
import "../common/ICrossChainSync.sol";
import "../signal/ISignalService.sol";
import "../libs/LibAddress.sol";
import "../libs/LibMath.sol";
import "../signal/ISignalService.sol";
import "./Lib1559Math.sol";
import "./CrossChainOwned.sol";
import "./TaikoL2Signer.sol";

/// @title TaikoL2
Expand All @@ -22,7 +22,7 @@ import "./TaikoL2Signer.sol";
/// It is used to anchor the latest L1 block details to L2 for cross-layer
/// communication, manage EIP-1559 parameters for gas pricing, and store
/// verified L1 block information.
contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync {
contract TaikoL2 is CrossChainOwned, TaikoL2Signer, ICrossChainSync {
using LibAddress for address;
using LibMath for uint256;

Expand All @@ -37,13 +37,11 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync {
mapping(uint256 l1height => ICrossChainSync.Snippet) public snippets;

// A hash to check the integrity of public inputs.
address public signalService; // slot 3
bytes32 public publicInputHash; // slot 4

uint64 public gasExcess; // slot 5
bytes32 public publicInputHash; // slot 3
uint64 public gasExcess; // slot 4
uint64 public latestSyncedL1Height;

uint256[145] private __gap;
uint256[146] private __gap;

event Anchored(bytes32 parentHash, uint64 gasExcess);

Expand All @@ -55,13 +53,18 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync {
error L2_TOO_LATE();

/// @notice Initializes the TaikoL2 contract.
/// @param _signalService Address of the {ISignalService} contract.
/// @param _addressManager Address of the AddressManager contract.
/// @param _l1ChainId The ID of the base layer.
/// @param _gasExcess The initial gasExcess.
function init(address _signalService, uint64 _gasExcess) external initializer {
__Essential_init();

if (_signalService == address(0)) revert L2_INVALID_PARAM();
signalService = _signalService;
function init(
address _addressManager,
uint64 _l1ChainId,
uint64 _gasExcess
)
external
initializer
{
__CrossChainOwned_init(_addressManager, _l1ChainId);

if (block.chainid <= 1 || block.chainid >= type(uint64).max) {
revert L2_INVALID_CHAIN_ID();
Expand Down Expand Up @@ -125,7 +128,7 @@ contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync {

// Store the L1's signal root as a signal to the local signal service to
// allow for multi-hop bridging.
ISignalService(signalService).sendSignal(l1SignalRoot);
ISignalService(resolve("signal_service", false)).sendSignal(l1SignalRoot);
emit CrossChainSynced(uint64(block.number), l1Height, l1BlockHash, l1SignalRoot);

// Update state variables
Expand Down
38 changes: 27 additions & 11 deletions packages/protocol/genesis/GenerateGenesis.g.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ contract TestGenerateGenesis is Test, AddressResolver {
vm.readFile(string.concat(vm.projectRoot(), "/deployments/genesis_config.json"));
string private genesisAllocJSON =
vm.readFile(string.concat(vm.projectRoot(), "/deployments/genesis_alloc.json"));
address private owner = configJSON.readAddress(".contractOwner");
address private ownerTimelockController = configJSON.readAddress(".ownerTimelockController");
address private ownerSecurityCouncil = configJSON.readAddress(".ownerSecurityCouncil");
uint256 private ownerChainId = configJSON.readUint(".ownerChainId");

function testSharedContractsDeployment() public {
assertEq(block.chainid, 167);
Expand Down Expand Up @@ -59,7 +61,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
checkDeployedCode("RollupAddressManager");

// check proxy implementations
checkProxyImplementation("TaikoL2", "TaikoL2Impl");
checkProxyImplementation("TaikoL2", "TaikoL2Impl", ownerTimelockController);
checkProxyImplementation("RollupAddressManager", "RollupAddressManagerImpl");

// check proxies
Expand All @@ -71,7 +73,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
AddressManager addressManagerProxy =
AddressManager(getPredeployedContractAddress("SharedAddressManager"));

assertEq(owner, addressManagerProxy.owner());
assertEq(ownerSecurityCouncil, addressManagerProxy.owner());

checkSavedAddress(addressManagerProxy, "Bridge", "bridge");
checkSavedAddress(addressManagerProxy, "ERC20Vault", "erc20_vault");
Expand All @@ -95,7 +97,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
AddressManager addressManagerProxy =
AddressManager(getPredeployedContractAddress("RollupAddressManager"));

assertEq(owner, addressManagerProxy.owner());
assertEq(ownerSecurityCouncil, addressManagerProxy.owner());

checkSavedAddress(addressManagerProxy, "TaikoL2", "taiko");
checkSavedAddress(addressManagerProxy, "SignalService", "signal_service");
Expand All @@ -115,6 +117,9 @@ contract TestGenerateGenesis is Test, AddressResolver {
function testTaikoL2() public {
TaikoL2 taikoL2Proxy = TaikoL2(getPredeployedContractAddress("TaikoL2"));

assertEq(ownerTimelockController, taikoL2Proxy.owner());
assertEq(ownerChainId, taikoL2Proxy.ownerChainId());

vm.startPrank(taikoL2Proxy.GOLDEN_TOUCH_ADDRESS());
for (uint32 i = 0; i < 300; ++i) {
vm.roll(block.number + 1);
Expand Down Expand Up @@ -152,7 +157,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
function testSingletonBridge() public {
Bridge bridgeProxy = Bridge(payable(getPredeployedContractAddress("Bridge")));

assertEq(owner, bridgeProxy.owner());
assertEq(ownerSecurityCouncil, bridgeProxy.owner());

vm.expectRevert(Bridge.B_PERMISSION_DENIED.selector);
bridgeProxy.processMessage(
Expand All @@ -175,7 +180,7 @@ contract TestGenerateGenesis is Test, AddressResolver {

assertEq(bridgeProxy.paused(), false);

vm.startPrank(owner);
vm.startPrank(ownerSecurityCouncil);
bridgeProxy.pause();
assertEq(bridgeProxy.paused(), true);

Expand Down Expand Up @@ -218,7 +223,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
AddressManager addressManager =
AddressManager(getPredeployedContractAddress("SharedAddressManager"));

assertEq(owner, erc20VaultProxy.owner());
assertEq(ownerSecurityCouncil, erc20VaultProxy.owner());

vm.startPrank(addressManager.owner());
addressManager.setAddress(1, "bridge", bridgeAddress);
Expand All @@ -244,7 +249,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
AddressManager addressManager =
AddressManager(getPredeployedContractAddress("SharedAddressManager"));

assertEq(owner, erc721VaultProxy.owner());
assertEq(ownerSecurityCouncil, erc721VaultProxy.owner());

vm.startPrank(addressManager.owner());
addressManager.setAddress(1, "bridge", bridgeAddress);
Expand All @@ -269,7 +274,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
AddressManager addressManager =
AddressManager(getPredeployedContractAddress("SharedAddressManager"));

assertEq(owner, erc1155VaultProxy.owner());
assertEq(ownerSecurityCouncil, erc1155VaultProxy.owner());

vm.startPrank(addressManager.owner());
addressManager.setAddress(1, "bridge", bridgeProxyAddress);
Expand All @@ -293,7 +298,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
SignalService signalServiceProxy =
SignalService(getPredeployedContractAddress("SignalService"));

assertEq(owner, signalServiceProxy.owner());
assertEq(ownerSecurityCouncil, signalServiceProxy.owner());

signalServiceProxy.sendSignal(keccak256(abi.encodePacked(block.prevrandao)));

Expand All @@ -304,7 +309,7 @@ contract TestGenerateGenesis is Test, AddressResolver {
)
);

vm.startPrank(owner);
vm.startPrank(ownerSecurityCouncil);

SignalService signalService =
SignalService(payable(getPredeployedContractAddress("SignalServiceImpl")));
Expand Down Expand Up @@ -335,11 +340,22 @@ contract TestGenerateGenesis is Test, AddressResolver {
assertEq(address(contractAddress).code, vm.parseBytes(deployedCode));
}


function checkProxyImplementation(
string memory proxyName,
string memory contractName
)
private
{
return checkProxyImplementation(proxyName, contractName, ownerSecurityCouncil);
}

function checkProxyImplementation(
string memory proxyName,
string memory contractName,
address owner
)
private
{
vm.startPrank(owner);
address contractAddress = getPredeployedContractAddress(contractName);
Expand Down
4 changes: 3 additions & 1 deletion packages/protocol/genesis/test_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
const ADDRESS_LENGTH = 40;

module.exports = {
contractOwner: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
ownerTimelockController: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
ownerSecurityCouncil: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
ownerChainId: 1,
chainId: 167,
seedAccounts: [
{
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/test/L1/Guardians.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ contract TestSignalService is TaikoTest {
deployProxy({
name: "guardians",
impl: address(new DummyGuardians()),
data: bytes.concat(DummyGuardians.init.selector)
data: abi.encodeCall(DummyGuardians.init, ())
})
);
}
Expand Down
43 changes: 17 additions & 26 deletions packages/protocol/test/L2/TaikoL2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,36 @@ contract TestTaikoL2 is TaikoTest {
// same as `block_gas_limit` in foundry.toml
uint32 public constant BLOCK_GAS_LIMIT = 30_000_000;

AddressManager public addressManager;
SignalService public ss;
address public addressManager;
TaikoL2EIP1559Configurable public L2;
SkipBasefeeCheckL2 public L2skip;

function setUp() public {
addressManager = AddressManager(
deployProxy({
name: "address_manager",
impl: address(new AddressManager()),
data: bytes.concat(AddressManager.init.selector)
})
);

ss = SignalService(
deployProxy({
name: "signal_service",
impl: address(new SignalService()),
data: bytes.concat(SignalService.init.selector),
registerTo: address(addressManager),
owner: address(0)
})
);
addressManager = deployProxy({
name: "address_manager",
impl: address(new AddressManager()),
data: abi.encodeCall(AddressManager.init, ())
});

deployProxy({
name: "signal_service",
impl: address(new SignalService()),
data: abi.encodeCall(SignalService.init, ()),
registerTo: addressManager,
owner: address(0)
});

uint64 gasExcess = 0;
uint8 quotient = 8;
uint32 gasTarget = 60_000_000;
uint64 l1ChainId = 12_345;

L2 = TaikoL2EIP1559Configurable(
payable(
deployProxy({
name: "taiko_l2",
impl: address(new TaikoL2EIP1559Configurable()),
data: bytes.concat(TaikoL2.init.selector, abi.encode(address(ss), gasExcess))
data: abi.encodeCall(TaikoL2.init, (addressManager, l1ChainId, gasExcess))
})
)
);
Expand All @@ -63,7 +59,7 @@ contract TestTaikoL2 is TaikoTest {
deployProxy({
name: "taiko_l2",
impl: address(new SkipBasefeeCheckL2()),
data: bytes.concat(TaikoL2.init.selector, abi.encode(address(ss), gasExcess))
data: abi.encodeCall(TaikoL2.init, (addressManager, l1ChainId, gasExcess))
})
)
);
Expand Down Expand Up @@ -258,11 +254,6 @@ contract TestTaikoL2 is TaikoTest {
L2skip.anchor(l1Hash, l1SignalRoot, l1Height, parentGasLimit);
}

function registerAddress(bytes32 nameHash, address addr) internal {
addressManager.setAddress(uint64(block.chainid), nameHash, addr);
console2.log(block.chainid, uint256(nameHash), unicode"", addr);
}

// Semi-random number generator
function pickRandomNumber(
uint256 randomNum,
Expand Down
Loading

1 comment on commit 9ef2dd2

@maksk2
Copy link

@maksk2 maksk2 commented on 9ef2dd2 Feb 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -5,8 +5,8 @@ import {AntdRegistry} from "@ant-design/nextjs-registry";
const inter = Inter({subsets: ["latin"]});

export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "MyAddressQuery",
description: "MyAddressQuery",
};

export default function RootLayout({children}) {

Please sign in to comment.