Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(protocol): improve test coverage #16428

Merged
merged 16 commits into from
Mar 19, 2024
103 changes: 103 additions & 0 deletions packages/protocol/test/L1/TaikoL1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ contract TaikoL1Test is TaikoL1TestBase {
vm.warp(block.timestamp + tierProvider().getTier(minTier).cooldownWindow * 60 + 1);

verifyBlock(Alice, 2);

(TaikoData.Block memory blk, TaikoData.TransitionState memory ts) = L1.getBlock(meta.id);
assertEq(meta.id, blk.blockId);

ts = L1.getTransition(meta.id, parentHash);
assertEq(ts.prover, Bob);

parentHash = blockHash;
}
printVariables("");
Expand Down Expand Up @@ -208,6 +215,10 @@ contract TaikoL1Test is TaikoL1TestBase {
giveEthAndTko(Henry, 0, maxAmount + 1 ether);

// So after this point we have 8 deposits

vm.prank(Alice, Alice);
bool canAliceDeposit = L1.canDepositEthToL2(1 ether);
assertEq(true, canAliceDeposit);
vm.prank(Alice, Alice);
L1.depositEtherToL2{ value: 1 ether }(address(0));
vm.prank(Bob, Bob);
Expand Down Expand Up @@ -246,4 +257,96 @@ contract TaikoL1Test is TaikoL1TestBase {
0x3b61cf81fd007398a8efd07a055ac8fb542bcfa62d76cf6dc28a889371afb21e
);
}

function test_pauseProving() external {
L1.pauseProving(true);

TaikoData.BlockMetadata memory meta;

giveEthAndTko(Alice, 1000 ether, 1000 ether);
giveEthAndTko(Bob, 1e8 ether, 100 ether);

// Proposing is still possible
(meta,) = proposeBlock(Alice, Bob, 1_000_000, 1024);
// Proving is not, so supply the revert reason to proveBlock
proveBlock(
Bob,
Bob,
meta,
GENESIS_BLOCK_HASH,
bytes32("01"),
bytes32("02"),
meta.minTier,
TaikoErrors.L1_PROVING_PAUSED.selector
);
}

function test_unpause() external {
L1.pause();

giveEthAndTko(Alice, 1000 ether, 1000 ether);
giveEthAndTko(Bob, 1e8 ether, 100 ether);

// Proposing is also not possible
proposeButRevert(Alice, Bob, 1024, EssentialContract.INVALID_PAUSE_STATUS.selector);

// unpause
L1.unpause();

// Proposing is possible again
proposeBlock(Alice, Bob, 1_000_000, 1024);
}

function test_burn() external {
uint256 balanceBeforeBurn = tko.balanceOf(address(this));
vm.prank(tko.owner(), tko.owner());
tko.burn(address(this), 1 ether);
uint256 balanceAfterBurn = tko.balanceOf(address(this));

assertEq(balanceBeforeBurn - 1 ether, balanceAfterBurn);
}

function test_snapshot() external {
vm.prank(tko.owner(), tko.owner());
tko.snapshot();

uint256 totalSupplyAtSnapshot = tko.totalSupplyAt(1);

vm.prank(tko.owner(), tko.owner());
tko.burn(address(this), 1 ether);

// At snapshot date vs. now, the total supply differs
assertEq(totalSupplyAtSnapshot, tko.totalSupply() + 1 ether);
}

function test_getTierIds() external {
uint16[] memory tiers = cp.getTierIds();
assertEq(tiers[0], LibTiers.TIER_OPTIMISTIC);
assertEq(tiers[1], LibTiers.TIER_SGX);
assertEq(tiers[2], LibTiers.TIER_GUARDIAN);

vm.expectRevert();
cp.getTier(123);
}

function proposeButRevert(
address proposer,
address prover,
uint24 txListSize,
bytes4 revertReason
)
internal
{
uint256 msgValue = 2 ether;
AssignmentHook.ProverAssignment memory assignment;
TaikoData.HookCall[] memory hookcalls = new TaikoData.HookCall[](1);
hookcalls[0] = TaikoData.HookCall(address(assignmentHook), abi.encode(assignment));

vm.prank(proposer, proposer);
vm.expectRevert(revertReason);
L1.proposeBlock{ value: msgValue }(
abi.encode(TaikoData.BlockParams(prover, address(0), 0, 0, hookcalls)),
new bytes(txListSize)
);
}
}
18 changes: 18 additions & 0 deletions packages/protocol/test/L2/TaikoL2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ contract TestTaikoL2 is TaikoTest {

vm.roll(block.number + 1);
vm.warp(block.timestamp + 30);

vm.deal(address(L2), 100 ether);
}

function test_L2_AnchorTx_with_constant_block_time() external {
Expand Down Expand Up @@ -137,6 +139,22 @@ contract TestTaikoL2 is TaikoTest {
LibL2Signer.signAnchor(digest, uint8(3));
}

function test_L2_withdraw() external {
vm.prank(L2.owner(), L2.owner());
L2.withdraw(address(0), Alice);
assertEq(address(L2).balance, 0 ether);
assertEq(Alice.balance, 100 ether);

// Random EOA cannot call withdraw
vm.expectRevert();
vm.prank(Alice, Alice);
L2.withdraw(address(0), Alice);
}

function test_L2_getBlockHash() external {
assertEq(L2.getBlockHash(uint64(1000)), 0);
}

function _anchor(uint32 parentGasLimit) private {
bytes32 l1Hash = randBytes32();
bytes32 l1StateRoot = randBytes32();
Expand Down
64 changes: 64 additions & 0 deletions packages/protocol/test/bridge/Bridge.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,70 @@ contract BridgeTest is TaikoTest {
assertEq(status == IBridge.Status.DONE, true);
}

function test_Bridge_suspend_messages() public {
vm.startPrank(Alice);
(IBridge.Message memory message, bytes memory proof) =
setUpPredefinedSuccessfulProcessMessageCall();

bytes32 msgHash = destChainBridge.hashMessage(message);
bytes32[] memory messageHashes = new bytes32[](1);
messageHashes[0] = msgHash;

vm.stopPrank();
// Suspend
vm.prank(destChainBridge.owner(), destChainBridge.owner());
destChainBridge.suspendMessages(messageHashes, true);

vm.startPrank(Alice);
vm.expectRevert(Bridge.B_INVOCATION_TOO_EARLY.selector);
destChainBridge.processMessage(message, proof);

vm.stopPrank();
// Unsuspend
vm.prank(destChainBridge.owner(), destChainBridge.owner());
destChainBridge.suspendMessages(messageHashes, false);

vm.startPrank(Alice);
destChainBridge.processMessage(message, proof);

IBridge.Status status = destChainBridge.messageStatus(msgHash);

assertEq(status == IBridge.Status.DONE, true);
}

function test_Bridge_ban_address() public {
vm.startPrank(Alice);
(IBridge.Message memory message, bytes memory proof) =
setUpPredefinedSuccessfulProcessMessageCall();

bytes32 msgHash = destChainBridge.hashMessage(message);
bytes32[] memory messageHashes = new bytes32[](1);
messageHashes[0] = msgHash;

vm.stopPrank();
// Ban address
vm.prank(destChainBridge.owner(), destChainBridge.owner());
destChainBridge.banAddress(message.to, true);

vm.startPrank(Alice);
// processMessage() still marks it DONE but dont call the invokeMessageCall on them
destChainBridge.processMessage(message, proof);

IBridge.Status status = destChainBridge.messageStatus(msgHash);

assertEq(status == IBridge.Status.DONE, true);
}

function test_Bridge_prove_message_received() public {
vm.startPrank(Alice);
(IBridge.Message memory message, bytes memory proof) =
setUpPredefinedSuccessfulProcessMessageCall();

bool received = destChainBridge.proveMessageReceived(message, proof);

assertEq(received, true);
}

// test with a known good merkle proof / message since we cant generate
// proofs via rpc
// in foundry
Expand Down
154 changes: 154 additions & 0 deletions packages/protocol/test/libs/LibAddress.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import "../TaikoTest.sol";
import "../../contracts/libs/LibAddress.sol";

contract CalldataReceiver {
// Returns success
function returnSuccess() public pure returns (bool) {
return true;
}

function supportsInterface(bytes4 interfaceId) external pure returns (bool result) {
if (interfaceId == 0x10101010) {
result = true;
}
}

// Reverts
receive() external payable {
revert();
}

fallback() external payable {
revert();
}
}

/// @notice The reason we call LibAddress.sendEther this way is - and not directly inside of a
/// test_sendEther() because this way the coverage adds up if you call functions against initiated
/// contracts, so basically it is a workaround making the library test 'count' towards the coverage.
/// @dev The EtherSenderContract in live environment is the Bridge.
contract EtherSenderContract {
function sendEther(
address _to,
uint256 _amount,
uint256 _gasLimit,
bytes memory _calldata
)
public
returns (bool success_)
{
success_ = LibAddress.sendEther(_to, _amount, _gasLimit, _calldata);
}

function sendEtherAndVerify(address _to, uint256 _amount, uint256 _gasLimit) public {
LibAddress.sendEtherAndVerify(_to, _amount, _gasLimit);
}

function sendEtherAndVerify(address _to, uint256 _amount) public {
LibAddress.sendEtherAndVerify(_to, _amount);
}

function supportsInterface(
address _addr,
bytes4 _interfaceId
)
public
view
returns (bool result)
{
return LibAddress.supportsInterface(_addr, _interfaceId);
}

function isValidSignature(
address _addr,
bytes32 _hash,
bytes memory _sig
)
public
view
returns (bool)
{
return LibAddress.isValidSignature(_addr, _hash, _sig);
}
}

contract TestLibAddress is TaikoTest {
EtherSenderContract bridge;
CalldataReceiver calledContract;

function setUp() public virtual {
bridge = new EtherSenderContract();
vm.deal(address(bridge), 1 ether);

calledContract = new CalldataReceiver();
}

function test_sendEther() public {
uint256 balanceBefore = Alice.balance;
bridge.sendEther((Alice), 0.5 ether, 2300, "");
assertEq(Alice.balance, balanceBefore + 0.5 ether);

// Cannot send to address(0)
vm.expectRevert(LibAddress.ETH_TRANSFER_FAILED.selector);
bridge.sendEther(address(0), 0.5 ether, 2300, "");
}

function test_sendEther_with_calldata() public {
bytes memory functionCalldata = abi.encodeCall(CalldataReceiver.returnSuccess, ());

bool success = bridge.sendEther(address(calledContract), 0, 230_000, functionCalldata);

assertEq(success, true);

// No input argument so it will fall to the fallback.
bytes memory wrongfunctionCalldata =
abi.encodeWithSelector(IERC165Upgradeable.supportsInterface.selector, 10);
success = bridge.sendEther(address(calledContract), 0, 230_000, wrongfunctionCalldata);

assertEq(success, false);
}

function test_sendEtherAndVerify() public {
uint256 balanceBefore = Alice.balance;
bridge.sendEtherAndVerify(Alice, 0.5 ether, 2300);
assertEq(Alice.balance, balanceBefore + 0.5 ether);

// Send 0 ether is also possible
bridge.sendEtherAndVerify(Alice, 0, 2300);

// If sending fails, call reverts
vm.expectRevert(LibAddress.ETH_TRANSFER_FAILED.selector);
bridge.sendEtherAndVerify(address(calledContract), 0.1 ether, 2300);

//Call sendEtherAndVerify without the gasLimit
bridge.sendEtherAndVerify(Alice, 0.5 ether);
assertEq(Alice.balance, balanceBefore + 1 ether);
}

function test_supportsInterface() public {
bool doesSupport = bridge.supportsInterface(Alice, 0x10101010);

assertEq(doesSupport, false);

doesSupport = bridge.supportsInterface(address(bridge), 0x10101010);

assertEq(doesSupport, false);

doesSupport = bridge.supportsInterface(address(calledContract), 0x10101010);

assertEq(doesSupport, true);
}

function test_isValidSignature() public {
bytes32 hash = bytes32("DUMMY_SIGNATURE_HASH_DATA");

// 0x1 is Alice's private key
(uint8 v, bytes32 r, bytes32 s) = vm.sign(0x1, hash);
bool validSignature = bridge.isValidSignature(Alice, hash, abi.encodePacked(r, s, v));

assertEq(validSignature, true);
}
}
Loading