-
Notifications
You must be signed in to change notification settings - Fork 3.9k
op-chain-ops: cross domain message #3462
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
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
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 hidden or 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,72 @@ | ||
| package crossdomain | ||
|
|
||
| import ( | ||
| "math/big" | ||
| "strings" | ||
|
|
||
| "github.com/ethereum/go-ethereum/accounts/abi" | ||
| "github.com/ethereum/go-ethereum/common" | ||
| ) | ||
|
|
||
| var ( | ||
| // NonceMask represents a mask used to extract version bytes from the nonce | ||
| NonceMask, _ = new(big.Int).SetString("0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16) | ||
| // relayMessage0ABI represents the v0 relay message encoding | ||
| relayMessage0ABI = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_target\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_message\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_messageNonce\",\"type\":\"uint256\"}],\"name\":\"relayMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" | ||
| // relayMessage1ABI represents the v1 relay message encoding | ||
| relayMessage1ABI = "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_minGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_message\",\"type\":\"bytes\"}],\"name\":\"relayMessage\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}]" | ||
| // relayMessage0 represents the ABI of relay message v0 | ||
| relayMessage0 abi.ABI | ||
| // relayMessage1 represents the ABI of relay message v1 | ||
| relayMessage1 abi.ABI | ||
| ) | ||
|
|
||
| // Create the required ABIs | ||
| func init() { | ||
| var err error | ||
| relayMessage0, err = abi.JSON(strings.NewReader(relayMessage0ABI)) | ||
| if err != nil { | ||
| panic(err) | ||
tynes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| relayMessage1, err = abi.JSON(strings.NewReader(relayMessage1ABI)) | ||
| if err != nil { | ||
| panic(err) | ||
| } | ||
| } | ||
|
|
||
| // EncodeCrossDomainMessageV0 will encode the calldata for | ||
| // "relayMessage(address,address,bytes,uint256)", | ||
| func EncodeCrossDomainMessageV0( | ||
| target *common.Address, | ||
| sender *common.Address, | ||
| message []byte, | ||
| nonce *big.Int, | ||
| ) ([]byte, error) { | ||
| return relayMessage0.Pack("relayMessage", target, sender, message, nonce) | ||
| } | ||
|
|
||
| // EncodeCrossDomainMessageV1 will encode the calldata for | ||
| // "relayMessage(uint256,address,address,uint256,uint256,bytes)", | ||
| func EncodeCrossDomainMessageV1( | ||
| nonce *big.Int, | ||
| sender *common.Address, | ||
| target *common.Address, | ||
| value *big.Int, | ||
| gasLimit *big.Int, | ||
| data []byte, | ||
| ) ([]byte, error) { | ||
| return relayMessage1.Pack("relayMessage", nonce, sender, target, value, gasLimit, data) | ||
| } | ||
|
|
||
| // DecodeVersionedNonce will decode the version that is encoded in the nonce | ||
| func DecodeVersionedNonce(versioned *big.Int) (*big.Int, *big.Int) { | ||
| nonce := new(big.Int).And(versioned, NonceMask) | ||
| version := new(big.Int).Rsh(versioned, 240) | ||
| return nonce, version | ||
| } | ||
|
|
||
| // EncodeVersionedNonce will encode the version into the nonce | ||
| func EncodeVersionedNonce(nonce, version *big.Int) *big.Int { | ||
| shifted := new(big.Int).Lsh(version, 240) | ||
| return new(big.Int).Or(nonce, shifted) | ||
| } | ||
This file contains hidden or 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,30 @@ | ||
| package crossdomain_test | ||
|
|
||
| import ( | ||
| "math/big" | ||
| "testing" | ||
|
|
||
| "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func FuzzVersionedNonce(f *testing.F) { | ||
| f.Fuzz(func(t *testing.T, _nonce []byte, _version uint16) { | ||
| inputNonce := new(big.Int).SetBytes(_nonce) | ||
|
|
||
| // Clamp nonce to uint240 | ||
| if inputNonce.Cmp(crossdomain.NonceMask) > 0 { | ||
| inputNonce = new(big.Int).Set(crossdomain.NonceMask) | ||
| } | ||
| // Clamp version to 0 or 1 | ||
| _version = _version % 2 | ||
|
|
||
| inputVersion := new(big.Int).SetUint64(uint64(_version)) | ||
| encodedNonce := crossdomain.EncodeVersionedNonce(inputNonce, inputVersion) | ||
|
|
||
| decodedNonce, decodedVersion := crossdomain.DecodeVersionedNonce(encodedNonce) | ||
|
|
||
| require.Equal(t, decodedNonce.Uint64(), inputNonce.Uint64()) | ||
| require.Equal(t, decodedVersion.Uint64(), inputVersion.Uint64()) | ||
| }) | ||
| } |
This file contains hidden or 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,42 @@ | ||
| package crossdomain | ||
|
|
||
| import ( | ||
| "math/big" | ||
|
|
||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/crypto" | ||
| ) | ||
|
|
||
| // HashCrossDomainMessageV0 computes the pre bedrock cross domain messaging | ||
| // hashing scheme. | ||
| func HashCrossDomainMessageV0( | ||
| target *common.Address, | ||
| sender *common.Address, | ||
| data []byte, | ||
| nonce *big.Int, | ||
| ) (common.Hash, error) { | ||
| encoded, err := EncodeCrossDomainMessageV0(target, sender, data, nonce) | ||
| if err != nil { | ||
| return common.Hash{}, err | ||
| } | ||
| hash := crypto.Keccak256(encoded) | ||
| return common.BytesToHash(hash), nil | ||
tynes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // HashCrossDomainMessageV1 computes the first post bedrock cross domain | ||
| // messaging hashing scheme. | ||
| func HashCrossDomainMessageV1( | ||
| nonce *big.Int, | ||
| sender *common.Address, | ||
| target *common.Address, | ||
| value *big.Int, | ||
| gasLimit *big.Int, | ||
| data []byte, | ||
| ) (common.Hash, error) { | ||
| encoded, err := EncodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, data) | ||
| if err != nil { | ||
| return common.Hash{}, err | ||
| } | ||
| hash := crypto.Keccak256(encoded) | ||
| return common.BytesToHash(hash), nil | ||
| } | ||
This file contains hidden or 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,72 @@ | ||
| package crossdomain | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "math/big" | ||
|
|
||
| "github.com/ethereum/go-ethereum/common" | ||
| ) | ||
|
|
||
| // CrossDomainMessage represents a cross domain message | ||
| // used by the CrossDomainMessenger. The version is encoded | ||
| // in the nonce. Version 0 messages do not have a value, | ||
| // version 1 messages have a value and the most significant | ||
| // byte of the nonce is a 1 | ||
| type CrossDomainMessage struct { | ||
| Nonce *big.Int | ||
| Sender *common.Address | ||
| Target *common.Address | ||
| Value *big.Int | ||
| GasLimit *big.Int | ||
| Data []byte | ||
| } | ||
|
|
||
| // NewCrossDomainMessage creates a CrossDomainMessage. | ||
| func NewCrossDomainMessage( | ||
| nonce *big.Int, | ||
| sender, target *common.Address, | ||
| value, gasLimit *big.Int, | ||
| data []byte, | ||
| ) *CrossDomainMessage { | ||
| return &CrossDomainMessage{ | ||
| Nonce: nonce, | ||
| Sender: sender, | ||
| Target: target, | ||
| Value: value, | ||
| GasLimit: gasLimit, | ||
| Data: data, | ||
| } | ||
| } | ||
|
|
||
| // Version will return the version of the CrossDomainMessage. | ||
| // It does this by looking at the first byte of the nonce. | ||
| func (c *CrossDomainMessage) Version() uint64 { | ||
| _, version := DecodeVersionedNonce(c.Nonce) | ||
| return version.Uint64() | ||
| } | ||
|
|
||
| // Encode will encode a cross domain message based on the version. | ||
| func (c *CrossDomainMessage) Encode() ([]byte, error) { | ||
| version := c.Version() | ||
| switch version { | ||
| case 0: | ||
| return EncodeCrossDomainMessageV0(c.Target, c.Sender, c.Data, c.Nonce) | ||
| case 1: | ||
| return EncodeCrossDomainMessageV1(c.Nonce, c.Sender, c.Target, c.Value, c.GasLimit, c.Data) | ||
| default: | ||
| return nil, fmt.Errorf("unknown nonce version %d", version) | ||
| } | ||
| } | ||
|
|
||
| // Hash will compute the hash of the CrossDomainMessage | ||
| func (c *CrossDomainMessage) Hash() (common.Hash, error) { | ||
| version := c.Version() | ||
| switch version { | ||
| case 0: | ||
| return HashCrossDomainMessageV0(c.Target, c.Sender, c.Data, c.Nonce) | ||
| case 1: | ||
| return HashCrossDomainMessageV1(c.Nonce, c.Sender, c.Target, c.Value, c.GasLimit, c.Data) | ||
| default: | ||
| return common.Hash{}, fmt.Errorf("unknown nonce version %d", version) | ||
| } | ||
| } |
This file contains hidden or 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,100 @@ | ||
| package crossdomain_test | ||
|
|
||
| import ( | ||
| "math/big" | ||
| "testing" | ||
|
|
||
| "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" | ||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/common/hexutil" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // TestEncode tests the encoding of a CrossDomainMessage. The assertion was | ||
| // created using solidity. | ||
| func TestEncode(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| t.Run("V0", func(t *testing.T) { | ||
| msg := crossdomain.NewCrossDomainMessage( | ||
| crossdomain.EncodeVersionedNonce(common.Big0, common.Big0), | ||
| &common.Address{}, | ||
| &common.Address{19: 0x01}, | ||
| big.NewInt(0), | ||
| big.NewInt(5), | ||
| []byte{}, | ||
| ) | ||
|
|
||
| require.Equal(t, uint64(0), msg.Version()) | ||
|
|
||
| encoded, err := msg.Encode() | ||
| require.Nil(t, err) | ||
|
|
||
| expect := hexutil.MustDecode("0xcbd4ece900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") | ||
| require.Equal(t, expect, encoded) | ||
| }) | ||
|
|
||
| t.Run("V1", func(t *testing.T) { | ||
| msg := crossdomain.NewCrossDomainMessage( | ||
| crossdomain.EncodeVersionedNonce(common.Big1, common.Big1), | ||
| &common.Address{19: 0x01}, | ||
| &common.Address{19: 0x02}, | ||
| big.NewInt(100), | ||
| big.NewInt(555), | ||
| []byte{}, | ||
| ) | ||
|
|
||
| require.Equal(t, uint64(1), msg.Version()) | ||
|
|
||
| encoded, err := msg.Encode() | ||
| require.Nil(t, err) | ||
|
|
||
| expect := hexutil.MustDecode("0xd764ad0b0001000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000022b00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000") | ||
|
|
||
| require.Equal(t, expect, encoded) | ||
| }) | ||
| } | ||
|
|
||
| // TestEncode tests the hash of a CrossDomainMessage. The assertion was | ||
| // created using solidity. | ||
| func TestHash(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| t.Run("V0", func(t *testing.T) { | ||
| msg := crossdomain.NewCrossDomainMessage( | ||
| crossdomain.EncodeVersionedNonce(common.Big0, common.Big0), | ||
| &common.Address{}, | ||
| &common.Address{19: 0x01}, | ||
| big.NewInt(10), | ||
| big.NewInt(5), | ||
| []byte{}, | ||
| ) | ||
|
|
||
| require.Equal(t, uint64(0), msg.Version()) | ||
|
|
||
| hash, err := msg.Hash() | ||
| require.Nil(t, err) | ||
|
|
||
| expect := common.HexToHash("0x5bb579a193681e7c4d43c8c2e4bc6c2c447d21ef9fa887ca23b2d3f9a0fac065") | ||
| require.Equal(t, expect, hash) | ||
| }) | ||
|
|
||
| t.Run("V1", func(t *testing.T) { | ||
| msg := crossdomain.NewCrossDomainMessage( | ||
| crossdomain.EncodeVersionedNonce(common.Big0, common.Big1), | ||
| &common.Address{}, | ||
| &common.Address{19: 0x01}, | ||
| big.NewInt(0), | ||
| big.NewInt(5), | ||
| []byte{}, | ||
| ) | ||
|
|
||
| require.Equal(t, uint64(1), msg.Version()) | ||
|
|
||
| hash, err := msg.Hash() | ||
| require.Nil(t, err) | ||
|
|
||
| expect := common.HexToHash("0x09bbda7f59cdaccab5c41cab4600bd458b2bd7d9f8410f13316fe07e5f4237cc") | ||
| require.Equal(t, expect, hash) | ||
| }) | ||
| } | ||
tynes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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.
Uh oh!
There was an error while loading. Please reload this page.