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
12 changes: 11 additions & 1 deletion op-node/rollup/derive/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,19 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err))
}

txs := make([]hexutil.Bytes, 0, 1+len(depositTxs)+len(upgradeTxs))
var afterForceIncludeTxs []hexutil.Bytes
if ba.rollupCfg.IsInterop(nextL2Time) {
depositsCompleteTx, err := DepositsCompleteBytes(seqNumber, l1Info)
if err != nil {
return nil, NewCriticalError(fmt.Errorf("failed to create depositsCompleteTx: %w", err))
}
afterForceIncludeTxs = append(afterForceIncludeTxs, depositsCompleteTx)
}

txs := make([]hexutil.Bytes, 0, 1+len(depositTxs)+len(afterForceIncludeTxs)+len(upgradeTxs))
txs = append(txs, l1InfoTx)
txs = append(txs, depositTxs...)
txs = append(txs, afterForceIncludeTxs...)
txs = append(txs, upgradeTxs...)

var withdrawals *types.Withdrawals
Expand Down
92 changes: 92 additions & 0 deletions op-node/rollup/derive/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,98 @@ func TestPreparePayloadAttributes(t *testing.T) {
require.Equal(t, l1InfoTx, []byte(attrs.Transactions[0]))
require.True(t, attrs.NoTxPool)
})
t.Run("new origin with deposits on post-Isthmus", func(t *testing.T) {
rng := rand.New(rand.NewSource(1234))
l1Fetcher := &testutils.MockL1Source{}
defer l1Fetcher.AssertExpectations(t)
l2Parent := testutils.RandomL2BlockRef(rng)
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)

l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoParentHash = l2Parent.L1Origin.Hash
l1Info.InfoNum = l2Parent.L1Origin.Number + 1 // next origin, where deposits may be

receipts, depositTxs, err := makeReceipts(rng, l1Info.InfoHash, cfg.DepositContractAddress, []receiptData{
{goodReceipt: true, DepositLogs: []bool{true, false}},
{goodReceipt: true, DepositLogs: []bool{true}},
{goodReceipt: false, DepositLogs: []bool{true}},
{goodReceipt: false, DepositLogs: []bool{false}},
})
require.NoError(t, err)
userDepositTxs, err := encodeDeposits(depositTxs)
require.NoError(t, err)

// sets config to post-interop
cfg.ActivateAtGenesis(rollup.Interop)

seqNumber := uint64(0)
epoch := l1Info.ID()
l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, seqNumber, l1Info, 0)
require.NoError(t, err)
depositsComplete, err := DepositsCompleteBytes(seqNumber, l1Info)
require.NoError(t, err)

var l2Txs []eth.Data
l2Txs = append(l2Txs, l1InfoTx)
l2Txs = append(l2Txs, userDepositTxs...)
l2Txs = append(l2Txs, depositsComplete)

l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, receipts, nil)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NoError(t, err)
require.NotNil(t, attrs)
require.Equal(t, l2Parent.Time+cfg.BlockTime, uint64(attrs.Timestamp))
require.Equal(t, eth.Bytes32(l1Info.InfoMixDigest), attrs.PrevRandao)
require.Equal(t, predeploys.SequencerFeeVaultAddr, attrs.SuggestedFeeRecipient)
require.Equal(t, len(l2Txs), len(attrs.Transactions), "Expected txs to equal l1 info tx + user deposit txs + DepositsComplete")
require.Equal(t, eth.Data(depositsComplete).String(), attrs.Transactions[len(l2Txs)-1].String())
require.Equal(t, l2Txs, attrs.Transactions)
require.True(t, attrs.NoTxPool)
})

t.Run("same origin without deposits on post-Isthmus", func(t *testing.T) {
rng := rand.New(rand.NewSource(1234))
l1Fetcher := &testutils.MockL1Source{}
defer l1Fetcher.AssertExpectations(t)
l2Parent := testutils.RandomL2BlockRef(rng)
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoHash = l2Parent.L1Origin.Hash
l1Info.InfoNum = l2Parent.L1Origin.Number // same origin again, so the sequence number is not reset

// sets config to post-interop
cfg.ActivateAtGenesis(rollup.Interop)

seqNumber := l2Parent.SequenceNumber + 1
epoch := l1Info.ID()
l1InfoTx, err := L1InfoDepositBytes(cfg, testSysCfg, seqNumber, l1Info, 0)
require.NoError(t, err)
depositsComplete, err := DepositsCompleteBytes(seqNumber, l1Info)
require.NoError(t, err)

var l2Txs []eth.Data
l2Txs = append(l2Txs, l1InfoTx)
l2Txs = append(l2Txs, depositsComplete)

l1Fetcher.ExpectInfoByHash(epoch.Hash, l1Info, nil)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NoError(t, err)
require.NotNil(t, attrs)
require.Equal(t, l2Parent.Time+cfg.BlockTime, uint64(attrs.Timestamp))
require.Equal(t, eth.Bytes32(l1Info.InfoMixDigest), attrs.PrevRandao)
require.Equal(t, predeploys.SequencerFeeVaultAddr, attrs.SuggestedFeeRecipient)
require.Equal(t, len(l2Txs), len(attrs.Transactions), "Expected txs to equal l1 info tx + user deposit txs + DepositsComplete")
require.Equal(t, eth.Data(depositsComplete).String(), attrs.Transactions[len(l2Txs)-1].String())
require.Equal(t, l2Txs, attrs.Transactions)
require.True(t, attrs.NoTxPool)
})

// Test that the payload attributes builder changes the deposit format based on L2-time-based regolith activation
t.Run("regolith", func(t *testing.T) {
testCases := []struct {
Expand Down
25 changes: 22 additions & 3 deletions op-node/rollup/derive/deposit_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ type UserDepositSource struct {
}

const (
UserDepositSourceDomain = 0
L1InfoDepositSourceDomain = 1
UpgradeDepositSourceDomain = 2
UserDepositSourceDomain = 0
L1InfoDepositSourceDomain = 1
UpgradeDepositSourceDomain = 2
AfterForceIncludeSourceDomain = 3
Comment thread
tynes marked this conversation as resolved.
)

func (dep *UserDepositSource) SourceHash() common.Hash {
Expand Down Expand Up @@ -63,3 +64,21 @@ func (dep *UpgradeDepositSource) SourceHash() common.Hash {
copy(domainInput[32:], intentHash[:])
return crypto.Keccak256Hash(domainInput[:])
}

// AfterForceIncludeSource identifies the DepositsComplete post-user-deposits deposit-transaction.
type AfterForceIncludeSource struct {
L1BlockHash common.Hash
SeqNumber uint64 // without this the Deposit tx would have the same tx hash for every time the L1 info repeats.
}

func (dep *AfterForceIncludeSource) SourceHash() common.Hash {
var input [32 * 2]byte
copy(input[:32], dep.L1BlockHash[:])
binary.BigEndian.PutUint64(input[32*2-8:], dep.SeqNumber)
depositIDHash := crypto.Keccak256Hash(input[:])

var domainInput [32 * 2]byte
binary.BigEndian.PutUint64(domainInput[32-8:32], AfterForceIncludeSourceDomain)
copy(domainInput[32:], depositIDHash[:])
return crypto.Keccak256Hash(domainInput[:])
}
31 changes: 31 additions & 0 deletions op-node/rollup/derive/deposit_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package derive
import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -34,3 +35,33 @@ func TestEcotone4788ContractSourceHash(t *testing.T) {

assert.Equal(t, expected, actual.Hex())
}

// TestL1InfoDepositSource
// cast keccak $(cast concat-hex 0x0000000000000000000000000000000000000000000000000000000000000001 $(cast keccak $(cast concat-hex 0xc00e5d67c2755389aded7d8b151cbd5bcdf7ed275ad5e028b664880fc7581c77 0x0000000000000000000000000000000000000000000000000000000000000004)))
// # 0x0586c503340591999b8b38bc9834bb16aec7d5bc00eb5587ab139c9ddab81977
func TestL1InfoDepositSource(t *testing.T) {
source := L1InfoDepositSource{
L1BlockHash: common.HexToHash("0xc00e5d67c2755389aded7d8b151cbd5bcdf7ed275ad5e028b664880fc7581c77"),
SeqNumber: 4,
}

actual := source.SourceHash()
expected := "0x0586c503340591999b8b38bc9834bb16aec7d5bc00eb5587ab139c9ddab81977"

assert.Equal(t, expected, actual.Hex())
}

// TestAfterForceIncludeSourceHash
// cast keccak $(cast concat-hex 0x0000000000000000000000000000000000000000000000000000000000000003 $(cast keccak $(cast concat-hex 0xc00e5d67c2755389aded7d8b151cbd5bcdf7ed275ad5e028b664880fc7581c77 0x0000000000000000000000000000000000000000000000000000000000000004)))
// # 0x0d165c391384b29c29f655e3f32315755b8c1e4c1147d1824d1243420dda5ec3
func TestAfterForceIncludeSource(t *testing.T) {
source := AfterForceIncludeSource{
L1BlockHash: common.HexToHash("0xc00e5d67c2755389aded7d8b151cbd5bcdf7ed275ad5e028b664880fc7581c77"),
SeqNumber: 4,
}

actual := source.SourceHash()
expected := "0x0d165c391384b29c29f655e3f32315755b8c1e4c1147d1824d1243420dda5ec3"

assert.Equal(t, expected, actual.Hex())
}
17 changes: 14 additions & 3 deletions op-node/rollup/derive/fuzz_parsers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,26 @@ func FuzzL1InfoEcotoneRoundTrip(f *testing.F) {
}
enc, err := in.marshalBinaryEcotone()
if err != nil {
t.Fatalf("Failed to marshal binary: %v", err)
t.Fatalf("Failed to marshal Ecotone binary: %v", err)
}
var out L1BlockInfo
err = out.unmarshalBinaryEcotone(enc)
if err != nil {
t.Fatalf("Failed to unmarshal binary: %v", err)
t.Fatalf("Failed to unmarshal Ecotone binary: %v", err)
}
if !cmp.Equal(in, out, cmp.Comparer(testutils.BigEqual)) {
t.Fatalf("The data did not round trip correctly. in: %v. out: %v", in, out)
t.Fatalf("The Ecotone data did not round trip correctly. in: %v. out: %v", in, out)
}
enc, err = in.marshalBinaryIsthmus()
if err != nil {
t.Fatalf("Failed to marshal Isthmus binary: %v", err)
}
err = out.unmarshalBinaryIsthmus(enc)
if err != nil {
t.Fatalf("Failed to unmarshal Isthmus binary: %v", err)
}
if !cmp.Equal(in, out, cmp.Comparer(testutils.BigEqual)) {
t.Fatalf("The Isthmus data did not round trip correctly. in: %v. out: %v", in, out)
}

})
Expand Down
Loading