forked from paradigmxyz/reth
-
Notifications
You must be signed in to change notification settings - Fork 39
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
feat(gwyneth): incorporate booster sample contracts #26
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
fa9f982
Integrate and make contracts compiling
853e2cd
move sync-only consuem and write to XChain
784f613
add xtransfer and token example
aed907b
meeting outcomes
4f649b3
Merge pull request #28 from taikoxyz/modify_erc20_xchain_version_inhe…
adaki2004 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import "../gwyneth/XChainToken.sol"; | ||
|
||
contract xErc20Example is ERC20, XChainToken { | ||
constructor() ERC20("xERC20", "xERC") { | ||
_mint(msg.sender, 100_000_000_000 * 10**18 ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity >=0.8.12 <0.9.0; | ||
|
||
import "./XChain.sol"; | ||
|
||
contract Bus is XChain { | ||
// Messages are stored only on the source chain for ASYNC messages. | ||
// In SYNC mode, the message is stored on both the source and the target chain. | ||
bytes32[] public messages; | ||
|
||
// Stored only on the target chain | ||
mapping (bytes32 => bool) public consumed; | ||
|
||
enum ProofType { | ||
INVALID, | ||
ASYNC, | ||
SYNC | ||
} | ||
|
||
function isMessageSent(bytes32 messageHash, uint busID) external view returns (bool) { | ||
return messages[busID] == messageHash; | ||
} | ||
|
||
function write(bytes memory message) public override returns (uint) { | ||
messages.push(calcMessageHash(message)); | ||
return messages.length - 1; | ||
} | ||
|
||
function consume(uint fromChainId, bytes memory message, bytes calldata proof) public override { | ||
ProofType proofType = ProofType(uint16(bytes2(proof[:2]))); | ||
if (proofType == ProofType.ASYNC) { | ||
// Decode the proof | ||
AsyncBusProof memory busProof = abi.decode(proof[2:], (AsyncBusProof)); | ||
|
||
// Calculate the message hash | ||
bytes32 messageHash = calcMessageHash(message); | ||
|
||
// Do the call on the source chain to see if the message was sent there | ||
xCallOptions(fromChainId, true, busProof.boosterCallProof); | ||
bool isSent = this.isMessageSent(messageHash, busProof.busID); | ||
require(isSent == true); | ||
|
||
// Make sure this is the first and last time this message is consumed | ||
require(consumed[messageHash] == false); | ||
consumed[messageHash] = true; | ||
} else if (proofType == ProofType.SYNC) { | ||
// Sync system with shared validity | ||
write(message); | ||
} else { | ||
revert("INVALID BUS PROOF"); | ||
} | ||
} | ||
|
||
function calcMessageHash(bytes memory message) internal view returns (bytes32) { | ||
return keccak256(abi.encode(EVM.chainId(), msg.sender, message)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity >=0.8.12 <0.9.0; | ||
|
||
// EVM library | ||
library EVM { | ||
// precompile addresses | ||
address constant xCallOptionsAddress = address(0x1100); | ||
|
||
uint constant l1ChainId = 1; | ||
uint constant version = 1; | ||
|
||
function xCallOnL1() | ||
public | ||
view | ||
{ | ||
xCallOptions(l1ChainId); | ||
} | ||
|
||
function xCallOptions(uint chainID) | ||
public | ||
view | ||
{ | ||
xCallOptions(chainID, true); | ||
} | ||
|
||
function xCallOptions(uint chainID, bool sandbox) | ||
public | ||
view | ||
{ | ||
xCallOptions(chainID, sandbox, address(0), address(0)); | ||
} | ||
|
||
function xCallOptions(uint chainID, bool sandbox, address txOrigin, address msgSender) | ||
public | ||
view | ||
{ | ||
xCallOptions(chainID, sandbox, txOrigin, msgSender, 0x0, ""); | ||
} | ||
|
||
function xCallOptions(uint chainID, bool sandbox, bytes32 blockHash, bytes memory proof) | ||
public | ||
view | ||
{ | ||
xCallOptions(chainID, sandbox, address(0), address(0), blockHash, proof); | ||
} | ||
|
||
function xCallOptions(uint chainID, bool sandbox, address txOrigin, address msgSender, bytes32 blockHash, bytes memory proof) | ||
public | ||
view | ||
{ | ||
// This precompile is not supported on L1 | ||
require(chainID != l1ChainId); | ||
|
||
// Call the custom precompile | ||
bytes memory input = abi.encodePacked(version, chainID, sandbox, txOrigin, msgSender, blockHash, proof); | ||
(bool success, ) = xCallOptionsAddress.staticcall(input); | ||
require(success); | ||
} | ||
|
||
function isOnL1() public view returns (bool) { | ||
return chainId() == l1ChainId; | ||
} | ||
|
||
function chainId() public view returns (uint256) { | ||
return block.chainid; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity >=0.8.12 <0.9.0; | ||
|
||
import "./EVM.sol"; | ||
|
||
contract XChain { | ||
struct XChainCallProof { | ||
uint chainID; | ||
uint blockID; | ||
bytes callProof; | ||
} | ||
|
||
struct AsyncBusProof { | ||
uint busID; | ||
bytes boosterCallProof; | ||
} | ||
|
||
struct AsyncBusProofV2 { | ||
uint blockNumber; | ||
uint busID; | ||
} | ||
|
||
// Only stored on L1 | ||
// Currently getBlockHash() is not supported via the new Taiko Gwyneth | ||
//ITaiko public taiko; | ||
// todo (@Brecht): XChain has a bus property but Bus is an XChain (inherits). It does not make too much sense to me, or maybe i'm missing the point ? | ||
//Bus public bus; | ||
|
||
// Event that is logged when a transaction on a chain also needs to be executed on another chain | ||
event ExecuteNextOn(uint chainID, address from, address target, bytes callData); | ||
|
||
error FUNC_NOT_IMPLEMENTED(); | ||
|
||
function init(/*ITaiko _taiko*/) | ||
internal | ||
{ | ||
//taiko = _taiko; | ||
} | ||
|
||
modifier notImplemented() { | ||
revert FUNC_NOT_IMPLEMENTED(); | ||
_; | ||
} | ||
|
||
// xExecuteOn functions need | ||
// - to be external | ||
modifier xExecuteOn(uint chainID) { | ||
if (EVM.chainId() == chainID) { | ||
_; | ||
} else { | ||
EVM.xCallOptions(chainID, true); | ||
(bool success, bytes memory data) = address(this).staticcall(msg.data); | ||
require(success); | ||
// Just pass through the return data | ||
assembly { | ||
return(add(data, 32), mload(data)) | ||
} | ||
} | ||
} | ||
|
||
// xFunctions functions need | ||
// - to be external | ||
// - to have `bytes proof` as the last function argument | ||
modifier xFunction(uint fromChainId, uint toChainId, bytes calldata proof) { | ||
if (fromChainId != toChainId) { | ||
// Remove the proof data from the message data | ||
// Bytes arays are padded to 32 bytes and start with a 32 byte length value | ||
uint messageLength = msg.data.length - ((proof.length + 31) / 32 + 1) * 32; | ||
bytes memory message = msg.data; | ||
assembly { | ||
mstore(message, messageLength) | ||
} | ||
|
||
// Use the bus to communicate between chains | ||
if (EVM.chainId() == fromChainId) { | ||
uint busID = write(message); | ||
|
||
// Always suggest doing an async proof for now on the target chain | ||
AsyncBusProofV2 memory asyncProof = AsyncBusProofV2({ | ||
busID: busID, | ||
blockNumber: block.number | ||
}); | ||
bytes memory encodedProof = abi.encode(asyncProof); | ||
bytes memory callData = bytes(string.concat(string(new bytes(0x0001)), string(message), string(encodedProof))); | ||
emit ExecuteNextOn(toChainId, address(0), address(this), callData); | ||
} else if (EVM.chainId() == toChainId) { | ||
consume(fromChainId, message, proof); | ||
} else { | ||
revert(); | ||
} | ||
} | ||
_; | ||
} | ||
|
||
// These could also be exposed using a precompile because we could get them from public input, | ||
// but that requires extra work so let's just fetch them from L1 for now | ||
function getBlockHash(uint chainID, uint blockID) external view xExecuteOn(EVM.l1ChainId) returns (bytes32) { | ||
// todo(@Brecht): Currently not supported or well, at least TaikoL1 does not have it with the current design. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, outdated! Hmmmm only needed for async case so let's skip for now. |
||
//return taiko.getBlockHash(chainID, blockID); | ||
} | ||
|
||
// Supports setting the call options using any L2 in the booster network. | ||
// This is done by first checking the validity of the blockhash of the specified L2. | ||
function xCallOptions(uint chainID, bool sandbox, bytes memory proof) internal view { | ||
// Decode the proof | ||
XChainCallProof memory chainCallProof = abi.decode(proof, (XChainCallProof)); | ||
require(chainID == chainCallProof.chainID); | ||
|
||
// If the source chain isn't L1, go fetch the block header of the L2 stored on L1 | ||
bytes32 blockHash = 0x0; | ||
if (chainID != EVM.l1ChainId) { | ||
|
||
blockHash = this.getBlockHash(chainID, chainCallProof.blockID); | ||
} | ||
|
||
// Do the call on the specified chain | ||
EVM.xCallOptions(chainID, sandbox, blockHash, chainCallProof.callProof); | ||
} | ||
|
||
// todo (@Brecht): | ||
// There was a circular reference (XBus inherits from XChain, while also XChain has a XBus property, so i made these to compile) | ||
// They will be inherited in XBus, but basically XBus can be incorporated into XChain, no ? | ||
function write(bytes memory message) public virtual notImplemented returns (uint) {} | ||
function consume(uint fromChainId, bytes memory message, bytes calldata proof) public virtual notImplemented {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity >=0.8.12 <0.9.0; | ||
|
||
import "./XChain.sol"; | ||
|
||
contract XChainToken is XChain { | ||
// Only stored on L1 | ||
uint private _totalBalance; | ||
// Stored on all chains | ||
mapping(address => uint) public balances; | ||
|
||
function totalBalance() | ||
xExecuteOn(EVM.l1ChainId) | ||
external | ||
view | ||
returns (uint) | ||
{ | ||
return _totalBalance; | ||
} | ||
|
||
function xtransfer(address to, uint amount, uint256 fromChainId, uint256 toChainId, bytes calldata proof) | ||
xFunction(fromChainId, toChainId, proof) | ||
external | ||
{ | ||
if (EVM.chainId() == fromChainId) { | ||
balances[msg.sender] -= amount; | ||
} | ||
if (EVM.chainId() == toChainId) { | ||
balances[to] += amount; | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bus might still be needed for async transactions to pass data from one chain to the other, but I guess to keep things simple let's assume synchronous everywhere for now.