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

[protocol] add more tests #179

Merged
merged 36 commits into from
Oct 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
16d9c66
tests for bridge contracts EtherVault, TokenVault, and BridgedERC20 s…
cyberhorsey Oct 17, 2022
71b92de
addtl tests
cyberhorsey Oct 17, 2022
954496e
revert .skip, use npm run test over npx hardhat test
cyberhorsey Oct 17, 2022
be6d159
coverage commands for package json
cyberhorsey Oct 18, 2022
fd42129
test folder structure mirrors contract structure now
cyberhorsey Oct 18, 2022
a5154e0
tests for addressmanager
cyberhorsey Oct 18, 2022
a40f9ae
bridge sendMessage tests
cyberhorsey Oct 18, 2022
ee38111
sendSignal() test
RogerLamTd Oct 18, 2022
8299271
porting Lib tests
RogerLamTd Oct 18, 2022
b215bfa
test contract port
RogerLamTd Oct 18, 2022
410b40a
sendSignal() tests happy/sad flows, processMessage() some sad flows
cyberhorsey Oct 18, 2022
1c6f014
merge confs
cyberhorsey Oct 18, 2022
2de247c
refactor libbridgedata test
cyberhorsey Oct 19, 2022
4454c8d
unused vars
cyberhorsey Oct 19, 2022
3f95443
remove unused vars from LibBridgeData, add LibBridgeSignal stubs
RogerLamTd Oct 19, 2022
20da0cd
tkoToken tests
cyberhorsey Oct 19, 2022
200763d
Merge branch 'tests' of github.com:taikochain/taiko-mono into tests
cyberhorsey Oct 19, 2022
9f1ade0
merge
cyberhorsey Oct 19, 2022
5020183
pathing for genesis
cyberhorsey Oct 19, 2022
6a54e66
pathing for deployerc20
cyberhorsey Oct 19, 2022
b9ed7b4
pathing
cyberhorsey Oct 19, 2022
5247a4e
LibBridgeSignal & TestLibBridgeSignal
RogerLamTd Oct 20, 2022
20a5fc8
fix
RogerLamTd Oct 20, 2022
be3bba6
isSignalSent() test written
RogerLamTd Oct 20, 2022
e81766a
Merge branch 'main' into tests
cyberhorsey Oct 20, 2022
d6cea5b
rename all Lib_ files to Lib files to keep the pattern consistent. Me…
cyberhorsey Oct 20, 2022
1184128
Merge branch 'tests' of github.com:taikochain/taiko-mono into tests
cyberhorsey Oct 20, 2022
4fb848f
LibBridgeSend stubs
RogerLamTd Oct 21, 2022
b31256c
solcover and coverage works
cyberhorsey Oct 21, 2022
d58a7bb
Merge branch 'tests' of github.com:taikochain/taiko-mono into tests
cyberhorsey Oct 21, 2022
d6b7e74
example of hwo to use state for LibBridgeSend tests
cyberhorsey Oct 21, 2022
8327edd
secure merkle trie tests
cyberhorsey Oct 21, 2022
679a691
added updateMessageStatus() tests and fix test contracts
RogerLamTd Oct 21, 2022
e605298
integration tests for libTrieProof, + readme
cyberhorsey Oct 21, 2022
6fb5dc6
Merge branch 'tests' of github.com:taikochain/taiko-mono into tests
cyberhorsey Oct 21, 2022
3317bed
comments + basic readme
cyberhorsey Oct 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/protocol/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ artifacts
cache
coverage*
gasReporterOutput.json
contracts/thirdparty/Lib_BlockHeaderDecoder.sol
contracts/thirdparty/LibBlockHeaderDecoder.sol
9 changes: 9 additions & 0 deletions packages/protocol/.solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
configureYulOptimizer: true,
skipFiles: [
'thirdparty/LibBlockHeaderDecoder.sol', // assembly too long
'libs/LibReceiptDecoder.sol', //integration test,
'test/libs/TestLibReceiptDecoder.sol', //integration tests
'test/thirdparty/TestLibBlockHeaderDecoder.sol' // assembly too long
]
};
6 changes: 3 additions & 3 deletions packages/protocol/.solhintignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
node_modules/
contracts/aux/tokens/ERC20Upgradeable.sol
contracts/test/TestLib_RLPReader.sol
contracts/test/TestLib_RLPWriter.sol
contracts/test/TestLibRLPReader.sol
contracts/test/TestLibRLPWriter.sol
contracts/libs/Lib1559Math.sol
contracts/thirdparty/
**/contracts/thirdparty/**/*.sol
51 changes: 49 additions & 2 deletions packages/protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ yarn test
To run test cases that rely on a go-ethereum node:

```bash
yarn test:geth
yarn test:integration
```

## Generate L2 genesis JSON's `alloc` field
Expand Down Expand Up @@ -56,7 +56,54 @@ the script above will output two JSON files:
- `./deployments/l2_genesis_alloc.json`: the `alloc` field which will be used in L2 genesis JSON file
- `./deployments/l2_genesis_storage_layout.json`: the storage layout of those pre-deployed contracts


## Github Actions

Each commit will automatically trigger the GitHub Actions to run. If any commit message in your push or the HEAD commit of your PR contains the strings [skip ci], [ci skip], [no ci], [skip actions], or [actions skip] workflows triggered on the push or pull_request events will be skipped.

## Contract Flow

### Bridge

`contracts/bridge/Bridge.sol` is to be deployed on both the Layer 1 and Layer 2 chains.

#### Bridging Ether

To bridge from Layer 1 to Layer 2, the process starts at `Bridge.sendMessage(message)` on Layer 1. The message must have a `depositValue`, `callValue`, and `processingFee` that sum up to the amount of Ether `msg.value`. The `destChainId` of the message must be different than `block.chainid`, and the chainId must be enabled in `state.destChains` by calling `Bridge.enableDestChain(chainId)`.

The `msg.value` amount of Ether will be sent to the Layer1 `EtherVault` contract to be held. Then, the mesage will be hashed and stored as a `signal`.

The bridge will use `LibBridgeSignal.sendSignal(bridgeContractAddress, signalHash)` to store the signal being sent as a boolean `1`, and then use `LibBridgeData.MessageSent(signal, message)` event as an indicator to an off-chain relayer that a message has been sent to be processed on Layer 2.

On the Layer 2 Bridge, the relayer, upon receipt of the Layer 1 `LibBridgeData.MessageSent` event, `Bridge.processMessage(message, proof)` must be called. A proof must be generated to pass in as the `proof`. `processMessage` will use `LibBridgeProcess.processMessage(state, addressResolver, message, proof)` to verify the message is valid.

`LibBridgeProces.processMessage` requires the message's `destChainId`, set earlier, is equal to the current `block.chainid`. Then, it will hash the message to generate the `signal`, and then require the internal `state.messageStatus` of the signal is equal to `LibBridgeData.MessageStatus.NEW` to ensure the message has not been processed already.

It will then require that the signal has been received, using `LibBridgeSignal.isSignalReceived(resolver, srcBridge, sender, signal, proof)`, and, using Merkle Trie verification via `LibTrieProof`, and the synced header hash from the `TaikoL2` contract, make sure the block has been synced to by comparing the merkle proof block header hash. The proof generated is proof that the storage of the `srcBridge` address contains a key, generated wth `_key(sender, signal)` on LibBridgeSignal, is equal to the value of `bytes32(uint256(1))`, proving that bridge has sent the signal from the Layer 1 chain. You can generate the proof with `eth_getProof` RPC call to Layer 1. The `LibTrieProof` expects the proof to be RLP encoded and concatenated together. In Javascript this looks like:

```ts
const proof = await ethers.provider.send("eth_getProof", [
bridge.address,
[key],
block.hash,
])

// RLP encode the proof together for LibTrieProof to decode
const encodedProof = ethers.utils.defaultAbiCoder.encode(
["bytes", "bytes"],
[RLP.encode(proof.accountProof), RLP.encode(proof.storageProof[0].proof)]
)
```

If that passes verification, the `EtherVault.receiveEither()` call is made to receive Ether on Layer 2. Then, the ether is sent to the `message.owner`.

The message status is updated if it succeeded to `LibBridgeData.MessageStatus.Done`, otherwise, to `LibBridgeData.MessageStatus.RETRIABLE`. Either way, `processMessage` can never be called again for this message. If it failed, caller must use `retryMessage` instead.

To finish off, a refund is calculated and sent to the `message.refundAddress`, or if it is not sent, the `message.owner`. In either case, the `msg.sender` gets the `message.processingFee` for being the first attempt relayer, and the `refundAddress` gets the `refundAmount` if it exists.

The funds should be successfully bridged now.

#### Bridging ERC20

#### Bridging ERC721

#### Bridging ERC1155
20 changes: 10 additions & 10 deletions packages/protocol/contracts/L1/v1/V1Proving.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import "../../libs/LibReceiptDecoder.sol";
import "../../libs/LibTxDecoder.sol";
import "../../libs/LibTxUtils.sol";
import "../../libs/LibZKP.sol";
import "../../thirdparty/Lib_BytesUtils.sol";
import "../../thirdparty/Lib_MerkleTrie.sol";
import "../../thirdparty/Lib_RLPWriter.sol";
import "../../thirdparty/LibBytesUtils.sol";
import "../../thirdparty/LibMerkleTrie.sol";
import "../../thirdparty/LibRLPWriter.sol";
import "../LibData.sol";

/// @author dantaik <dan@taiko.xyz>
Expand Down Expand Up @@ -80,7 +80,7 @@ library V1Proving {

// Check anchor tx's calldata is valid
require(
Lib_BytesUtils.equal(
LibBytesUtils.equal(
_tx.data,
bytes.concat(
LibConstants.V1_ANCHOR_TX_SELECTOR,
Expand All @@ -93,8 +93,8 @@ library V1Proving {

// Check anchor tx is the 1st tx in the block
require(
Lib_MerkleTrie.verifyInclusionProof(
Lib_RLPWriter.writeUint(0),
LibMerkleTrie.verifyInclusionProof(
LibRLPWriter.writeUint(0),
anchorTx,
evidence.proofs[1],
evidence.header.transactionsRoot
Expand All @@ -108,8 +108,8 @@ library V1Proving {

require(receipt.status == 1, "L1:receipt:status");
require(
Lib_MerkleTrie.verifyInclusionProof(
Lib_RLPWriter.writeUint(0),
LibMerkleTrie.verifyInclusionProof(
LibRLPWriter.writeUint(0),
anchorReceipt,
evidence.proofs[2],
evidence.header.receiptsRoot
Expand Down Expand Up @@ -163,8 +163,8 @@ library V1Proving {

// Check the event is the first one in the throw-away block
require(
Lib_MerkleTrie.verifyInclusionProof(
Lib_RLPWriter.writeUint(0),
LibMerkleTrie.verifyInclusionProof(
LibRLPWriter.writeUint(0),
invalidateBlockReceipt,
evidence.proofs[1],
evidence.header.receiptsRoot
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/bridge/BridgedERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract BridgedERC20 is
string memory _name
) external initializer {
require(
srcToken != address(0) &&
_srcToken != address(0) &&
_srcChainId != 0 &&
_srcChainId != block.chainid &&
bytes(_symbol).length > 0 &&
Expand Down
2 changes: 1 addition & 1 deletion packages/protocol/contracts/libs/LibAnchorSignature.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// ╱╱┃┃┃╭╮┃┃╭╮┫╰╯┃┃╰━╯┃╭╮┃╰╯┣━━┃
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;
import "../thirdparty/Lib_Uint512.sol";
import "../thirdparty/LibUint512.sol";

/// @author david <david@taiko.xyz>
library LibAnchorSignature {
Expand Down
34 changes: 17 additions & 17 deletions packages/protocol/contracts/libs/LibBlockHeader.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;

import "../thirdparty/Lib_RLPWriter.sol";
import "../thirdparty/LibRLPWriter.sol";
import "./LibConstants.sol";

/// @author david <david@taiko.xyz>
Expand Down Expand Up @@ -40,25 +40,25 @@ library LibBlockHeader {
returns (bytes32)
{
bytes[] memory list = new bytes[](15);
list[0] = Lib_RLPWriter.writeHash(header.parentHash);
list[1] = Lib_RLPWriter.writeHash(header.ommersHash);
list[2] = Lib_RLPWriter.writeAddress(header.beneficiary);
list[3] = Lib_RLPWriter.writeHash(header.stateRoot);
list[4] = Lib_RLPWriter.writeHash(header.transactionsRoot);
list[5] = Lib_RLPWriter.writeHash(header.receiptsRoot);
list[6] = Lib_RLPWriter.writeBytes(abi.encodePacked(header.logsBloom));
list[7] = Lib_RLPWriter.writeUint(header.difficulty);
list[8] = Lib_RLPWriter.writeUint(header.height);
list[9] = Lib_RLPWriter.writeUint64(header.gasLimit);
list[10] = Lib_RLPWriter.writeUint64(header.gasUsed);
list[11] = Lib_RLPWriter.writeUint64(header.timestamp);
list[12] = Lib_RLPWriter.writeBytes(header.extraData);
list[13] = Lib_RLPWriter.writeHash(header.mixHash);
list[0] = LibRLPWriter.writeHash(header.parentHash);
list[1] = LibRLPWriter.writeHash(header.ommersHash);
list[2] = LibRLPWriter.writeAddress(header.beneficiary);
list[3] = LibRLPWriter.writeHash(header.stateRoot);
list[4] = LibRLPWriter.writeHash(header.transactionsRoot);
list[5] = LibRLPWriter.writeHash(header.receiptsRoot);
list[6] = LibRLPWriter.writeBytes(abi.encodePacked(header.logsBloom));
list[7] = LibRLPWriter.writeUint(header.difficulty);
list[8] = LibRLPWriter.writeUint(header.height);
list[9] = LibRLPWriter.writeUint64(header.gasLimit);
list[10] = LibRLPWriter.writeUint64(header.gasUsed);
list[11] = LibRLPWriter.writeUint64(header.timestamp);
list[12] = LibRLPWriter.writeBytes(header.extraData);
list[13] = LibRLPWriter.writeHash(header.mixHash);
// According to the ethereum yellow paper, we should treat `nonce`
// as [8]byte when hashing the block.
list[14] = Lib_RLPWriter.writeBytes(abi.encodePacked(header.nonce));
list[14] = LibRLPWriter.writeBytes(abi.encodePacked(header.nonce));

bytes memory rlpHeader = Lib_RLPWriter.writeList(list);
bytes memory rlpHeader = LibRLPWriter.writeList(list);
return keccak256(rlpHeader);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/protocol/contracts/libs/LibInvalidTxList.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ pragma solidity ^0.8.9;
import "../libs/LibConstants.sol";
import "../libs/LibTxDecoder.sol";
import "../libs/LibTxUtils.sol";
import "../thirdparty/Lib_RLPReader.sol";
import "../thirdparty/Lib_RLPWriter.sol";
import "../thirdparty/LibRLPReader.sol";
import "../thirdparty/LibRLPWriter.sol";

/// @dev A library to invalidate a txList using the following rules:
/// @author david <david@taiko.xyz>
Expand Down
32 changes: 16 additions & 16 deletions packages/protocol/contracts/libs/LibReceiptDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;

import "../thirdparty/Lib_BytesUtils.sol";
import "../thirdparty/Lib_RLPReader.sol";
import "../thirdparty/LibBytesUtils.sol";
import "../thirdparty/LibRLPReader.sol";

/// @author david <david@taiko.xyz>
library LibReceiptDecoder {
Expand All @@ -32,61 +32,61 @@ library LibReceiptDecoder {
returns (Receipt memory receipt)
{
// Non-legacy transaction receipts should remove the type prefix at first.
Lib_RLPReader.RLPItem[] memory rlpItems = Lib_RLPReader.readList(
LibRLPReader.RLPItem[] memory rlpItems = LibRLPReader.readList(
encoded[0] >= 0x0 && encoded[0] <= 0x7f
? Lib_BytesUtils.slice(encoded, 1)
? LibBytesUtils.slice(encoded, 1)
: encoded
);

require(rlpItems.length == 4, "invalid items length");

receipt.status = uint64(Lib_RLPReader.readUint256(rlpItems[0]));
receipt.status = uint64(LibRLPReader.readUint256(rlpItems[0]));
receipt.cumulativeGasUsed = uint64(
Lib_RLPReader.readUint256(rlpItems[1])
LibRLPReader.readUint256(rlpItems[1])
);
receipt.logsBloom = decodeLogsBloom(rlpItems[2]);
receipt.logs = decodeLogs(Lib_RLPReader.readList(rlpItems[3]));
receipt.logs = decodeLogs(LibRLPReader.readList(rlpItems[3]));
}

function decodeLogsBloom(Lib_RLPReader.RLPItem memory logsBloomRlp)
function decodeLogsBloom(LibRLPReader.RLPItem memory logsBloomRlp)
internal
pure
returns (bytes32[8] memory logsBloom)
{
bytes memory bloomBytes = Lib_RLPReader.readBytes(logsBloomRlp);
bytes memory bloomBytes = LibRLPReader.readBytes(logsBloomRlp);
require(bloomBytes.length == 256, "invalid logs bloom");

return abi.decode(bloomBytes, (bytes32[8]));
}

function decodeLogs(Lib_RLPReader.RLPItem[] memory logsRlp)
function decodeLogs(LibRLPReader.RLPItem[] memory logsRlp)
internal
pure
returns (Log[] memory)
{
Log[] memory logs = new Log[](logsRlp.length);

for (uint256 i = 0; i < logsRlp.length; i++) {
Lib_RLPReader.RLPItem[] memory rlpItems = Lib_RLPReader.readList(
LibRLPReader.RLPItem[] memory rlpItems = LibRLPReader.readList(
logsRlp[i]
);
logs[i].contractAddress = Lib_RLPReader.readAddress(rlpItems[0]);
logs[i].topics = decodeTopics(Lib_RLPReader.readList(rlpItems[1]));
logs[i].data = Lib_RLPReader.readBytes(rlpItems[2]);
logs[i].contractAddress = LibRLPReader.readAddress(rlpItems[0]);
logs[i].topics = decodeTopics(LibRLPReader.readList(rlpItems[1]));
logs[i].data = LibRLPReader.readBytes(rlpItems[2]);
}

return logs;
}

function decodeTopics(Lib_RLPReader.RLPItem[] memory topicsRlp)
function decodeTopics(LibRLPReader.RLPItem[] memory topicsRlp)
internal
pure
returns (bytes32[] memory)
{
bytes32[] memory topics = new bytes32[](topicsRlp.length);

for (uint256 i = 0; i < topicsRlp.length; i++) {
topics[i] = Lib_RLPReader.readBytes32(topicsRlp[i]);
topics[i] = LibRLPReader.readBytes32(topicsRlp[i]);
}

return topics;
Expand Down
16 changes: 8 additions & 8 deletions packages/protocol/contracts/libs/LibTrieProof.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
// ╱╱╰╯╰╯╰┻┻╯╰┻━━╯╰━━━┻╯╰┻━━┻━━╯
pragma solidity ^0.8.9;

import "../thirdparty/Lib_RLPReader.sol";
import "../thirdparty/Lib_RLPWriter.sol";
import "../thirdparty/Lib_SecureMerkleTrie.sol";
import "../thirdparty/LibRLPReader.sol";
import "../thirdparty/LibRLPWriter.sol";
import "../thirdparty/LibSecureMerkleTrie.sol";

/// @author dantaik <dan@taiko.xyz>
library LibTrieProof {
Expand Down Expand Up @@ -46,24 +46,24 @@ library LibTrieProof {
(bytes, bytes)
);

(bool exists, bytes memory rlpAccount) = Lib_SecureMerkleTrie.get(
(bool exists, bytes memory rlpAccount) = LibSecureMerkleTrie.get(
abi.encodePacked(addr),
accountProof,
stateRoot
);

require(exists, "LTP:invalid account proof");

Lib_RLPReader.RLPItem[] memory accountState = Lib_RLPReader.readList(
LibRLPReader.RLPItem[] memory accountState = LibRLPReader.readList(
rlpAccount
);
bytes32 storageRoot = Lib_RLPReader.readBytes32(
bytes32 storageRoot = LibRLPReader.readBytes32(
accountState[ACCOUNT_FIELD_INDEX_STORAGE_HASH]
);

bool verified = Lib_SecureMerkleTrie.verifyInclusionProof(
bool verified = LibSecureMerkleTrie.verifyInclusionProof(
abi.encodePacked(key),
Lib_RLPWriter.writeBytes32(value),
LibRLPWriter.writeBytes32(value),
storageProof,
storageRoot
);
Expand Down