Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions op-acceptance-tests/tests/interop/happy/interop_happy_tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package happy

import (
"math/rand"
"testing"
"time"

"github.com/davecgh/go-spew/spew"
"github.com/ethereum-optimism/optimism/devnet-sdk/contracts/bindings"
"github.com/ethereum-optimism/optimism/devnet-sdk/contracts/constants"
"github.com/ethereum-optimism/optimism/op-acceptance-tests/tests/interop"
"github.com/ethereum-optimism/optimism/op-devstack/devtest"
"github.com/ethereum-optimism/optimism/op-devstack/dsl"
"github.com/ethereum-optimism/optimism/op-devstack/presets"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/txintent"
"github.com/ethereum-optimism/optimism/op-service/txplan"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)

// TestMain creates the test-setups against the shared backend
func TestMain(m *testing.M) {
// Other setups may be added here, hydrated from the same orchestrator
presets.DoMain(m, presets.WithSimpleInterop())
}

// TestInteropHappyTx is testing that a valid init message, followed by a valid exec message are correctly
// included in two L2 chains and that the cross-safe ref for both of them progresses as expected beyond
// the block number where the messages were included
func TestInteropHappyTx(gt *testing.T) {
t := devtest.SerialT(gt)
ctx := t.Ctx()

sys := presets.NewSimpleInterop(t)
l := sys.Log

// two EOAs for triggering the init and exec interop txs
var alice, bob *dsl.EOA
{
// alice is on chain A
pk, err := crypto.GenerateKey()
require.NoError(t, err)
alice = dsl.NewEOA(dsl.NewKey(t, pk), sys.L2ELA)
sys.FaucetA.Fund(alice.Address(), eth.OneEther)

// bob is on chain B
pk, err = crypto.GenerateKey()
require.NoError(t, err)
bob = dsl.NewEOA(dsl.NewKey(t, pk), sys.L2ELB)
sys.FaucetB.Fund(bob.Address(), eth.OneEther)

l.Info("alice", "address", alice.Address())
l.Info("bob", "address", bob.Address())
}

sys.L1Network.WaitForBlock()
sys.L2ChainA.WaitForBlock()

// deploy event logger on chain A
var eventLoggerAddress common.Address
{
tx := txplan.NewPlannedTx(txplan.Combine(
alice.Plan(),
txplan.WithData(common.FromHex(bindings.EventloggerBin)),
))
res, err := tx.Included.Eval(ctx)
require.NoError(t, err)

eventLoggerAddress = res.ContractAddress
l.Info("deployed EventLogger", "chainID", tx.ChainID.Value(), "address", eventLoggerAddress)
}

sys.L1Network.WaitForBlock()

var initTrigger *txintent.InitTrigger
// prepare init trigger (i.e. what logs to emit on chain A)
{
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
nTopics := 3
lenData := 10
initTrigger = interop.RandomInitTrigger(rng, eventLoggerAddress, nTopics, lenData)

l.Info("created init trigger", "address", eventLoggerAddress, "topics", nTopics, "lenData", lenData)
}

// wait for chain B to catch up to chain A if necessary
sys.L2ChainB.CatchUpTo(sys.L2ChainA)

var initTx *txintent.IntentTx[*txintent.InitTrigger, *txintent.InteropOutput]
var initReceipt *types.Receipt
// prepare and include initiating message on chain A
{
initTx = txintent.NewIntent[*txintent.InitTrigger, *txintent.InteropOutput](alice.Plan())
initTx.Content.Set(initTrigger)
var err error
initReceipt, err = initTx.PlannedTx.Included.Eval(ctx)
require.NoError(t, err)

l.Info("initiating message included", "chain", sys.L2ChainA.ChainID(), "block_number", initReceipt.BlockNumber, "block_hash", initReceipt.BlockHash, "now", time.Now().Unix())
}

// at least one block between the init tx on chain A and the exec tx on chain B
sys.L2ChainB.WaitForBlock()

var execTx *txintent.IntentTx[*txintent.ExecTrigger, *txintent.InteropOutput]
var execReceipt *types.Receipt
// prepare and include executing message on chain B
{
execTx = txintent.NewIntent[*txintent.ExecTrigger, *txintent.InteropOutput](bob.Plan())
execTx.Content.DependOn(&initTx.Result)
// single event in tx so index is 0. ExecuteIndexed returns a lambda to transform InteropOutput to a new ExecTrigger
execTx.Content.Fn(txintent.ExecuteIndexed(constants.CrossL2Inbox, &initTx.Result, 0))
var err error
execReceipt, err = execTx.PlannedTx.Included.Eval(ctx)
require.NoError(t, err)
require.Equal(t, 1, len(execReceipt.Logs))

l.Info("executing message included", "chain", sys.L2ChainB.ChainID(), "block_number", execReceipt.BlockNumber, "block_hash", execReceipt.BlockHash, "now", time.Now().Unix())
}

crossSafeMinimumBlock_A := initReceipt.BlockNumber.Uint64() + 1
crossSafeMinimumBlock_B := execReceipt.BlockNumber.Uint64() + 1

// confirm that the cross-safe minimum block has been reached
err := wait.For(ctx, 5*time.Second, func() (bool, error) {
safeL2Head_supervisor_A := sys.Supervisor.SafeBlockID(sys.L2ChainA.ChainID()).Hash
safeL2Head_supervisor_B := sys.Supervisor.SafeBlockID(sys.L2ChainB.ChainID()).Hash
safeL2Head_sequencer_A := sys.L2CLA.SafeL2BlockRef()
safeL2Head_sequencer_B := sys.L2CLB.SafeL2BlockRef()

if safeL2Head_sequencer_A.Number < crossSafeMinimumBlock_A {
l.Info("Safe ref number is still behind", "block_a", crossSafeMinimumBlock_A, "safe", safeL2Head_sequencer_A.Number)
return false, nil
}

if safeL2Head_sequencer_B.Number < crossSafeMinimumBlock_B {
l.Info("Safe ref number is still behind", "block_b", crossSafeMinimumBlock_B, "safe", safeL2Head_sequencer_B.Number)
return false, nil
}

l.Info("Safe ref the same across supervisor and sequencers",
"supervisor_A", safeL2Head_supervisor_A,
"supervisor_B", safeL2Head_supervisor_B)

return true, nil
})
require.NoError(t, err, "Expected to get same safe ref on both supervisor and sequencer eventually")

// confirm that the init msg block and exec msg block are not reorged
{
latestBlock_A, err := sys.L2ELA.Escape().EthClient().BlockRefByNumber(ctx, initReceipt.BlockNumber.Uint64())
require.NoError(t, err)
require.Equal(t, latestBlock_A.Hash, initReceipt.BlockHash)

latestBlock_B, err := sys.L2ELB.Escape().EthClient().BlockRefByNumber(ctx, execReceipt.BlockNumber.Uint64())
require.NoError(t, err)
require.Equal(t, latestBlock_B.Hash, execReceipt.BlockHash)
}

sys.L2ChainA.PrintChain()
sys.L2ChainB.PrintChain()
spew.Dump(sys.Supervisor.FetchSyncStatus())
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@ func TestReorgInitExecMsg(gt *testing.T) {
pk, err := crypto.GenerateKey()
require.NoError(t, err)
alice = dsl.NewEOA(dsl.NewKey(t, pk), sys.L2ELA)
sys.FaucetA.Fund(alice.Address(), eth.ThousandEther)
sys.FaucetA.Fund(alice.Address(), eth.OneEther)

// bob is on chain B
pk, err = crypto.GenerateKey()
require.NoError(t, err)
bob = dsl.NewEOA(dsl.NewKey(t, pk), sys.L2ELB)
sys.FaucetB.Fund(bob.Address(), eth.ThousandEther)
sys.FaucetB.Fund(bob.Address(), eth.OneEther)

// cathrine is on chain A
pk, err = crypto.GenerateKey()
require.NoError(t, err)
cathrine = dsl.NewEOA(dsl.NewKey(t, pk), sys.L2ELA)
sys.FaucetA.Fund(cathrine.Address(), eth.ThousandEther)
sys.FaucetA.Fund(cathrine.Address(), eth.OneEther)

l.Info("alice", "address", alice.Address())
l.Info("bob", "address", bob.Address())
Expand Down Expand Up @@ -200,7 +200,7 @@ func TestReorgInitExecMsg(gt *testing.T) {

// include simple transfer tx in opened block
{
to := cathrine.PlanTransfer(alice.Address(), eth.OneEther)
to := cathrine.PlanTransfer(alice.Address(), eth.OneGWei)
opt := txplan.Combine(to)
ptx := txplan.NewPlannedTx(opt)
signed_tx, err := ptx.Signed.Eval(ctx)
Expand Down
Loading