Skip to content

Commit

Permalink
feat(protocol): Additional integration tests, solidity bump, reduce T…
Browse files Browse the repository at this point in the history
…okenVault contract size (#13155)

Co-authored-by: David <david@taiko.xyz>
Co-authored-by: Daniel Wang <99078276+dantaik@users.noreply.github.com>
Co-authored-by: Daniel Wang <dan@taiko.xyz>
  • Loading branch information
4 people committed Feb 16, 2023
1 parent a6d9bed commit ffdf5db
Show file tree
Hide file tree
Showing 26 changed files with 696 additions and 250 deletions.
3 changes: 2 additions & 1 deletion packages/protocol/.prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
{
"files": ["*.sol", "*.ts"],
"options": {
"tabWidth": 4
"tabWidth": 4,
"compiler": "^0.8.18"
}
}
],
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/.solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "solhint:recommended",
"rules": {
"avoid-low-level-calls": "off",
"compiler-version": ["error", "^0.8.0"],
"compiler-version": ["error", "^0.8.18"],
"func-visibility": ["warn", { "ignoreConstructors": true }],
"max-line-length": ["warn", 80],
"no-empty-blocks": "off",
Expand Down
15 changes: 6 additions & 9 deletions packages/protocol/contracts/L1/TaikoData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,12 @@ library TaikoData {

// This struct takes 9 slots.
struct State {
// block id => block hash (some blocks' hashes won't be persisted,
// only the latest one if verified in a batch)
mapping(uint256 => bytes32) l2Hashes;
// block id => ProposedBlock
mapping(uint256 => ProposedBlock) proposedBlocks;
// block id => parent hash => fork choice
mapping(uint256 => mapping(bytes32 => ForkChoice)) forkChoices;
// proposer => commitSlot => hash(commitHash, commitHeight)
mapping(address => mapping(uint256 => bytes32)) commits;
// some blocks' hashes won't be persisted,
// only the latest one if verified in a batch
mapping(uint256 blockId => bytes32 blockHash) l2Hashes;
mapping(uint256 blockId => ProposedBlock proposedBlock) proposedBlocks;
mapping(uint256 blockId => mapping(bytes32 parentHash => ForkChoice forkChoice)) forkChoices;
mapping(address proposerAddress => mapping(uint256 commitSlot => bytes32 commitHash)) commits;
// Never or rarely changed
uint64 genesisHeight;
uint64 genesisTimestamp;
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/L1/TaikoL1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ contract TaikoL1 is EssentialContract, IHeaderSync, TaikoEvents {
});
LibVerifying.verifyBlocks({
state: state,
config: getConfig(),
config: config,
resolver: AddressResolver(this),
maxBlocks: config.maxVerificationsPerTx,
checkHalt: false
Expand Down
3 changes: 2 additions & 1 deletion packages/protocol/contracts/L1/libs/LibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ library LibProving {
error L1_ANCHOR_RECEIPT_ADDR();
error L1_ANCHOR_RECEIPT_TOPICS();
error L1_ANCHOR_RECEIPT_DATA();
error L1_HALTED();

function proveBlock(
TaikoData.State storage state,
Expand All @@ -81,7 +82,7 @@ library LibProving {
uint256 blockId,
bytes[] calldata inputs
) public {
assert(!LibUtils.isHalted(state));
if (LibUtils.isHalted(state)) revert L1_HALTED();

// Check and decode inputs
if (inputs.length != 3) revert L1_INPUT_SIZE();
Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/L2/TaikoL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ contract TaikoL2 is AddressResolver, ReentrancyGuard, IHeaderSync {

// Mapping from L2 block numbers to their block hashes.
// All L2 block hashes will be saved in this mapping.
mapping(uint256 => bytes32) private _l2Hashes;
mapping(uint256 blockNumber => bytes32 blockHash) private _l2Hashes;

// Mapping from L1 block numbers to their block hashes.
// Note that only hashes of L1 blocks where at least one L2
// block has been proposed will be saved in this mapping.
mapping(uint256 => bytes32) private _l1Hashes;
mapping(uint256 blockNumber => bytes32 blockHash) private _l1Hashes;

// A hash to check te integrity of public inputs.
bytes32 private _publicInputHash;
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/bridge/EtherVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract EtherVault is EssentialContract {
* State Variables *
*********************/

mapping(address => bool) private _authorizedAddrs;
mapping(address addr => bool isAuthorized) private _authorizedAddrs;
uint256[49] private __gap;

/*********************
Expand Down
83 changes: 48 additions & 35 deletions packages/protocol/contracts/bridge/TokenVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,20 @@ contract TokenVault is EssentialContract {
*********************/

// Tracks if a token on the current chain is a canonical or bridged token.
mapping(address => bool) public isBridgedToken;
mapping(address tokenAddress => bool isBridged) public isBridgedToken;

// Mappings from bridged tokens to their canonical tokens.
mapping(address => CanonicalERC20) public bridgedToCanonical;
mapping(address bridgedAddress => CanonicalERC20 canonicalErc20)
public bridgedToCanonical;

// Mappings from canonical tokens to their bridged tokens.
// Also storing chainId for tokens across other chains aside from Ethereum.
// chainId => canonical address => bridged address
mapping(uint256 => mapping(address => address)) public canonicalToBridged;
mapping(uint256 chainId => mapping(address canonicalAddress => address bridgedAddress))
public canonicalToBridged;

// Tracks the token and amount associated with a message hash.
mapping(bytes32 => MessageDeposit) public messageDeposits;
mapping(bytes32 msgHash => MessageDeposit messageDeposit)
public messageDeposits;

uint256[47] private __gap;

Expand Down Expand Up @@ -108,6 +110,21 @@ contract TokenVault is EssentialContract {
uint256 amount
);

/*********************
* Custom Errors*
*********************/

error TOKENVAULT_INVALID_TO();
error TOKENVAULT_INVALID_VALUE();
error TOKENVAULT_INVALID_CALL_VALUE();
error TOKENVAULT_INVALID_TOKEN();
error TOKENVAULT_INVALID_AMOUNT();
error TOKENVAULT_CANONICAL_TOKEN_NOT_FOUND();
error TOKENVAULT_INVALID_OWNER();
error TOKENVAULT_INVALID_SRC_CHAIN_ID();
error TOKENVAULT_MESSAGE_NOT_FAILED();
error TOKENVAULT_INVALID_SENDER();

/*********************
* External Functions*
*********************/
Expand All @@ -134,12 +151,11 @@ contract TokenVault is EssentialContract {
address refundAddress,
string memory memo
) external payable nonReentrant {
require(
to != address(0) &&
to != resolve(destChainId, "token_vault", false),
"V:to"
);
require(msg.value > processingFee, "V:msgValue");
if (
to == address(0) || to == resolve(destChainId, "token_vault", false)
) revert TOKENVAULT_INVALID_TO();

if (msg.value <= processingFee) revert TOKENVAULT_INVALID_VALUE();

IBridge.Message memory message;
message.destChainId = destChainId;
Expand All @@ -152,7 +168,7 @@ contract TokenVault is EssentialContract {
message.memo = memo;

// prevent future PRs from changing the callValue when it must be zero
require(message.callValue == 0, "V:callValue");
if (message.callValue != 0) revert TOKENVAULT_INVALID_CALL_VALUE();

bytes32 msgHash = IBridge(resolve("bridge", false)).sendMessage{
value: msg.value
Expand Down Expand Up @@ -191,13 +207,13 @@ contract TokenVault is EssentialContract {
address refundAddress,
string memory memo
) external payable nonReentrant {
require(
to != address(0) &&
to != resolve(destChainId, "token_vault", false),
"V:to"
);
require(token != address(0), "V:token");
require(amount > 0, "V:amount");
if (
to == address(0) || to == resolve(destChainId, "token_vault", false)
) revert TOKENVAULT_INVALID_TO();

if (token == address(0)) revert TOKENVAULT_INVALID_TOKEN();

if (amount == 0) revert TOKENVAULT_INVALID_AMOUNT();

CanonicalERC20 memory canonicalToken;
uint256 _amount;
Expand All @@ -206,7 +222,8 @@ contract TokenVault is EssentialContract {
if (isBridgedToken[token]) {
BridgedERC20(token).bridgeBurnFrom(msg.sender, amount);
canonicalToken = bridgedToCanonical[token];
require(canonicalToken.addr != address(0), "V:canonicalToken");
if (canonicalToken.addr == address(0))
revert TOKENVAULT_CANONICAL_TOKEN_NOT_FOUND();
_amount = amount;
} else {
// is a canonical token, meaning, it lives on this chain
Expand Down Expand Up @@ -271,20 +288,19 @@ contract TokenVault is EssentialContract {
IBridge.Message calldata message,
bytes calldata proof
) external nonReentrant {
require(message.owner != address(0), "B:owner");
require(message.srcChainId == block.chainid, "B:srcChainId");
if (message.owner == address(0)) revert TOKENVAULT_INVALID_OWNER();
if (message.srcChainId != block.chainid)
revert TOKENVAULT_INVALID_SRC_CHAIN_ID();

IBridge bridge = IBridge(resolve("bridge", false));
bytes32 msgHash = bridge.hashMessage(message);

address token = messageDeposits[msgHash].token;
uint256 amount = messageDeposits[msgHash].amount;
require(token != address(0), "B:ERC20Released");
require(
bridge.isMessageFailed(msgHash, message.destChainId, proof),
"V:notFailed"
);
if (token == address(0)) revert TOKENVAULT_INVALID_TOKEN();

if (!bridge.isMessageFailed(msgHash, message.destChainId, proof))
revert TOKENVAULT_MESSAGE_NOT_FAILED();
messageDeposits[msgHash] = MessageDeposit(address(0), 0);

if (amount > 0) {
Expand Down Expand Up @@ -321,10 +337,8 @@ contract TokenVault is EssentialContract {
uint256 amount
) external nonReentrant onlyFromNamed("bridge") {
IBridge.Context memory ctx = IBridge(msg.sender).context();
require(
ctx.sender == resolve(ctx.srcChainId, "token_vault", false),
"V:sender"
);
if (ctx.sender != resolve(ctx.srcChainId, "token_vault", false))
revert TOKENVAULT_INVALID_SENDER();

address token;
if (canonicalToken.chainId == block.chainid) {
Expand Down Expand Up @@ -367,12 +381,11 @@ contract TokenVault is EssentialContract {
function _deployBridgedToken(
CanonicalERC20 calldata canonicalToken
) private returns (address bridgedToken) {
bytes32 salt = keccak256(
abi.encodePacked(canonicalToken.chainId, canonicalToken.addr)
);
bridgedToken = Create2Upgradeable.deploy(
0, // amount of Ether to send
salt,
keccak256(
abi.encodePacked(canonicalToken.chainId, canonicalToken.addr)
),
type(BridgedERC20).creationCode
);

Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/bridge/libs/LibBridgeData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ library LibBridgeData {
struct State {
uint256 nextMessageId;
IBridge.Context ctx; // 3 slots
mapping(bytes32 => bool) etherReleased;
mapping(bytes32 msgHash => bool released) etherReleased;
uint256[45] __gap;
}

Expand Down
3 changes: 2 additions & 1 deletion packages/protocol/contracts/test/libs/TestLibProving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ library TestLibProving {
error L1_ANCHOR_RECEIPT_ADDR();
error L1_ANCHOR_RECEIPT_TOPICS();
error L1_ANCHOR_RECEIPT_DATA();
error L1_HALTED();

function proveBlock(
TaikoData.State storage state,
Expand All @@ -87,7 +88,7 @@ library TestLibProving {
uint256 blockId,
bytes[] calldata inputs
) public {
assert(!LibUtils.isHalted(state));
if (LibUtils.isHalted(state)) revert L1_HALTED();

// Check and decode inputs
if (inputs.length != 3) revert L1_INPUT_SIZE();
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/thirdparty/AddressManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ contract AddressManager is OwnableUpgradeable {
* Variables *
*************/

mapping(bytes32 => address) private addresses;
mapping(bytes32 nameHash => address addr) private addresses;

/**********
* Events *
Expand Down
5 changes: 3 additions & 2 deletions packages/protocol/contracts/thirdparty/ERC20Upgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ contract ERC20Upgradeable is
IERC20Upgradeable,
IERC20MetadataUpgradeable
{
mapping(address => uint256) private _balances;
mapping(address owner => uint256 balance) private _balances;

mapping(address => mapping(address => uint256)) private _allowances;
mapping(address owner => mapping(address spender => uint256 allowance))
private _allowances;

uint256 private _totalSupply;

Expand Down
3 changes: 2 additions & 1 deletion packages/protocol/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "@openzeppelin/hardhat-upgrades";
import "@typechain/hardhat";
import "hardhat-abi-exporter";
import "hardhat-gas-reporter";
import "hardhat-contract-sizer";
import { HardhatUserConfig } from "hardhat/config";
import "solidity-coverage";
import "solidity-docgen";
Expand Down Expand Up @@ -133,7 +134,7 @@ const config: HardhatUserConfig = {
},
},
},
version: "0.8.9",
version: "0.8.18",
},
};

Expand Down
14 changes: 8 additions & 6 deletions packages/protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"export:abi": "pnpm hardhat clear-abi && pnpm hardhat export-abi",
"export:docs": "pnpm hardhat docgen && pnpm prettier --write ../website/pages/docs/reference/contract-documentation/**/*.md",
"clean": "rm -rf abis cache && pnpm hardhat clean",
"lint:sol": "pnpm prettier '**/*.sol' --write && pnpm solhint 'contracts/**/*.sol' --fix",
"lint:sol": "pnpm prettier '**/*.sol' --write",
"eslint": "pnpm exec eslint --ignore-path .eslintignore --ext .js,.ts .",
"eslint:fix": "pnpm exec eslint --ignore-path .eslintignore --ext .js,.ts . --fix",
"test": "pnpm hardhat test --grep '^[^integration]'",
Expand All @@ -18,9 +18,10 @@
"test:genesis": "./test/genesis/generate_genesis.test.sh",
"test:integration": "TEST_TYPE=integration ./test/test_integration.sh",
"test:tokenomics": "TEST_TYPE=tokenomics ./test/test_integration.sh",
"test:all": "pnpm run test && pnpm run test:integration && pnpm run test:tokenomics",
"test:all": "pnpm run test && pnpm run test:integration && pnpm run test:tokenomics && pnpm run test:genesis",
"deploy:hardhat": "./scripts/download_solc.sh && LOG_LEVEL=debug pnpm hardhat deploy_L1 --network hardhat --dao-vault 0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39 --team-vault 0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39 --l2-genesis-block-hash 0xee1950562d42f0da28bd4550d88886bc90894c77c9c9eaefef775d4c8223f259 --bridge-funder-private-key ddbf12f72c946bb1e6de5eaf580c51db51828ba198d9b0dba9c7d48ec748dc04 --bridge-fund 0xff --oracle-prover 0xdf08f82de32b8d460adbe8d72043e3a7e25a3b39 --confirmations 1",
"lint-staged": "lint-staged --allow-empty"
"lint-staged": "lint-staged --allow-empty",
"sizer": "pnpm hardhat size-contracts"
},
"lint-staged": {
"*.sol": "pnpm lint:sol",
Expand Down Expand Up @@ -66,15 +67,16 @@
"glob": "^8.1.0",
"hardhat": "^2.8.3",
"hardhat-abi-exporter": "^2.10.0",
"hardhat-contract-sizer": "^2.8.0",
"hardhat-docgen": "^1.3.0",
"hardhat-gas-reporter": "^1.0.7",
"lint-staged": "^12.3.4",
"merkle-patricia-tree": "^4.2.4",
"prettier": "^2.5.1",
"prettier-plugin-solidity": "^1.0.0-beta.19",
"prettier-plugin-solidity": "^1.1.2",
"rlp": "^3.0.0",
"solhint": "^3.3.7",
"solidity-coverage": "^0.8.2",
"solhint": "^3.3.8",
"solidity-coverage": "github:taikoxyz/solidity-coverage",
"solidity-docgen": "^0.6.0-beta.34",
"ts-node": "^10.5.0",
"typechain": "^5.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/scripts/download_solc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ if [ -f "solc" ]; then
exit 0
fi

VERSION=v0.8.9
VERSION=v0.8.18

if [ "$(uname)" = 'Darwin' ]; then
SOLC_FILE_NAME=solc-macos
Expand Down

0 comments on commit ffdf5db

Please sign in to comment.