Skip to content
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

Add a boolean field to tx receipt object indicating if the tx was timeboosted #2760

Open
wants to merge 2 commits into
base: bulk-blockmetadata-api
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions arbnode/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ func (n *Node) GetFinalizedMsgCount(ctx context.Context) (arbutil.MessageIndex,
return n.InboxReader.GetFinalizedMsgCount(ctx)
}

func (n *Node) WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata, msgResult execution.MessageResult, blockMetadata arbostypes.BlockMetadata) error {
func (n *Node) WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata, msgResult execution.MessageResult, blockMetadata common.BlockMetadata) error {
return n.TxStreamer.WriteMessageFromSequencer(pos, msgWithMeta, msgResult, blockMetadata)
}

Expand All @@ -1035,6 +1035,6 @@ func (n *Node) ValidatedMessageCount() (arbutil.MessageIndex, error) {
return n.BlockValidator.GetValidated(), nil
}

func (n *Node) BlockMetadataAtCount(count arbutil.MessageIndex) (arbostypes.BlockMetadata, error) {
func (n *Node) BlockMetadataAtCount(count arbutil.MessageIndex) (common.BlockMetadata, error) {
return n.TxStreamer.BlockMetadataAtCount(count)
}
4 changes: 2 additions & 2 deletions arbnode/transaction_streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ func (s *TransactionStreamer) WriteMessageFromSequencer(
pos arbutil.MessageIndex,
msgWithMeta arbostypes.MessageWithMetadata,
msgResult execution.MessageResult,
blockMetadata arbostypes.BlockMetadata,
blockMetadata common.BlockMetadata,
) error {
if err := s.ExpectChosenSequencer(); err != nil {
return err
Expand Down Expand Up @@ -1091,7 +1091,7 @@ func (s *TransactionStreamer) writeMessages(pos arbutil.MessageIndex, messages [
return nil
}

func (s *TransactionStreamer) BlockMetadataAtCount(count arbutil.MessageIndex) (arbostypes.BlockMetadata, error) {
func (s *TransactionStreamer) BlockMetadataAtCount(count arbutil.MessageIndex) (common.BlockMetadata, error) {
if count == 0 {
return []byte{}, nil
}
Expand Down
13 changes: 1 addition & 12 deletions arbos/arbostypes/messagewithmeta.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ type MessageWithMetadata struct {
DelayedMessagesRead uint64 `json:"delayedMessagesRead"`
}

type BlockMetadata []byte

type MessageWithMetadataAndBlockInfo struct {
MessageWithMeta MessageWithMetadata
BlockHash *common.Hash
BlockMetadata BlockMetadata
BlockMetadata common.BlockMetadata
}

var EmptyTestMessageWithMetadata = MessageWithMetadata{
Expand All @@ -35,15 +33,6 @@ var TestMessageWithMetadataAndRequestId = MessageWithMetadata{
Message: &TestIncomingMessageWithRequestId,
}

// IsTxTimeboosted given a tx's index in the block returns whether the tx was timeboosted or not
func (b BlockMetadata) IsTxTimeboosted(txIndex int) bool {
maxTxCount := (len(b) - 1) * 8
if txIndex >= maxTxCount {
return false
}
return b[1+(txIndex/8)]&(1<<(txIndex%8)) != 0
}

func (m *MessageWithMetadata) Hash(sequenceNumber arbutil.MessageIndex, chainId uint64) (common.Hash, error) {
serializedExtraData := make([]byte, 24)
binary.BigEndian.PutUint64(serializedExtraData[:8], uint64(sequenceNumber))
Expand Down
4 changes: 2 additions & 2 deletions broadcaster/broadcaster.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (b *Broadcaster) NewBroadcastFeedMessage(
message arbostypes.MessageWithMetadata,
sequenceNumber arbutil.MessageIndex,
blockHash *common.Hash,
blockMetadata arbostypes.BlockMetadata,
blockMetadata common.BlockMetadata,
) (*m.BroadcastFeedMessage, error) {
var messageSignature []byte
if b.dataSigner != nil {
Expand All @@ -70,7 +70,7 @@ func (b *Broadcaster) BroadcastSingle(
msg arbostypes.MessageWithMetadata,
seq arbutil.MessageIndex,
blockHash *common.Hash,
blockMetadata arbostypes.BlockMetadata,
blockMetadata common.BlockMetadata,
) (err error) {
defer func() {
if r := recover(); r != nil {
Expand Down
2 changes: 1 addition & 1 deletion broadcaster/message/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type BroadcastFeedMessage struct {
Message arbostypes.MessageWithMetadata `json:"message"`
BlockHash *common.Hash `json:"blockHash,omitempty"`
Signature []byte `json:"signature"`
BlockMetadata arbostypes.BlockMetadata `json:"blockMetadata"`
BlockMetadata common.BlockMetadata `json:"blockMetadata"`

CumulativeSumMsgSize uint64 `json:"-"`
}
Expand Down
4 changes: 2 additions & 2 deletions broadcaster/message/message_blockmetadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package message
import (
"testing"

"github.com/offchainlabs/nitro/arbos/arbostypes"
"github.com/ethereum/go-ethereum/common"
)

func TestTimeboostedInDifferentScenarios(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name string
blockMetadata arbostypes.BlockMetadata
blockMetadata common.BlockMetadata
txs []bool // Array representing whether the tx is timeboosted or not. First tx is always false as its an arbitrum internal tx
}{
{
Expand Down
12 changes: 6 additions & 6 deletions execution/gethexec/blockmetadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"fmt"
"sync"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/rpc"
"github.com/offchainlabs/nitro/arbos/arbostypes"
"github.com/offchainlabs/nitro/arbutil"
"github.com/offchainlabs/nitro/util/containers"
"github.com/offchainlabs/nitro/util/stopwaiter"
Expand All @@ -18,7 +18,7 @@ import (
var ErrBlockMetadataApiBlocksLimitExceeded = errors.New("number of blocks requested for blockMetadata exceeded")

type BlockMetadataFetcher interface {
BlockMetadataAtCount(count arbutil.MessageIndex) (arbostypes.BlockMetadata, error)
BlockMetadataAtCount(count arbutil.MessageIndex) (common.BlockMetadata, error)
BlockNumberToMessageIndex(blockNum uint64) (arbutil.MessageIndex, error)
MessageIndexToBlockNumber(messageNum arbutil.MessageIndex) uint64
SetReorgEventsReader(reorgEventsReader chan struct{})
Expand All @@ -31,14 +31,14 @@ type BulkBlockMetadataFetcher struct {
reorgDetector chan struct{}
blocksLimit int
cacheMutex sync.RWMutex
cache *containers.LruCache[arbutil.MessageIndex, arbostypes.BlockMetadata]
cache *containers.LruCache[arbutil.MessageIndex, common.BlockMetadata]
}

func NewBulkBlockMetadataFetcher(bc *core.BlockChain, fetcher BlockMetadataFetcher, cacheSize, blocksLimit int) *BulkBlockMetadataFetcher {
var cache *containers.LruCache[arbutil.MessageIndex, arbostypes.BlockMetadata]
var cache *containers.LruCache[arbutil.MessageIndex, common.BlockMetadata]
var reorgDetector chan struct{}
if cacheSize != 0 {
cache = containers.NewLruCache[arbutil.MessageIndex, arbostypes.BlockMetadata](cacheSize)
cache = containers.NewLruCache[arbutil.MessageIndex, common.BlockMetadata](cacheSize)
reorgDetector = make(chan struct{})
fetcher.SetReorgEventsReader(reorgDetector)
}
Expand Down Expand Up @@ -70,7 +70,7 @@ func (b *BulkBlockMetadataFetcher) Fetch(fromBlock, toBlock rpc.BlockNumber) ([]
}
var result []NumberAndBlockMetadata
for i := start; i <= end; i++ {
var data arbostypes.BlockMetadata
var data common.BlockMetadata
var found bool
if b.cache != nil {
b.cacheMutex.RLock()
Expand Down
6 changes: 3 additions & 3 deletions execution/gethexec/executionengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func (s *ExecutionEngine) SetConsensus(consensus execution.FullConsensusClient)
s.consensus = consensus
}

func (s *ExecutionEngine) BlockMetadataAtCount(count arbutil.MessageIndex) (arbostypes.BlockMetadata, error) {
func (s *ExecutionEngine) BlockMetadataAtCount(count arbutil.MessageIndex) (common.BlockMetadata, error) {
if s.consensus != nil {
return s.consensus.BlockMetadataAtCount(count)
}
Expand Down Expand Up @@ -568,8 +568,8 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes.
// starting from the second byte, (N)th bit would represent if (N)th tx is timeboosted or not, 1 means yes and 0 means no
// blockMetadata[index / 8 + 1] & (1 << (index % 8)) != 0; where index = (N - 1), implies whether (N)th tx in a block is timeboosted
// note that number of txs in a block will always lag behind (len(blockMetadata) - 1) * 8 but it wont lag more than a value of 7
func (s *ExecutionEngine) blockMetadataFromBlock(block *types.Block, timeboostedTxs map[common.Hash]struct{}) arbostypes.BlockMetadata {
bits := make(arbostypes.BlockMetadata, 1+arbmath.DivCeil(uint64(len(block.Transactions())), 8))
func (s *ExecutionEngine) blockMetadataFromBlock(block *types.Block, timeboostedTxs map[common.Hash]struct{}) common.BlockMetadata {
bits := make(common.BlockMetadata, 1+arbmath.DivCeil(uint64(len(block.Transactions())), 8))
if len(timeboostedTxs) == 0 {
return bits
}
Expand Down
14 changes: 14 additions & 0 deletions execution/gethexec/sync_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package gethexec
import (
"context"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/offchainlabs/nitro/execution"
"github.com/pkg/errors"
flag "github.com/spf13/pflag"
Expand Down Expand Up @@ -121,3 +123,15 @@ func (s *SyncMonitor) Synced() bool {
func (s *SyncMonitor) SetConsensusInfo(consensus execution.ConsensusInfo) {
s.consensus = consensus
}

func (s *SyncMonitor) BlockMetadataByNumber(blockNum uint64) (common.BlockMetadata, error) {
count, err := s.exec.BlockNumberToMessageIndex(blockNum)
if err != nil {
return nil, err
}
if s.consensus != nil {
return s.consensus.BlockMetadataAtCount(count + 1)
}
log.Debug("FullConsensusClient is not accessible to execution, BlockMetadataByNumber will return nil")
return nil, nil
}
4 changes: 2 additions & 2 deletions execution/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ type ConsensusInfo interface {
Synced() bool
FullSyncProgressMap() map[string]interface{}
SyncTargetMessageCount() arbutil.MessageIndex
BlockMetadataAtCount(count arbutil.MessageIndex) (arbostypes.BlockMetadata, error)
BlockMetadataAtCount(count arbutil.MessageIndex) (common.BlockMetadata, error)

// TODO: switch from pulling to pushing safe/finalized
GetSafeMsgCount(ctx context.Context) (arbutil.MessageIndex, error)
Expand All @@ -92,7 +92,7 @@ type ConsensusInfo interface {
}

type ConsensusSequencer interface {
WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata, msgResult MessageResult, blockMetadata arbostypes.BlockMetadata) error
WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata, msgResult MessageResult, blockMetadata common.BlockMetadata) error
ExpectChosenSequencer() error
}

Expand Down
96 changes: 87 additions & 9 deletions system_tests/timeboost_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"crypto/ecdsa"
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"net"
Expand Down Expand Up @@ -39,13 +40,24 @@ import (
"github.com/offchainlabs/nitro/timeboost"
"github.com/offchainlabs/nitro/timeboost/bindings"
"github.com/offchainlabs/nitro/util/arbmath"
"github.com/offchainlabs/nitro/util/colors"
"github.com/offchainlabs/nitro/util/containers"
"github.com/offchainlabs/nitro/util/redisutil"
"github.com/offchainlabs/nitro/util/stopwaiter"
"github.com/stretchr/testify/require"
)

func TestTimeboostBulkBlockMetadataAPI(t *testing.T) {
var blockMetadataInputFeedKey = func(pos uint64) []byte {
var key []byte
prefix := []byte("t")
key = append(key, prefix...)
data := make([]byte, 8)
binary.BigEndian.PutUint64(data, pos)
key = append(key, data...)
return key
}

func TestTimeboostedFieldInReceiptsObject(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

Expand All @@ -54,16 +66,82 @@ func TestTimeboostBulkBlockMetadataAPI(t *testing.T) {
cleanup := builder.Build(t)
defer cleanup()

// Generate blocks until current block is totalBlocks
arbDb := builder.L2.ConsensusNode.ArbDB
blockMetadataInputFeedKey := func(pos uint64) []byte {
var key []byte
prefix := []byte("t")
key = append(key, prefix...)
data := make([]byte, 8)
binary.BigEndian.PutUint64(data, pos)
key = append(key, data...)
return key
blockNum := big.NewInt(2)
builder.L2Info.GenerateAccount("User")
user := builder.L2Info.GetDefaultTransactOpts("User", ctx)
for i := 0; ; i++ {
builder.L2.TransferBalanceTo(t, "Owner", util.RemapL1Address(user.From), big.NewInt(1e18), builder.L2Info)
latestL2, err := builder.L2.Client.BlockNumber(ctx)
Require(t, err)
// Clean BlockMetadata from arbDB so that we can modify it at will
Require(t, arbDb.Delete(blockMetadataInputFeedKey(latestL2)))
if latestL2 >= blockNum.Uint64() {
break
}
}

block, err := builder.L2.Client.BlockByNumber(ctx, blockNum)
Require(t, err)
if len(block.Transactions()) != 2 {
t.Fatalf("expecting two txs in the second block, but found: %d txs", len(block.Transactions()))
}

// Set first tx (internal tx anyway) to not timeboosted and Second one to timeboosted- BlockMetadata (in bits)-> 00000000 00000010
arbDb.Put(blockMetadataInputFeedKey(blockNum.Uint64()), []byte{0, 2})
l2rpc := builder.L2.Stack.Attach()
// Extra timeboosted field in pointer form to check for its existence
type timeboostedFromReceipt struct {
Timeboosted *bool `json:"timeboosted"`
}
var receiptResult []timeboostedFromReceipt
err = l2rpc.CallContext(ctx, &receiptResult, "eth_getBlockReceipts", rpc.BlockNumber(blockNum.Int64()))
Require(t, err)
if receiptResult[0].Timeboosted != nil {
t.Fatal("timeboosted field shouldn't exist in the receipt object of first tx")
}
if receiptResult[1].Timeboosted == nil {
t.Fatal("timeboosted field should exist in the receipt object of second tx")
}
if *receiptResult[1].Timeboosted != true {
t.Fatal("second tx was timeboosted, but the field indicates otherwise")
}

// Check that timeboosted is accurate for eth_getTransactionReceipt as well
var txReceipt timeboostedFromReceipt
err = l2rpc.CallContext(ctx, &txReceipt, "eth_getTransactionReceipt", block.Transactions()[0].Hash())
Require(t, err)
if txReceipt.Timeboosted != nil {
t.Fatal("timeboosted field shouldn't exist in the receipt object of first tx")
}
err = l2rpc.CallContext(ctx, &txReceipt, "eth_getTransactionReceipt", block.Transactions()[1].Hash())
Require(t, err)
if txReceipt.Timeboosted == nil {
t.Fatal("timeboosted field should exist in the receipt object of second tx")
}
if *txReceipt.Timeboosted != true {
t.Fatal("second tx was timeboosted, but the field indicates otherwise")
}

// Print the receipt object for reference
var receiptResultRaw json.RawMessage
err = l2rpc.CallContext(ctx, &receiptResultRaw, "eth_getBlockReceipts", rpc.BlockNumber(blockNum.Int64()))
Require(t, err)
colors.PrintGrey("receipt object- ", string(receiptResultRaw))

}

func TestTimeboostBulkBlockMetadataAPI(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

builder := NewNodeBuilder(ctx).DefaultConfig(t, false)
builder.execConfig.BlockMetadataApiCacheSize = 0 // Caching is disabled
cleanup := builder.Build(t)
defer cleanup()

arbDb := builder.L2.ConsensusNode.ArbDB

// Generate blocks until current block is end
start := 1
Expand Down
Loading