Skip to content

Commit

Permalink
Skip executing the transactions guaranteed by other builders (#94)
Browse files Browse the repository at this point in the history
* Update espresso-sequencer-go

* Support builder-guaranteed tx

* Update the error message
  • Loading branch information
ImJeremyHe authored Mar 6, 2024
1 parent 06f7c3d commit 2293be6
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 15 deletions.
41 changes: 30 additions & 11 deletions arbos/block_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package arbos

import (
"bytes"
"encoding/binary"
"errors"
"fmt"
Expand All @@ -17,6 +18,7 @@ import (
"github.com/offchainlabs/nitro/solgen/go/precompilesgen"
"github.com/offchainlabs/nitro/util/arbmath"

espressoTypes "github.com/EspressoSystems/espresso-sequencer-go/types"
"github.com/ethereum/go-ethereum/arbitrum_types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
Expand All @@ -42,6 +44,8 @@ var EmitReedeemScheduledEvent func(*vm.EVM, uint64, uint64, [32]byte, [32]byte,
var EmitTicketCreatedEvent func(*vm.EVM, [32]byte) error
var gasUsedSinceStartupCounter = metrics.NewRegisteredCounter("arb/gas_used", nil)

const NOT_EXPECTED_BUILDER_ERROR string = "This transaction is part of a block not built by the desired builder"

// A helper struct that implements String() by marshalling to JSON.
// This is useful for logging because it's lazy, so if the log level is too high to print the transaction,
// it doesn't waste compute marshalling the transaction when the result wouldn't be used.
Expand Down Expand Up @@ -71,7 +75,7 @@ func (info *L1Info) L1BlockNumber() uint64 {
return info.l1BlockNumber
}

func createNewHeader(prevHeader *types.Header, l1info *L1Info, state *arbosState.ArbosState, chainConfig *params.ChainConfig, hotShotHeight uint64) *types.Header {
func createNewHeader(prevHeader *types.Header, l1info *L1Info, state *arbosState.ArbosState, chainConfig *params.ChainConfig, espressoHeader *espressoTypes.Header) *types.Header {
l2Pricing := state.L2PricingState()
baseFee, err := l2Pricing.BaseFeeWei()
state.Restrict(err)
Expand All @@ -96,8 +100,10 @@ func createNewHeader(prevHeader *types.Header, l1info *L1Info, state *arbosState
mixDigest = prevHeader.MixDigest

if chainConfig.ArbitrumChainParams.EnableEspresso {
// Store the hotshot height temporarily
binary.BigEndian.PutUint64(mixDigest[24:32], hotShotHeight)
if espressoHeader != nil {
// Store the hotshot height temporarily
binary.BigEndian.PutUint64(mixDigest[24:32], espressoHeader.Height)
}
}
}
header := &types.Header{
Expand Down Expand Up @@ -176,7 +182,7 @@ func ProduceBlock(
txes = types.Transactions{}
}

var height uint64
var espressoHeader *espressoTypes.Header
if chainConfig.ArbitrumChainParams.EnableEspresso {
if message.Header.Kind == arbostypes.L1MessageType_L2Message &&
message.L2msg[0] == L2MessageKind_EspressoTx {
Expand All @@ -185,17 +191,14 @@ func ProduceBlock(
if err != nil {
return nil, nil, err
}
height = jst.Header.Height
} else {
lastInfo := types.DeserializeHeaderExtraInformation(lastBlockHeader)
height = lastInfo.HotShotHeight
espressoHeader = &jst.Header
}
}

hooks := NoopSequencingHooks()

return ProduceBlockAdvanced(
message.Header, txes, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, hooks, height,
message.Header, txes, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, hooks, espressoHeader,
)
}

Expand All @@ -209,7 +212,7 @@ func ProduceBlockAdvanced(
chainContext core.ChainContext,
chainConfig *params.ChainConfig,
sequencingHooks *SequencingHooks,
height uint64,
espressoHeader *espressoTypes.Header,
) (*types.Block, types.Receipts, error) {

state, err := arbosState.OpenSystemArbosState(statedb, nil, true)
Expand All @@ -229,7 +232,7 @@ func ProduceBlockAdvanced(
l1Timestamp: l1Header.Timestamp,
}

header := createNewHeader(lastBlockHeader, l1Info, state, chainConfig, height)
header := createNewHeader(lastBlockHeader, l1Info, state, chainConfig, espressoHeader)
signer := types.MakeSigner(chainConfig, header.Number, header.Time)
// Note: blockGasLeft will diverge from the actual gas left during execution in the event of invalid txs,
// but it's only used as block-local representation limiting the amount of work done in a block.
Expand Down Expand Up @@ -312,6 +315,22 @@ func ProduceBlockAdvanced(
return nil, nil, err
}

if chainConfig.ArbitrumChainParams.EnableEspresso {
calldata := tx.Data()
calldataLen := len(calldata)
if calldataLen >= 52 {
extraData := calldata[calldataLen-52:]
var magicBytes [32]byte
copy(magicBytes[:], extraData[:32])
if magicBytes == espressoTypes.GetMagicBytes() {
builderAddr := espressoHeader.FeeInfo.Account.Bytes()
if !bytes.Equal(builderAddr, extraData[32:]) {
return nil, nil, fmt.Errorf(NOT_EXPECTED_BUILDER_ERROR)
}
}
}
}

if basefee.Sign() > 0 {
dataGas = math.MaxUint64
brotliCompressionLevel, err := state.BrotliCompressionLevel()
Expand Down
4 changes: 2 additions & 2 deletions execution/gethexec/executionengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ func (s *ExecutionEngine) SequenceTransactionsEspresso(
s.bc,
s.bc.Config(),
hooks,
jst.Header.Height,
&jst.Header,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -404,7 +404,7 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes.
s.bc,
s.bc.Config(),
hooks,
0,
nil,
)
if err != nil {
return nil, err
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ module github.com/offchainlabs/nitro

go 1.20


replace github.com/VictoriaMetrics/fastcache => ./fastcache

replace github.com/ethereum/go-ethereum => ./go-ethereum

require (
github.com/EspressoSystems/espresso-sequencer-go v0.0.8-0.20240223102020-14d6344d941c
github.com/EspressoSystems/espresso-sequencer-go v0.0.9
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
github.com/Shopify/toxiproxy v2.1.4+incompatible
github.com/alicebob/miniredis/v2 v2.21.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ github.com/EspressoSystems/espresso-sequencer-go v0.0.7 h1:qzP7WsBEej+6mZ6UUSWQp
github.com/EspressoSystems/espresso-sequencer-go v0.0.7/go.mod h1:9dSL1bj0l+jpgaMRmi55YeRBd3AhOZz8/HXQcQ42mRQ=
github.com/EspressoSystems/espresso-sequencer-go v0.0.8-0.20240223102020-14d6344d941c h1:uMi+cuSgTzsHUOJHERRxH/tItZT7Oi7x9Vv+fcAOv74=
github.com/EspressoSystems/espresso-sequencer-go v0.0.8-0.20240223102020-14d6344d941c/go.mod h1:9dSL1bj0l+jpgaMRmi55YeRBd3AhOZz8/HXQcQ42mRQ=
github.com/EspressoSystems/espresso-sequencer-go v0.0.8 h1:OtlZZsGKaSONOFjlU5zcPuJkNj2Y8+fK5NZ+TQr/+RU=
github.com/EspressoSystems/espresso-sequencer-go v0.0.8/go.mod h1:9dSL1bj0l+jpgaMRmi55YeRBd3AhOZz8/HXQcQ42mRQ=
github.com/EspressoSystems/espresso-sequencer-go v0.0.9 h1:KJkF79mmbzPi3qqiDdbobxURCwZbbp7YXjcB3YSC0to=
github.com/EspressoSystems/espresso-sequencer-go v0.0.9/go.mod h1:9dSL1bj0l+jpgaMRmi55YeRBd3AhOZz8/HXQcQ42mRQ=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
Expand Down
123 changes: 123 additions & 0 deletions system_tests/espresso_builder_tx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package arbtest

import (
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"testing"

espressoTypes "github.com/EspressoSystems/espresso-sequencer-go/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/offchainlabs/nitro/arbos"
"github.com/offchainlabs/nitro/arbos/arbosState"
"github.com/offchainlabs/nitro/arbos/arbostypes"
"github.com/offchainlabs/nitro/arbos/l2pricing"
"github.com/offchainlabs/nitro/statetransfer"
)

func TestEspressoBuilderGuaranteedTx(t *testing.T) {
l1Header := arbostypes.L1IncomingMessageHeader{
Kind: arbostypes.L1MessageType_L2Message,
L1BaseFee: big.NewInt(10000),
Poster: common.Address{},
}
chainDb := rawdb.NewMemoryDatabase()
chainConfig := params.ArbitrumDevTestChainConfig()
chainConfig.ArbitrumChainParams.EnableEspresso = true

serializedChainConfig, err := json.Marshal(chainConfig)
if err != nil {
panic(err)
}
initMessage := &arbostypes.ParsedInitMessage{
ChainId: chainConfig.ChainID,
InitialL1BaseFee: arbostypes.DefaultInitialL1BaseFee,
ChainConfig: chainConfig,
SerializedChainConfig: serializedChainConfig,
}
stateRoot, err := arbosState.InitializeArbosInDatabase(
chainDb,
statetransfer.NewMemoryInitDataReader(&statetransfer.ArbosInitializationInfo{}),
chainConfig,
initMessage,
0,
0,
)
if err != nil {
panic(err)
}
statedb, err := state.New(stateRoot, state.NewDatabase(chainDb), nil)
if err != nil {
panic(err)
}

chainContext := noopChainContext{}
seqBatch := make([]byte, 40)
binary.BigEndian.PutUint64(seqBatch[8:16], ^uint64(0))
binary.BigEndian.PutUint64(seqBatch[24:32], ^uint64(0))
binary.BigEndian.PutUint64(seqBatch[32:40], uint64(0))
genesis := &types.Header{
Number: new(big.Int),
Nonce: types.EncodeNonce(0),
Time: 0,
ParentHash: common.Hash{},
Extra: []byte("Arbitrum"),
GasLimit: l2pricing.GethBlockGasLimit,
GasUsed: 0,
BaseFee: big.NewInt(l2pricing.InitialBaseFeeWei),
Difficulty: big.NewInt(1),
MixDigest: common.Hash{},
Coinbase: common.Address{},
Root: stateRoot,
}

builderAddr := common.HexToAddress("0xa3612e81E1f9cdF8f54C3d65f7FBc0aBf5B21E8f")
// Since we are not really building a block, so this illegal header is enough
espressoHeader := espressoTypes.Header{
FeeInfo: &espressoTypes.FeeInfo{Account: builderAddr},
Height: 10,
}

testInfo := NewArbTestInfo(t, chainConfig.ChainID)

otherBuilderTx := testInfo.PrepareTx("Owner", "Faucet", 1000000, big.NewInt(1000000), getExtraBytes(common.Address{10: 5}))
hooks := arbos.NoopSequencingHooks()
_, _, err = arbos.ProduceBlockAdvanced(&l1Header, types.Transactions{otherBuilderTx}, 0, genesis, statedb, chainContext, chainConfig, hooks, &espressoHeader)
if err != nil {
panic(err)
}
txErr := hooks.TxErrors[0]
if txErr == nil {
panic("this tx should not be executed successfully")
}
if txErr.Error() != arbos.NOT_EXPECTED_BUILDER_ERROR {
panic(fmt.Sprintf("error should be %s", arbos.NOT_EXPECTED_BUILDER_ERROR))
}

hooks2 := arbos.NoopSequencingHooks()
thisBuilderTx := testInfo.PrepareTx("Owner", "Faucet", 1000000, big.NewInt(1000000), getExtraBytes(builderAddr))
_, _, err = arbos.ProduceBlockAdvanced(&l1Header, types.Transactions{thisBuilderTx}, 0, genesis, statedb, chainContext, chainConfig, hooks2, &espressoHeader)
if err != nil {
panic(err)
}
txErr2 := hooks2.TxErrors[0]
if txErr2 == nil {
panic("this tx should not be executed successfully")
}
if txErr2.Error() == arbos.NOT_EXPECTED_BUILDER_ERROR {
panic(fmt.Sprintf("error should not be %s", arbos.NOT_EXPECTED_BUILDER_ERROR))
}
}

func getExtraBytes(addr common.Address) []byte {
result := [52]byte{}
magicBytes := espressoTypes.GetMagicBytes()
copy(result[0:32], magicBytes[:])
copy(result[32:], addr.Bytes())
return result[:]
}

0 comments on commit 2293be6

Please sign in to comment.