Commit f5765f4c authored by clabby's avatar clabby

Go impl of `getProveWithdrawalTransactionInputs` diff method

parent 5b9728d3
...@@ -477,16 +477,15 @@ contract FFIInterface is Test { ...@@ -477,16 +477,15 @@ contract FFIInterface is Test {
bytes[] memory bytes[] memory
) )
{ {
string[] memory cmds = new string[](9); string[] memory cmds = new string[](8);
cmds[0] = "node"; cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "dist/scripts/differential-testing.js"; cmds[1] = "getProveWithdrawalTransactionInputs";
cmds[2] = "getProveWithdrawalTransactionInputs"; cmds[2] = vm.toString(_tx.nonce);
cmds[3] = vm.toString(_tx.nonce); cmds[3] = vm.toString(_tx.sender);
cmds[4] = vm.toString(_tx.sender); cmds[4] = vm.toString(_tx.target);
cmds[5] = vm.toString(_tx.target); cmds[5] = vm.toString(_tx.value);
cmds[6] = vm.toString(_tx.value); cmds[6] = vm.toString(_tx.gasLimit);
cmds[7] = vm.toString(_tx.gasLimit); cmds[7] = vm.toString(_tx.data);
cmds[8] = vm.toString(_tx.data);
bytes memory result = vm.ffi(cmds); bytes memory result = vm.ffi(cmds);
( (
......
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"os" "os"
...@@ -9,18 +10,20 @@ import ( ...@@ -9,18 +10,20 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/trie"
) )
// ABI types // ABI types
var ( var (
// Plain dynamic bytes type // Plain dynamic bytesAbi type
bytes, _ = abi.NewType("bytes", "bytes", []abi.ArgumentMarshaling{ bytesAbi, _ = abi.NewType("bytes", "bytes", []abi.ArgumentMarshaling{
{Name: "data", Type: "bytes"}, {Name: "data", Type: "bytes"},
}) })
bytesArgs = abi.Arguments{ bytesArgs = abi.Arguments{
{Type: bytes}, {Type: bytesAbi},
} }
// Plain fixed bytes32 type // Plain fixed bytes32 type
...@@ -40,6 +43,15 @@ var ( ...@@ -40,6 +43,15 @@ var (
{Name: "nonce", Type: decodedNonce}, {Name: "nonce", Type: decodedNonce},
} }
// WithdrawalHash slot tuple (bytes32, bytes32)
withdrawalSlot, _ = abi.NewType("tuple", "ArraySlotHash", []abi.ArgumentMarshaling{
{Name: "withdrawalHash", Type: "bytes32"},
{Name: "zeroPadding", Type: "bytes32"},
})
withdrawalSlotArgs = abi.Arguments{
{Name: "slotHash", Type: withdrawalSlot},
}
// Withdrawal transaction tuple (uint256, address, address, uint256, uint256, bytes) // Withdrawal transaction tuple (uint256, address, address, uint256, uint256, bytes)
withdrawalTransaction, _ = abi.NewType("tuple", "WithdrawalTransaction", []abi.ArgumentMarshaling{ withdrawalTransaction, _ = abi.NewType("tuple", "WithdrawalTransaction", []abi.ArgumentMarshaling{
{Name: "nonce", Type: "uint256"}, {Name: "nonce", Type: "uint256"},
...@@ -63,6 +75,18 @@ var ( ...@@ -63,6 +75,18 @@ var (
outputRootProofArgs = abi.Arguments{ outputRootProofArgs = abi.Arguments{
{Name: "proof", Type: outputRootProof}, {Name: "proof", Type: outputRootProof},
} }
// Prove withdrawal inputs tuple (bytes32, bytes32, bytes32, bytes32, bytes[])
proveWithdrawalInputs, _ = abi.NewType("tuple", "ProveWithdrawalInputs", []abi.ArgumentMarshaling{
{Name: "worldRoot", Type: "bytes32"},
{Name: "storageRoot", Type: "bytes32"},
{Name: "outputRoot", Type: "bytes32"},
{Name: "withdrawalHash", Type: "bytes32"},
{Name: "proof", Type: "bytes[]"},
})
proveWithdrawalInputsArgs = abi.Arguments{
{Name: "inputs", Type: proveWithdrawalInputs},
}
) )
func main() { func main() {
...@@ -75,6 +99,7 @@ func main() { ...@@ -75,6 +99,7 @@ func main() {
switch args[0] { switch args[0] {
case "decodeVersionedNonce": case "decodeVersionedNonce":
// Parse input arguments
input, ok := new(big.Int).SetString(args[1], 10) input, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok) checkOk(ok)
...@@ -95,6 +120,7 @@ func main() { ...@@ -95,6 +120,7 @@ func main() {
fmt.Print(hexutil.Encode(packed)) fmt.Print(hexutil.Encode(packed))
break break
case "encodeCrossDomainMessage": case "encodeCrossDomainMessage":
// Parse input arguments
nonce, ok := new(big.Int).SetString(args[1], 10) nonce, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok) checkOk(ok)
sender := common.HexToAddress(args[2]) sender := common.HexToAddress(args[2])
...@@ -210,31 +236,12 @@ func main() { ...@@ -210,31 +236,12 @@ func main() {
checkOk(ok) checkOk(ok)
data := common.FromHex(args[6]) data := common.FromHex(args[6])
// Pack withdrawal // Hash withdrawal
wdtx := struct { hash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data)
Nonce *big.Int checkErr(err, fmt.Sprintf("Error hashing withdrawal: %s", err))
Sender common.Address
Target common.Address
Value *big.Int
GasLimit *big.Int
Data []byte
}{
Nonce: nonce,
Sender: sender,
Target: target,
Value: value,
GasLimit: gasLimit,
Data: data,
}
packed, err := withdrawalTransactionArgs.Pack(&wdtx)
// println(hexutil.Encode(packed))
checkErr(err, fmt.Sprintf("Error packing withdrawal: %s", err))
// Hash packed withdrawal (we ignore the pointer)
hash := crypto.Keccak256Hash(packed[32:])
// Pack hash // Pack hash
packed, err = fixedBytesArgs.Pack(&hash) packed, err := fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err)) checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed)) fmt.Print(hexutil.Encode(packed))
...@@ -246,34 +253,114 @@ func main() { ...@@ -246,34 +253,114 @@ func main() {
messagePasserStorageRoot := common.HexToHash(args[3]) messagePasserStorageRoot := common.HexToHash(args[3])
latestBlockHash := common.HexToHash(args[4]) latestBlockHash := common.HexToHash(args[4])
// Pack proof // Hash the output root proof
proof := struct { hash, err := hashOutputRootProof(version, stateRoot, messagePasserStorageRoot, latestBlockHash)
Version common.Hash
StateRoot common.Hash
MessagePasserStorageRoot common.Hash
LatestBlockHash common.Hash
}{
Version: version,
StateRoot: stateRoot,
MessagePasserStorageRoot: messagePasserStorageRoot,
LatestBlockHash: latestBlockHash,
}
packed, err := outputRootProofArgs.Pack(&proof)
checkErr(err, fmt.Sprintf("Error packing proof: %s", err))
// Hash packed proof
hash := crypto.Keccak256Hash(packed)
// Pack hash // Pack hash
packed, err = fixedBytesArgs.Pack(&hash) packed, err := fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err)) checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed)) fmt.Print(hexutil.Encode(packed))
break break
case "getProveWithdrawalTransactionInputs": case "getProveWithdrawalTransactionInputs":
// TODO // Parse input arguments
nonce, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok)
sender := common.HexToAddress(args[2])
target := common.HexToAddress(args[3])
value, ok := new(big.Int).SetString(args[4], 10)
checkOk(ok)
gasLimit, ok := new(big.Int).SetString(args[5], 10)
checkOk(ok)
data := common.FromHex(args[6])
wdHash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data)
checkErr(err, fmt.Sprintf("Error hashing withdrawal: %s", err))
// Compute the storage slot the withdrawalHash will be stored in
slot := struct {
WithdrawalHash common.Hash
ZeroPadding common.Hash
}{
WithdrawalHash: wdHash,
ZeroPadding: common.Hash{},
}
packed, err := withdrawalSlotArgs.Pack(&slot)
checkErr(err, fmt.Sprintf("Error packing withdrawal slot: %s", err))
// Compute the storage slot the withdrawalHash will be stored in
hash := crypto.Keccak256Hash(packed)
// Create a secure trie for storage
storage, err := trie.NewStateTrie(
trie.TrieID(common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")),
trie.NewDatabase(rawdb.NewMemoryDatabase()),
)
checkErr(err, fmt.Sprintf("Error creating secure trie: %s", err))
// Put a "true" bool in the storage slot
storage.Update(hash.Bytes(), []byte{0x01})
// Create a secure trie for the world state
world, err := trie.NewStateTrie(
trie.TrieID(common.HexToHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")),
trie.NewDatabase(rawdb.NewMemoryDatabase()),
)
checkErr(err, fmt.Sprintf("Error creating secure trie: %s", err))
// Put the storage root into the L2ToL1MessagePasser storage
address := common.HexToAddress("0x4200000000000000000000000000000000000016")
account := types.StateAccount{
Nonce: 0,
Balance: big.NewInt(0),
Root: storage.Hash(),
}
writer := new(bytes.Buffer)
checkErr(account.EncodeRLP(writer), fmt.Sprintf("Error encoding account: %s", err))
world.Update(address.Bytes(), writer.Bytes())
// Get the proof
var proof proofList
checkErr(storage.Prove(address.Bytes(), 0, &proof), fmt.Sprintf("Error getting proof: %s", err))
// Get the output root
outputRoot, err := hashOutputRootProof(common.Hash{}, world.Hash(), storage.Hash(), common.Hash{})
// Pack the output
output := struct {
WorldRoot common.Hash
StorageRoot common.Hash
OutputRoot common.Hash
WithdrawalHash common.Hash
Proof [][]byte
}{
WorldRoot: world.Hash(),
StorageRoot: storage.Hash(),
OutputRoot: outputRoot,
WithdrawalHash: wdHash,
Proof: proof,
}
packed, err = proveWithdrawalInputsArgs.Pack(&output)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
// Print the output
fmt.Print(hexutil.Encode(packed[32:]))
break break
default: default:
panic(fmt.Errorf("Unknown command: %s", args[0])) panic(fmt.Errorf("Unknown command: %s", args[0]))
} }
} }
// Custom type to write the generated proof to
type proofList [][]byte
func (n *proofList) Put(key []byte, value []byte) error {
*n = append(*n, value)
return nil
}
func (n *proofList) Delete(key []byte) error {
panic("not supported")
}
...@@ -9,10 +9,13 @@ import ( ...@@ -9,10 +9,13 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
) )
var UnknownNonceVersion = errors.New("Unknown nonce version") var UnknownNonceVersion = errors.New("Unknown nonce version")
var ZeroPadding = [32]byte{}
// checkOk checks if ok is false, and panics if so. // checkOk checks if ok is false, and panics if so.
// Shorthand to ease go's god awful error handling // Shorthand to ease go's god awful error handling
func checkOk(ok bool) { func checkOk(ok bool) {
...@@ -48,6 +51,56 @@ func encodeCrossDomainMessage(nonce *big.Int, sender common.Address, target comm ...@@ -48,6 +51,56 @@ func encodeCrossDomainMessage(nonce *big.Int, sender common.Address, target comm
return encoded, err return encoded, err
} }
// hashWithdrawal hashes a withdrawal transaction.
func hashWithdrawal(nonce *big.Int, sender common.Address, target common.Address, value *big.Int, gasLimit *big.Int, data []byte) (common.Hash, error) {
// Pack withdrawal
wdtx := struct {
Nonce *big.Int
Sender common.Address
Target common.Address
Value *big.Int
GasLimit *big.Int
Data []byte
}{
Nonce: nonce,
Sender: sender,
Target: target,
Value: value,
GasLimit: gasLimit,
Data: data,
}
packed, err := withdrawalTransactionArgs.Pack(&wdtx)
if err != nil {
return common.Hash{}, err
}
// Hash packed withdrawal (we ignore the pointer)
return crypto.Keccak256Hash(packed[32:]), nil
}
// hashOutputRootProof hashes an output root proof.
func hashOutputRootProof(version common.Hash, stateRoot common.Hash, messagePasserStorageRoot common.Hash, latestBlockHash common.Hash) (common.Hash, error) {
// Pack proof
proof := struct {
Version common.Hash
StateRoot common.Hash
MessagePasserStorageRoot common.Hash
LatestBlockHash common.Hash
}{
Version: version,
StateRoot: stateRoot,
MessagePasserStorageRoot: messagePasserStorageRoot,
LatestBlockHash: latestBlockHash,
}
packed, err := outputRootProofArgs.Pack(&proof)
if err != nil {
return common.Hash{}, err
}
// Hash packed proof
return crypto.Keccak256Hash(packed), nil
}
// makeDepositTx creates a deposit transaction type. // makeDepositTx creates a deposit transaction type.
func makeDepositTx( func makeDepositTx(
from common.Address, from common.Address,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment