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

Tipping tx feature gating #1946

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions arbnode/delayed.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func (b *DelayedBridge) logsToDeliveredMessages(ctx context.Context, logs []type
Timestamp: parsedLog.Timestamp,
RequestId: &requestId,
L1BaseFee: parsedLog.BaseFeeL1,
Features: 0, // TODO (magic) comment, for now it seems that we don't need any features set here
},
L2msg: data,
},
Expand Down
1 change: 1 addition & 0 deletions arbnode/delayed_seq_reorg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func TestSequencerReorgFromDelayed(t *testing.T) {
Timestamp: 0,
RequestId: &delayedRequestId,
L1BaseFee: common.Big0,
Features: 0, // TODO(magic)
},
},
}
Expand Down
1 change: 1 addition & 0 deletions arbnode/inbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ func TestTransactionStreamer(t *testing.T) {
Kind: arbostypes.L1MessageType_L2Message,
Poster: source,
RequestId: &requestId,
Features: 0, // TODO(magic)
},
L2msg: l2Message,
},
Expand Down
2 changes: 2 additions & 0 deletions arbnode/transaction_streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ func (s *TransactionStreamer) reorg(batch ethdb.Batch, count arbutil.MessageInde
if delayedFound.Message.Header.RequestId.Big().Uint64() != delayedSeqNum {
continue delayedInBlockLoop
}
delayedFound.Message.Header.Features = oldMessage.Message.Header.Features
if expectedAcc == delayedFound.AfterInboxAcc() && delayedFound.Message.Equals(oldMessage.Message) {
messageFound = true
}
Expand Down Expand Up @@ -540,6 +541,7 @@ func (s *TransactionStreamer) AddFakeInitMessage() error {
Kind: arbostypes.L1MessageType_Initialize,
RequestId: &common.Hash{},
L1BaseFee: common.Big0,
Features: 0, // TODO(magic)
},
L2msg: msg,
},
Expand Down
115 changes: 80 additions & 35 deletions arbos/arbostypes/incomingmessage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"errors"
"fmt"
"io"
"math"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"

Expand All @@ -34,7 +36,41 @@ const (

const MaxL2MessageSize = 256 * 1024

const ArbosVersion_ArbitrumSubtypedTx = 12
const ArbosVersion_ArbitrumTippingTx = 12

func RequiredArobosVersionForTxSubtype(txSubtype uint8) uint64 {
switch txSubtype {
case types.ArbitrumTippingTxSubtype:
return ArbosVersion_ArbitrumTippingTx
default:
// shouldn't be ever possible as tx with unsupported subtype is dropped earlier
return math.MaxUint64
}
}

type Features uint8

const (
FeatureFlag_ArbitrumTippingTx Features = (1 << 0)
FeatureFlag_Reserved Features = (1 << 7) // could be used to implement future feature format versioning e.g. where version = feature_flags & 0x7f
)

func (f Features) TxSubtypeSupported(subtype uint8) bool {
switch subtype {
case types.ArbitrumTippingTxSubtype:
return (f & FeatureFlag_ArbitrumTippingTx) > 0
default:
return false
}
}

func ArbosVersionBasedFeatureFlags(arbosVersion uint64) Features {
var features Features
if arbosVersion > ArbosVersion_ArbitrumTippingTx {
features |= FeatureFlag_ArbitrumTippingTx
}
return features
}

type L1IncomingMessageHeader struct {
Kind uint8 `json:"kind"`
Expand All @@ -43,6 +79,7 @@ type L1IncomingMessageHeader struct {
Timestamp uint64 `json:"timestamp"`
RequestId *common.Hash `json:"requestId" rlp:"nilList"`
L1BaseFee *big.Int `json:"baseFeeL1"`
Features Features `json:"features,omitempty" rlp:"optional"`
}

func (h L1IncomingMessageHeader) SeqNum() (uint64, error) {
Expand Down Expand Up @@ -83,50 +120,56 @@ var InvalidL1Message = &L1IncomingMessage{
L2msg: []byte{},
}

func (msg *L1IncomingMessage) Serialize() ([]byte, error) {
func (m *L1IncomingMessage) Serialize() ([]byte, error) {
wr := &bytes.Buffer{}
if err := wr.WriteByte(msg.Header.Kind); err != nil {
if err := wr.WriteByte(m.Header.Kind); err != nil {
return nil, err
}

if err := util.AddressTo256ToWriter(msg.Header.Poster, wr); err != nil {
if err := util.AddressTo256ToWriter(m.Header.Poster, wr); err != nil {
return nil, err
}

if err := util.Uint64ToWriter(msg.Header.BlockNumber, wr); err != nil {
if err := util.Uint64ToWriter(m.Header.BlockNumber, wr); err != nil {
return nil, err
}

if err := util.Uint64ToWriter(msg.Header.Timestamp, wr); err != nil {
if err := util.Uint64ToWriter(m.Header.Timestamp, wr); err != nil {
return nil, err
}

if msg.Header.RequestId == nil {
if m.Header.RequestId == nil {
return nil, errors.New("cannot serialize L1IncomingMessage without RequestId")
}
requestId := *msg.Header.RequestId
requestId := *m.Header.RequestId
if err := util.HashToWriter(requestId, wr); err != nil {
return nil, err
}

var l1BaseFeeHash common.Hash
if msg.Header.L1BaseFee == nil {
if m.Header.L1BaseFee == nil {
return nil, errors.New("cannot serialize L1IncomingMessage without L1BaseFee")
}
l1BaseFeeHash = common.BigToHash(msg.Header.L1BaseFee)
l1BaseFeeHash = common.BigToHash(m.Header.L1BaseFee)
if err := util.HashToWriter(l1BaseFeeHash, wr); err != nil {
return nil, err
}

if _, err := wr.Write(msg.L2msg); err != nil {
if m.Header.Features != 0 {
if err := wr.WriteByte(uint8(m.Header.Features)); err != nil {
return nil, err
}
}

if _, err := wr.Write(m.L2msg); err != nil {
return nil, err
}

return wr.Bytes(), nil
}

func (msg *L1IncomingMessage) Equals(other *L1IncomingMessage) bool {
return msg.Header.Equals(other.Header) && bytes.Equal(msg.L2msg, other.L2msg)
func (m *L1IncomingMessage) Equals(other *L1IncomingMessage) bool {
return m.Header.Equals(other.Header) && bytes.Equal(m.L2msg, other.L2msg)
}

func hashesEqual(ha, hb *common.Hash) bool {
Expand All @@ -143,7 +186,8 @@ func (h *L1IncomingMessageHeader) Equals(other *L1IncomingMessageHeader) bool {
h.BlockNumber == other.BlockNumber &&
h.Timestamp == other.Timestamp &&
hashesEqual(h.RequestId, other.RequestId) &&
arbmath.BigEquals(h.L1BaseFee, other.L1BaseFee)
arbmath.BigEquals(h.L1BaseFee, other.L1BaseFee) &&
h.Features == other.Features
}

func ComputeBatchGasCost(data []byte) uint64 {
Expand All @@ -163,11 +207,11 @@ func ComputeBatchGasCost(data []byte) uint64 {
return gas
}

func (msg *L1IncomingMessage) FillInBatchGasCost(batchFetcher FallibleBatchFetcher) error {
if batchFetcher == nil || msg.Header.Kind != L1MessageType_BatchPostingReport || msg.BatchGasCost != nil {
func (m *L1IncomingMessage) FillInBatchGasCost(batchFetcher FallibleBatchFetcher) error {
if batchFetcher == nil || m.Header.Kind != L1MessageType_BatchPostingReport || m.BatchGasCost != nil {
return nil
}
_, _, batchHash, batchNum, _, _, err := ParseBatchPostingReportMessageFields(bytes.NewReader(msg.L2msg))
_, _, batchHash, batchNum, _, _, err := ParseBatchPostingReportMessageFields(bytes.NewReader(m.L2msg))
if err != nil {
return fmt.Errorf("failed to parse batch posting report: %w", err)
}
Expand All @@ -180,7 +224,7 @@ func (msg *L1IncomingMessage) FillInBatchGasCost(batchFetcher FallibleBatchFetch
return fmt.Errorf("batch fetcher returned incorrect data hash %v (wanted %v for batch %v)", gotHash, batchHash, batchNum)
}
gas := ComputeBatchGasCost(batchData)
msg.BatchGasCost = &gas
m.BatchGasCost = &gas
return nil
}

Expand Down Expand Up @@ -224,12 +268,13 @@ func ParseIncomingL1Message(rd io.Reader, batchFetcher FallibleBatchFetcher) (*L

msg := &L1IncomingMessage{
&L1IncomingMessageHeader{
kind,
sender,
blockNumber,
timestamp,
&requestId,
baseFeeL1.Big(),
Kind: kind,
Poster: sender,
BlockNumber: blockNumber,
Timestamp: timestamp,
RequestId: &requestId,
L1BaseFee: baseFeeL1.Big(),
Features: 0, // TODO(magic)
},
data,
nil,
Expand Down Expand Up @@ -261,21 +306,21 @@ var TestInitMessage = &ParsedInitMessage{
}

// ParseInitMessage returns the chain id on success
func (msg *L1IncomingMessage) ParseInitMessage() (*ParsedInitMessage, error) {
if msg.Header.Kind != L1MessageType_Initialize {
return nil, fmt.Errorf("invalid init message kind %v", msg.Header.Kind)
func (m *L1IncomingMessage) ParseInitMessage() (*ParsedInitMessage, error) {
if m.Header.Kind != L1MessageType_Initialize {
return nil, fmt.Errorf("invalid init message kind %v", m.Header.Kind)
}
basefee := new(big.Int).Set(DefaultInitialL1BaseFee)
var chainConfig params.ChainConfig
var chainId *big.Int
if len(msg.L2msg) == 32 {
chainId = new(big.Int).SetBytes(msg.L2msg[:32])
if len(m.L2msg) == 32 {
chainId = new(big.Int).SetBytes(m.L2msg[:32])
return &ParsedInitMessage{chainId, basefee, nil, nil}, nil
}
if len(msg.L2msg) > 32 {
chainId = new(big.Int).SetBytes(msg.L2msg[:32])
version := msg.L2msg[32]
reader := bytes.NewReader(msg.L2msg[33:])
if len(m.L2msg) > 32 {
chainId = new(big.Int).SetBytes(m.L2msg[:32])
version := m.L2msg[32]
reader := bytes.NewReader(m.L2msg[33:])
switch version {
case 1:
var err error
Expand All @@ -291,12 +336,12 @@ func (msg *L1IncomingMessage) ParseInitMessage() (*ParsedInitMessage, error) {
}
err = json.Unmarshal(serializedChainConfig, &chainConfig)
if err != nil {
return nil, fmt.Errorf("failed to parse init message, err: %w, message data: %v", err, string(msg.L2msg))
return nil, fmt.Errorf("failed to parse init message, err: %w, message data: %v", err, string(m.L2msg))
}
return &ParsedInitMessage{chainId, basefee, &chainConfig, serializedChainConfig}, nil
}
}
return nil, fmt.Errorf("invalid init message data %v", string(msg.L2msg))
return nil, fmt.Errorf("invalid init message data %v", string(m.L2msg))
}

func ParseBatchPostingReportMessageFields(rd io.Reader) (*big.Int, common.Address, common.Hash, uint64, *big.Int, uint64, error) {
Expand Down
2 changes: 1 addition & 1 deletion arbos/block_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func ProduceBlock(
return nil, nil, err
}
var batchFetchErr error
txes, err := ParseL2Transactions(message, chainConfig.ChainID, arbState.ArbOSVersion(), func(batchNum uint64, batchHash common.Hash) []byte {
txes, err := ParseL2Transactions(message, chainConfig.ChainID, func(batchNum uint64, batchHash common.Hash) []byte {
data, err := batchFetcher(batchNum)
if err != nil {
batchFetchErr = err
Expand Down
3 changes: 2 additions & 1 deletion arbos/incomingmessage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func TestSerializeAndParseL1Message(t *testing.T) {
Timestamp: 8794561564,
RequestId: &requestId,
L1BaseFee: big.NewInt(10000000000000),
Features: 0, // TODO(magic)
}
msg := arbostypes.L1IncomingMessage{
Header: &header,
Expand All @@ -36,7 +37,7 @@ func TestSerializeAndParseL1Message(t *testing.T) {
if err != nil {
t.Error(err)
}
txes, err := ParseL2Transactions(newMsg, chainId, 0, nil)
txes, err := ParseL2Transactions(newMsg, chainId, nil)
if err != nil {
t.Error(err)
}
Expand Down
11 changes: 6 additions & 5 deletions arbos/parse_l2.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import (

type InfallibleBatchFetcher func(batchNum uint64, batchHash common.Hash) []byte

func ParseL2Transactions(msg *arbostypes.L1IncomingMessage, chainId *big.Int, arbOSVersion uint64, batchFetcher InfallibleBatchFetcher) (types.Transactions, error) {
func ParseL2Transactions(msg *arbostypes.L1IncomingMessage, chainId *big.Int, batchFetcher InfallibleBatchFetcher) (types.Transactions, error) {
if len(msg.L2msg) > arbostypes.MaxL2MessageSize {
// ignore the message if l2msg is too large
return nil, errors.New("message too large")
}
switch msg.Header.Kind {
case arbostypes.L1MessageType_L2Message:
return parseL2Message(bytes.NewReader(msg.L2msg), msg.Header.Poster, msg.Header.Timestamp, msg.Header.RequestId, chainId, 0, arbOSVersion)
return parseL2Message(bytes.NewReader(msg.L2msg), msg.Header.Poster, msg.Header.Timestamp, msg.Header.RequestId, chainId, msg.Header.Features, 0)
case arbostypes.L1MessageType_Initialize:
return nil, errors.New("ParseL2Transactions encounted initialize message (should've been handled explicitly at genesis)")
case arbostypes.L1MessageType_EndOfBlock:
Expand Down Expand Up @@ -109,7 +109,7 @@ func parseTimeOrPanic(format string, value string) time.Time {

var HeartbeatsDisabledAt = uint64(parseTimeOrPanic(time.RFC1123, "Mon, 08 Aug 2022 16:00:00 GMT").Unix())

func parseL2Message(rd io.Reader, poster common.Address, timestamp uint64, requestId *common.Hash, chainId *big.Int, depth int, arbOSVersion uint64) (types.Transactions, error) {
func parseL2Message(rd io.Reader, poster common.Address, timestamp uint64, requestId *common.Hash, chainId *big.Int, features arbostypes.Features, depth int) (types.Transactions, error) {
var l2KindBuf [1]byte
if _, err := rd.Read(l2KindBuf[:]); err != nil {
return nil, err
Expand Down Expand Up @@ -149,7 +149,7 @@ func parseL2Message(rd io.Reader, poster common.Address, timestamp uint64, reque
subRequestId := crypto.Keccak256Hash(requestId[:], math.U256Bytes(index))
nextRequestId = &subRequestId
}
nestedSegments, err := parseL2Message(bytes.NewReader(nextMsg), poster, timestamp, nextRequestId, chainId, depth+1, arbOSVersion)
nestedSegments, err := parseL2Message(bytes.NewReader(nextMsg), poster, timestamp, nextRequestId, chainId, features, depth+1)
if err != nil {
return nil, err
}
Expand All @@ -166,7 +166,8 @@ func parseL2Message(rd io.Reader, poster common.Address, timestamp uint64, reque
if err := newTx.UnmarshalBinary(readBytes); err != nil {
return nil, err
}
if newTx.Type() == types.ArbitrumSubtypedTxType && arbOSVersion < arbostypes.ArbosVersion_ArbitrumSubtypedTx {
if newTx.Type() == types.ArbitrumSubtypedTxType && !features.TxSubtypeSupported(types.GetArbitrumTxSubtype(newTx)) {

return nil, types.ErrTxTypeNotSupported
}
if newTx.Type() >= types.ArbitrumDepositTxType || newTx.Type() == types.BlobTxType {
Expand Down
3 changes: 2 additions & 1 deletion arbos/tx_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,8 @@ func (p *TxProcessor) DropTip() bool {
version := p.state.ArbOSVersion()
transaction := p.msg.Tx
tippingTx := false
if version >= arbostypes.ArbosVersion_ArbitrumSubtypedTx && transaction != nil && transaction.Type() == types.ArbitrumSubtypedTxType {
// TODO(magic)
if version >= arbostypes.ArbosVersion_ArbitrumTippingTx && transaction != nil && transaction.Type() == types.ArbitrumSubtypedTxType {
subtype := types.GetArbitrumTxSubtype(transaction)
tippingTx = subtype == types.ArbitrumTippingTxSubtype
}
Expand Down
8 changes: 6 additions & 2 deletions arbstate/das_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ const DASMessageHeaderFlag byte = 0x80
// Ignored when DASMessageHeaderFlag is not set.
const TreeDASMessageHeaderFlag byte = 0x08

// L1AuthenticatedMessageHeaderFlag indicates that this message was authenticated by L1. Currently unused.
const L1AuthenticatedMessageHeaderFlag byte = 0x40
// FeaturesHeaderFlag indicates that TODO
const FeaturesHeaderFlag byte = 0x40

// ZeroheavyMessageHeaderFlag indicates that this message is zeroheavy-encoded.
const ZeroheavyMessageHeaderFlag byte = 0x20
Expand All @@ -55,6 +55,10 @@ func IsZeroheavyEncodedHeaderByte(header byte) bool {
return (ZeroheavyMessageHeaderFlag & header) > 0
}

func FeaturesFlagSet(header byte) bool {
return (FeaturesHeaderFlag & header) > 0
}

func IsBrotliMessageHeaderByte(b uint8) bool {
return b == BrotliMessageHeaderByte
}
Expand Down
Loading
Loading