Skip to content

Commit 09af684

Browse files
Handle system tx (#191)
* handle system call * revert unnecessary changes * revert unnecessary changes * fix consensus * fix RewardStartedSlot * handle errors * clean * max gas * Refine the getSystemResult implementation * refactor start hook --------- Co-authored-by: curryxbo <[email protected]>
1 parent d28602a commit 09af684

File tree

12 files changed

+393
-7
lines changed

12 files changed

+393
-7
lines changed

consensus/clique/clique.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,10 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header
561561
return nil
562562
}
563563

564+
func (c *Clique) StartHook(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB) error {
565+
return nil
566+
}
567+
564568
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
565569
// rewards given.
566570
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {

consensus/consensus.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ type Engine interface {
8181
// rules of a particular engine. The changes are executed inline.
8282
Prepare(chain ChainHeaderReader, header *types.Header) error
8383

84+
// StartHook calling before start apply transactions of block
85+
//StartHook(chain consensus.ChainHeaderReader, header *types.Header, preHeader *types.Header, state *state.StateDB) error
86+
StartHook(chain ChainHeaderReader, header *types.Header, state *state.StateDB) error
87+
8488
// Finalize runs any post-transaction state modifications (e.g. block rewards)
8589
// but does not assemble the block.
8690
//

consensus/ethash/consensus.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,10 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H
590590
return nil
591591
}
592592

593+
func (ethash *Ethash) StartHook(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB) error {
594+
return nil
595+
}
596+
593597
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
594598
// setting the final state on the header
595599
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {

consensus/l2/consensus.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,27 @@ package l2
33
import (
44
"errors"
55
"fmt"
6+
"github.com/morph-l2/go-ethereum/contracts/morphtoken"
67
"math/big"
78

89
"github.com/morph-l2/go-ethereum/common"
910
"github.com/morph-l2/go-ethereum/consensus"
1011
"github.com/morph-l2/go-ethereum/consensus/misc"
12+
"github.com/morph-l2/go-ethereum/contracts/l2staking"
13+
"github.com/morph-l2/go-ethereum/core"
1114
"github.com/morph-l2/go-ethereum/core/state"
1215
"github.com/morph-l2/go-ethereum/core/types"
16+
"github.com/morph-l2/go-ethereum/core/vm"
1317
"github.com/morph-l2/go-ethereum/params"
18+
"github.com/morph-l2/go-ethereum/rollup/rcfg"
1419
"github.com/morph-l2/go-ethereum/rpc"
1520
"github.com/morph-l2/go-ethereum/trie"
1621
)
1722

1823
var (
19-
l2Difficulty = common.Big0 // The default block difficulty in the l2 consensus
20-
l2Nonce = types.EncodeNonce(0) // The default block nonce in the l2 consensus
24+
l2Difficulty = common.Big0 // The default block difficulty in the l2 consensus
25+
l2Nonce = types.EncodeNonce(0) // The default block nonce in the l2 consensus
26+
rewardEpoch uint64 = 86400
2127
)
2228

2329
// Various error messages to mark blocks invalid. These should be private to
@@ -31,6 +37,26 @@ var (
3137
errInvalidTimestamp = errors.New("invalid timestamp")
3238
)
3339

40+
// chain context
41+
type chainContext struct {
42+
Chain consensus.ChainHeaderReader
43+
engine consensus.Engine
44+
}
45+
46+
func (c chainContext) Engine() consensus.Engine {
47+
return c.engine
48+
}
49+
50+
func (c chainContext) GetHeader(hash common.Hash, number uint64) *types.Header {
51+
return c.Chain.GetHeader(hash, number)
52+
}
53+
54+
func (c chainContext) Config() *params.ChainConfig {
55+
return c.Chain.Config()
56+
}
57+
58+
var _ = consensus.Engine(&Consensus{})
59+
3460
type Consensus struct {
3561
ethone consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique
3662
config *params.ChainConfig
@@ -186,6 +212,44 @@ func (l2 *Consensus) Prepare(chain consensus.ChainHeaderReader, header *types.He
186212
return nil
187213
}
188214

215+
// StartHook implements calling before start apply transactions of block
216+
func (l2 *Consensus) StartHook(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB) error {
217+
rewardStarted := state.GetState(rcfg.L2StakingAddress, rcfg.RewardStartedSlot).Big()
218+
if rewardStarted.Cmp(common.Big1) != 0 {
219+
return nil
220+
}
221+
inflationMintedEpochs := state.GetState(rcfg.MorphTokenAddress, rcfg.InflationMintedEpochsSolt).Big().Uint64()
222+
rewardStartTime := state.GetState(rcfg.L2StakingAddress, rcfg.RewardStartTimeSlot).Big().Uint64()
223+
parentHeader := chain.GetHeaderByHash(header.ParentHash)
224+
if parentHeader == nil {
225+
return consensus.ErrUnknownAncestor
226+
}
227+
cx := chainContext{Chain: chain, engine: l2.ethone}
228+
blockContext := core.NewEVMBlockContext(header, cx, l2.config, nil)
229+
// TODO tracer
230+
evm := vm.NewEVM(blockContext, vm.TxContext{}, state, l2.config, vm.Config{Tracer: nil})
231+
stakingCallData, err := l2staking.PacketData(parentHeader.Coinbase)
232+
if err != nil {
233+
return err
234+
}
235+
systemAddress := vm.AccountRef(rcfg.SystemAddress)
236+
_, _, err = evm.Call(systemAddress, rcfg.L2StakingAddress, stakingCallData, params.MaxGasLimit, common.Big0)
237+
if err != nil {
238+
return err
239+
}
240+
if header.Time > rewardStartTime && (header.Time-rewardStartTime)/rewardEpoch > inflationMintedEpochs {
241+
callData, err := morphtoken.PacketData()
242+
if err != nil {
243+
return err
244+
}
245+
_, _, err = evm.Call(systemAddress, rcfg.MorphTokenAddress, callData, params.MaxGasLimit, common.Big0)
246+
if err != nil {
247+
return err
248+
}
249+
}
250+
return nil
251+
}
252+
189253
// Finalize implements consensus.Engine, setting the final state on the header
190254
func (l2 *Consensus) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
191255
// The block reward is no longer handled here. It's done by the

contracts/l2staking/l2staking.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package l2staking
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"sync"
7+
8+
"github.com/morph-l2/go-ethereum/accounts/abi"
9+
"github.com/morph-l2/go-ethereum/common"
10+
)
11+
12+
const jsonData = `[{"inputs":[{"internalType":"address","name":"sequencerAddr","type":"address"}],"name":"recordBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
13+
14+
var (
15+
l2StakingABI *abi.ABI
16+
loadOnce sync.Once
17+
loadErr error
18+
)
19+
20+
func Abi() (*abi.ABI, error) {
21+
loadOnce.Do(func() {
22+
stakingABI, err := abi.JSON(strings.NewReader(jsonData))
23+
if err != nil {
24+
loadErr = fmt.Errorf("failed to parse ABI: %w", err)
25+
return
26+
}
27+
l2StakingABI = &stakingABI
28+
})
29+
return l2StakingABI, loadErr
30+
}
31+
32+
func PacketData(addr common.Address) ([]byte, error) {
33+
a, err := Abi()
34+
if err != nil {
35+
return nil, fmt.Errorf("failed to get ABI: %w", err)
36+
}
37+
data, err := a.Pack("recordBlocks", addr)
38+
if err != nil {
39+
return nil, fmt.Errorf("failed to pack data: %w", err)
40+
}
41+
return data, nil
42+
}

contracts/l2staking/l2staking_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package l2staking
2+
3+
import (
4+
"testing"
5+
6+
"github.com/morph-l2/go-ethereum/common"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestPackData(t *testing.T) {
11+
_, err := PacketData(common.HexToAddress("0x01"))
12+
require.NoError(t, err)
13+
}

contracts/morphtoken/morph_token.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package morphtoken
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"sync"
7+
8+
"github.com/morph-l2/go-ethereum/accounts/abi"
9+
)
10+
11+
const jsonData = `[{"inputs":[],"name":"mintInflations","outputs":[],"stateMutability":"nonpayable","type":"function"}]`
12+
13+
var (
14+
morphTokenABI *abi.ABI
15+
loadOnce sync.Once
16+
loadErr error
17+
)
18+
19+
func Abi() (*abi.ABI, error) {
20+
loadOnce.Do(func() {
21+
tokenABI, err := abi.JSON(strings.NewReader(jsonData))
22+
if err != nil {
23+
loadErr = fmt.Errorf("failed to parse ABI: %w", err)
24+
return
25+
}
26+
morphTokenABI = &tokenABI
27+
})
28+
return morphTokenABI, loadErr
29+
}
30+
31+
func PacketData() ([]byte, error) {
32+
a, err := Abi()
33+
if err != nil {
34+
return nil, fmt.Errorf("failed to get ABI: %w", err)
35+
}
36+
data, err := a.Pack("mintInflations")
37+
if err != nil {
38+
return nil, fmt.Errorf("failed to pack data: %w", err)
39+
}
40+
return data, nil
41+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package morphtoken
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestPackData(t *testing.T) {
10+
_, err := PacketData()
11+
require.NoError(t, err)
12+
}

core/state_processor.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
9292
blockContext := NewEVMBlockContext(header, p.bc, p.config, nil)
9393
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
9494
processorBlockTransactionGauge.Update(int64(block.Transactions().Len()))
95+
err := p.engine.StartHook(p.bc, header, statedb)
96+
if err != nil {
97+
return nil, nil, 0, err
98+
}
9599
// Iterate over and process the individual transactions
96100
for i, tx := range block.Transactions() {
97101
msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee)

core/types/l2trace.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type BlockTrace struct {
2020
Bytecodes []*BytecodeTrace `json:"codes"`
2121
TxStorageTraces []*StorageTrace `json:"txStorageTraces,omitempty"`
2222
ExecutionResults []*ExecutionResult `json:"executionResults"`
23+
StartHookResult *ExecutionResult `json:"startHookResult,omitempty"`
2324
WithdrawTrieRoot common.Hash `json:"withdraw_trie_root,omitempty"`
2425
SequencerSetVerifyHash common.Hash `json:"sequencer_set_verify_hash,omitempty"`
2526
StartL1QueueIndex uint64 `json:"startL1QueueIndex"`

0 commit comments

Comments
 (0)