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

L1sload #748

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .github/workflows/l2geth_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ jobs:
- name: Checkout code
uses: actions/checkout@v2
- run: goimports -local github.com/scroll-tech/go-ethereum/ -w .
- run: git diff
# If there are any diffs from goimports, fail.
- name: Verify no changes from goimports
run: |
Expand Down
1 change: 1 addition & 0 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,7 @@ func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
func (m callMsg) IsL1MessageTx() bool { return false }
func (m callMsg) IsSystemTx() bool { return false }

// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
Expand Down
72 changes: 69 additions & 3 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ import (
"github.com/scroll-tech/go-ethereum/core/rawdb"
"github.com/scroll-tech/go-ethereum/core/state"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/ethdb"
"github.com/scroll-tech/go-ethereum/log"
"github.com/scroll-tech/go-ethereum/metrics"
"github.com/scroll-tech/go-ethereum/params"
"github.com/scroll-tech/go-ethereum/rollup/circuitcapacitychecker"
"github.com/scroll-tech/go-ethereum/rollup/rcfg"
"github.com/scroll-tech/go-ethereum/trie"
)

Expand Down Expand Up @@ -69,7 +71,7 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
}

type tracerWrapper interface {
CreateTraceEnvAndGetBlockTrace(*params.ChainConfig, ChainContext, consensus.Engine, ethdb.Database, *state.StateDB, *types.Block, *types.Block, bool) (*types.BlockTrace, error)
CreateTraceEnvAndGetBlockTrace(*params.ChainConfig, ChainContext, consensus.Engine, ethdb.Database, *state.StateDB, vm.L1Client, *types.Block, *types.Block, bool) (*types.BlockTrace, error)
}

func (v *BlockValidator) SetupTracerAndCircuitCapacityChecker(tracer tracerWrapper) {
Expand Down Expand Up @@ -111,6 +113,9 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
}
return consensus.ErrPrunedAncestor
}
if err := v.ValidateSystemTxs(block); err != nil {
return err
}
if err := v.ValidateL1Messages(block); err != nil {
return err
}
Expand All @@ -137,9 +142,66 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return nil
}

// ValidateSystemTxs validates all system txs contained in a block.
// We check that:
// - the sender is a predetermined address
// - the recipient is a system contract
func (v *BlockValidator) ValidateSystemTxs(block *types.Block) error {
// first pass: ensure that system txs are first, in a continuous block
haveSystemTx := false
haveNonSystemTx := false
for _, tx := range block.Transactions() {
if tx.IsSystemTx() {
if !v.config.Scroll.SystemTxEnabled() {
return ErrSystemTxNotEnabled
}

if haveNonSystemTx {
return consensus.ErrInvalidL1MessageOrder
}

haveSystemTx = true
continue
}

haveNonSystemTx = true
}

if !haveSystemTx {
return nil
}

for _, tx := range block.Transactions() {
if !tx.IsSystemTx() {
break
}

stx := tx.AsSystemTx()

found := false
if stx.Sender != rcfg.SystemSenderAddress {
return ErrUnknownSystemSigner
}

found = false
for _, contract := range v.config.Scroll.SystemTx.Contracts {
if stx.To == contract {
found = true
break
}
}

if !found {
return ErrUnknownSystemContract
}
}

return nil
}

// ValidateL1Messages validates L1 messages contained in a block.
// We check the following conditions:
// - L1 messages are in a contiguous section at the front of the block.
// - L1 messages are in a contiguous section at the front of the block, after system txs
// - The first L1 message's QueueIndex is right after the last L1 message included in the chain.
// - L1 messages follow the QueueIndex order.
// - The L1 messages included in the block match the node's view of the L1 ledger.
Expand Down Expand Up @@ -171,6 +233,10 @@ func (v *BlockValidator) ValidateL1Messages(block *types.Block) error {
it := rawdb.IterateL1MessagesFrom(v.bc.db, queueIndex)

for _, tx := range block.Transactions() {
if tx.IsSystemTx() {
continue
}

if !tx.IsL1MessageTx() {
L1SectionOver = true
continue // we do not verify L2 transactions here
Expand Down Expand Up @@ -298,7 +364,7 @@ func (v *BlockValidator) createTraceEnvAndGetBlockTrace(block *types.Block) (*ty
return nil, err
}

return v.tracer.CreateTraceEnvAndGetBlockTrace(v.config, v.bc, v.engine, v.bc.db, statedb, parent, block, true)
return v.tracer.CreateTraceEnvAndGetBlockTrace(v.config, v.bc, v.engine, v.bc.db, statedb, v.bc.GetVMConfig().L1Client, parent, block, true)
}

func (v *BlockValidator) validateCircuitRowConsumption(block *types.Block) (*types.RowConsumption, error) {
Expand Down
12 changes: 12 additions & 0 deletions core/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,15 @@ var (
// ErrSenderNoEOA is returned if the sender of a transaction is a contract.
ErrSenderNoEOA = errors.New("sender not an eoa")
)

var (
// ErrUnknownSystemSigner is returned if a sender for a system transaction
// is not a known whitelisted system signer
ErrUnknownSystemSigner = errors.New("unknown system signer")
// ErrUnknownSystemContract is returned if the destination for a system
// transaction is not a known whitelisted system contract
ErrUnknownSystemContract = errors.New("unknown system contract")
// ErrSystemTxNotEnabled is returned if a system tx is included in a block
// but system transactions are not enabled in the sequenced
ErrSystemTxNotEnabled = errors.New("unexpected system transaction")
)
17 changes: 15 additions & 2 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type Message interface {
Data() []byte
AccessList() types.AccessList
IsL1MessageTx() bool
IsSystemTx() bool
}

// ExecutionResult includes all output after executing given evm
Expand Down Expand Up @@ -227,6 +228,11 @@ func (st *StateTransition) to() common.Address {
}

func (st *StateTransition) buyGas() error {
if st.msg.IsSystemTx() {
// no gas accounting for system txs
return nil
}

mgval := new(big.Int).SetUint64(st.msg.Gas())
mgval = mgval.Mul(mgval, st.gasPrice)

Expand Down Expand Up @@ -266,6 +272,13 @@ func (st *StateTransition) buyGas() error {
}

func (st *StateTransition) preCheck() error {
if st.msg.IsSystemTx() {
// system tx gas is free and not accounted for.
st.gas += st.msg.Gas()
st.initialGas = st.msg.Gas()
return nil
}

if st.msg.IsL1MessageTx() {
// No fee fields to check, no nonce to check, and no need to check if EOA (L1 already verified it for us)
// Gas is free, but no refunds!
Expand Down Expand Up @@ -400,8 +413,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
stateTransitionEvmCallExecutionTimer.Update(time.Since(evmCallStart))
}

// no refunds for l1 messages
if st.msg.IsL1MessageTx() {
// no refunds for l1 messages and system txs
if st.msg.IsL1MessageTx() || st.msg.IsSystemTx() {
return &ExecutionResult{
L1DataFee: big.NewInt(0),
UsedGas: st.gasUsed(),
Expand Down
5 changes: 5 additions & 0 deletions core/tx_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,11 @@ func (l *txPricedList) Removed(count int) {
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
// lowest priced (remote) transaction currently being tracked.
func (l *txPricedList) Underpriced(tx *types.Transaction) bool {
// system txs are never priced
if tx.IsSystemTx() {
return false
}

// Note: with two queues, being underpriced is defined as being worse than the worst item
// in all non-empty queues if there is any. If both queues are empty then nothing is underpriced.
return (l.underpricedFor(&l.urgent, tx) || len(l.urgent.list) == 0) &&
Expand Down
4 changes: 2 additions & 2 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,8 @@ func (pool *TxPool) local() map[common.Address]types.Transactions {
// validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
// No unauthenticated deposits allowed in the transaction pool.
if tx.IsL1MessageTx() {
// No unauthenticated deposits or system txs allowed in the transaction pool.
if tx.IsL1MessageTx() || tx.IsSystemTx() {
return ErrTxTypeNotSupported
}

Expand Down
5 changes: 4 additions & 1 deletion core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func (r *Receipt) decodeTyped(b []byte) error {
return errEmptyTypedReceipt
}
switch b[0] {
case DynamicFeeTxType, AccessListTxType, BlobTxType, L1MessageTxType:
case DynamicFeeTxType, AccessListTxType, BlobTxType, L1MessageTxType, SystemTxType:
var data receiptRLP
err := rlp.DecodeBytes(b[1:], &data)
if err != nil {
Expand Down Expand Up @@ -434,6 +434,9 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
case L1MessageTxType:
w.WriteByte(L1MessageTxType)
rlp.Encode(w, data)
case SystemTxType:
w.WriteByte(SystemTxType)
rlp.Encode(w, data)
default:
// For unsupported types, write nothing. Since this is for
// DeriveSha, the error will be caught matching the derived hash
Expand Down
84 changes: 84 additions & 0 deletions core/types/system_tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package types

import (
"bytes"
"math/big"

"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/rlp"
)

type SystemTx struct {
Sender common.Address // pre-determined sender
To common.Address // system contract address
Data []byte // calldata
}

// not accountend
const SystemTxGas = 1_000_000

func (tx *SystemTx) txType() byte { return SystemTxType }

func (tx *SystemTx) copy() TxData {
return &SystemTx{
Sender: tx.Sender,
To: tx.To,
Data: common.CopyBytes(tx.Data),
}
}

func (tx *SystemTx) chainID() *big.Int { return new(big.Int) }
func (tx *SystemTx) accessList() AccessList { return nil }
func (tx *SystemTx) data() []byte { return tx.Data }
func (tx *SystemTx) gas() uint64 { return SystemTxGas }
func (tx *SystemTx) gasPrice() *big.Int { return new(big.Int) }
func (tx *SystemTx) gasTipCap() *big.Int { return new(big.Int) }
func (tx *SystemTx) gasFeeCap() *big.Int { return new(big.Int) }
func (tx *SystemTx) value() *big.Int { return new(big.Int) }
func (tx *SystemTx) nonce() uint64 { return 0 }
func (tx *SystemTx) to() *common.Address { return &tx.To }

func (tx *SystemTx) rawSignatureValues() (v, r, s *big.Int) {
return new(big.Int), new(big.Int), new(big.Int)
}

func (tx *SystemTx) setSignatureValues(chainID, v, r, s *big.Int) {}

func (tx *SystemTx) encode(b *bytes.Buffer) error {
return rlp.Encode(b, tx)
}

func (tx *SystemTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

var _ TxData = (*SystemTx)(nil)

func NewOrderedSystemTxs(stxs []*SystemTx) *OrderedSystemTxs {
txs := make([]*Transaction, 0, len(stxs))
for _, stx := range stxs {
txs = append(txs, NewTx(stx))
}
return &OrderedSystemTxs{txs: txs}
}

type OrderedSystemTxs struct {
txs []*Transaction
}

func (o *OrderedSystemTxs) Peek() *Transaction {
if len(o.txs) > 0 {
return o.txs[0]
}
return nil
}

func (o *OrderedSystemTxs) Shift() {
if len(o.txs) > 0 {
o.txs = o.txs[1:]
}
}

func (o *OrderedSystemTxs) Pop() {}

var _ OrderedTransactionSet = (*OrderedSystemTxs)(nil)
Loading