Commit f5765f4c authored by clabby's avatar clabby

Go impl of `getProveWithdrawalTransactionInputs` diff method

parent 5b9728d3
......@@ -477,16 +477,15 @@ contract FFIInterface is Test {
bytes[] memory
)
{
string[] memory cmds = new string[](9);
cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "getProveWithdrawalTransactionInputs";
cmds[3] = vm.toString(_tx.nonce);
cmds[4] = vm.toString(_tx.sender);
cmds[5] = vm.toString(_tx.target);
cmds[6] = vm.toString(_tx.value);
cmds[7] = vm.toString(_tx.gasLimit);
cmds[8] = vm.toString(_tx.data);
string[] memory cmds = new string[](8);
cmds[0] = "scripts/differential-testing/differential-testing";
cmds[1] = "getProveWithdrawalTransactionInputs";
cmds[2] = vm.toString(_tx.nonce);
cmds[3] = vm.toString(_tx.sender);
cmds[4] = vm.toString(_tx.target);
cmds[5] = vm.toString(_tx.value);
cmds[6] = vm.toString(_tx.gasLimit);
cmds[7] = vm.toString(_tx.data);
bytes memory result = vm.ffi(cmds);
(
......
package main
import (
"bytes"
"fmt"
"math/big"
"os"
......@@ -9,18 +10,20 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"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/crypto"
"github.com/ethereum/go-ethereum/trie"
)
// ABI types
var (
// Plain dynamic bytes type
bytes, _ = abi.NewType("bytes", "bytes", []abi.ArgumentMarshaling{
// Plain dynamic bytesAbi type
bytesAbi, _ = abi.NewType("bytes", "bytes", []abi.ArgumentMarshaling{
{Name: "data", Type: "bytes"},
})
bytesArgs = abi.Arguments{
{Type: bytes},
{Type: bytesAbi},
}
// Plain fixed bytes32 type
......@@ -40,6 +43,15 @@ var (
{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)
withdrawalTransaction, _ = abi.NewType("tuple", "WithdrawalTransaction", []abi.ArgumentMarshaling{
{Name: "nonce", Type: "uint256"},
......@@ -63,6 +75,18 @@ var (
outputRootProofArgs = abi.Arguments{
{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() {
......@@ -75,6 +99,7 @@ func main() {
switch args[0] {
case "decodeVersionedNonce":
// Parse input arguments
input, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok)
......@@ -95,6 +120,7 @@ func main() {
fmt.Print(hexutil.Encode(packed))
break
case "encodeCrossDomainMessage":
// Parse input arguments
nonce, ok := new(big.Int).SetString(args[1], 10)
checkOk(ok)
sender := common.HexToAddress(args[2])
......@@ -210,31 +236,12 @@ func main() {
checkOk(ok)
data := common.FromHex(args[6])
// 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)
// 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:])
// Hash withdrawal
hash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data)
checkErr(err, fmt.Sprintf("Error hashing withdrawal: %s", err))
// Pack hash
packed, err = fixedBytesArgs.Pack(&hash)
packed, err := fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
......@@ -246,34 +253,114 @@ func main() {
messagePasserStorageRoot := common.HexToHash(args[3])
latestBlockHash := common.HexToHash(args[4])
// 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)
checkErr(err, fmt.Sprintf("Error packing proof: %s", err))
// Hash packed proof
hash := crypto.Keccak256Hash(packed)
// Hash the output root proof
hash, err := hashOutputRootProof(version, stateRoot, messagePasserStorageRoot, latestBlockHash)
// Pack hash
packed, err = fixedBytesArgs.Pack(&hash)
packed, err := fixedBytesArgs.Pack(&hash)
checkErr(err, fmt.Sprintf("Error encoding output: %s", err))
fmt.Print(hexutil.Encode(packed))
break
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
default:
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 (
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
var UnknownNonceVersion = errors.New("Unknown nonce version")
var ZeroPadding = [32]byte{}
// checkOk checks if ok is false, and panics if so.
// Shorthand to ease go's god awful error handling
func checkOk(ok bool) {
......@@ -48,6 +51,56 @@ func encodeCrossDomainMessage(nonce *big.Int, sender common.Address, target comm
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.
func makeDepositTx(
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