Skip to content

Commit

Permalink
Refactor system transactions (#124)
Browse files Browse the repository at this point in the history
* Refactor system transactions

* Small error fix

* Refactor damn stuff

* Ensure system transactions will be not reverted

* Remove some quotes

* Remove stale method from interface

* Remove system gas vars

* Fix system tx gas

* Remove duplicate basic gas
  • Loading branch information
devfans committed Oct 12, 2023
1 parent 354e59c commit 812a849
Show file tree
Hide file tree
Showing 28 changed files with 550 additions and 655 deletions.
10 changes: 8 additions & 2 deletions consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,14 +568,20 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header

// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given.
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction,
uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error {
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil)
return nil
}

// Filter out system transactions from common transactions
// returns common transactions, system transactions and system transaction message provider
func (c *Clique) BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions,
func(*types.Transaction, *big.Int) types.Message, error) {
return block.Transactions(), nil, nil, nil
}

// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
Expand Down
14 changes: 6 additions & 8 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,17 @@ type Engine interface {
// rules of a particular engine. The changes are executed inline.
Prepare(chain ChainHeaderReader, header *types.Header) error

// Filter out system transactions from common transactions
// returns common transactions, system transactions and system transaction message provider
BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions,
func(*types.Transaction, *big.Int) types.Message, error)

// Finalize runs any post-transaction state modifications (e.g. block rewards)
// but does not assemble the block.
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction,
uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error
Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error

// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
// rewards) and assembles the final block.
Expand Down Expand Up @@ -165,12 +169,6 @@ type HotStuff interface {

// FillHeader fulfill the header with extra which contains epoch change info
FillHeader(state *state.StateDB, header *types.Header) error

// IsSystemCall return method id and true if the tx is an system transaction
IsSystemTransaction(tx *types.Transaction, header *types.Header) (string, bool)

// HasSystemTxHook return true if systemTxHook is not nil
HasSystemTxHook() bool
}

// Handler should be implemented is the consensus needs to handle and send peer's message
Expand Down
12 changes: 9 additions & 3 deletions consensus/ethash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,20 +586,26 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H

// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state on the header
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction,
uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error {
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error {
// Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
return nil
}

// Filter out system transactions from common transactions
// returns common transactions, system transactions and system transaction message provider
func (ethash *Ethash) BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions,
func(*types.Transaction, *big.Int) types.Message, error) {
return block.Transactions(), nil, nil, nil
}

// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB,
txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) {

ethash.Finalize(chain, header, state, &txs, uncles, nil, nil, nil)
ethash.Finalize(chain, header, state, uncles)

// Header seems complete, assemble into a block and return
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), receipts, nil
Expand Down
3 changes: 0 additions & 3 deletions consensus/hotstuff/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ type backend struct {
currentBlock func() *types.Block
getBlockByHash func(hash common.Hash) *types.Block
hasBadBlock func(db ethdb.Reader, hash common.Hash) bool
systemTxHook SystemTxFn
}

func New(chainConfig *params.ChainConfig, config *hotstuff.Config, privateKey *ecdsa.PrivateKey, db ethdb.Database, mock bool) *backend {
Expand All @@ -92,10 +91,8 @@ func New(chainConfig *params.ChainConfig, config *hotstuff.Config, privateKey *e
}

if mock {
backend.systemTxHook = nil
backend.core = core.New(backend, config, signer, db, nil)
} else {
backend.systemTxHook = backend.executeSystemTxs
backend.core = core.New(backend, config, signer, db, backend.CheckPoint)
}

Expand Down
77 changes: 0 additions & 77 deletions consensus/hotstuff/backend/economic.go

This file was deleted.

110 changes: 67 additions & 43 deletions consensus/hotstuff/backend/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@ import (
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/hotstuff"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/contracts/native/governance"
"github.com/ethereum/go-ethereum/contracts/native/utils"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
Expand Down Expand Up @@ -109,15 +114,48 @@ func (s *backend) Prepare(chain consensus.ChainHeaderReader, header *types.Heade
return nil
}

func (s *backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction,
uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error {
// Filter out system transactions from common transactions
// returns common transactions, system transactions and system transaction message provider
func (s *backend) BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions,
func(*types.Transaction, *big.Int) types.Message, error) {
systemTransactions, err := governance.AssembleSystemTransactions(state, block.NumberU64())
if err != nil {
return nil, nil, nil, err
}
allTransactions := block.Transactions()
commonTransactionCount := len(allTransactions) - len(systemTransactions)
if commonTransactionCount < 0 {
return nil, nil, nil, fmt.Errorf("missing required system transactions, count %v", len(systemTransactions))
}

if s.HasSystemTxHook() {
if err := s.executeSystemTxs(chain, header, state, txs, receipts, systemTxs, usedGas, false); err != nil {
return err
signer := types.MakeSigner(s.chainConfig, block.Number())
for i, tx := range systemTransactions {
includedTx := allTransactions[commonTransactionCount + i]
if includedTx.Hash() != tx.Hash() {
return nil, nil, nil, fmt.Errorf("unexpected system tx hash detected, tx index %v, hash %s, expected: %s", commonTransactionCount + i, includedTx.Hash(), tx.Hash())
}
from, err := signer.Sender(includedTx)
if err != nil {
return nil, nil, nil, fmt.Errorf("check system tx signature failed, %w", err)
}
if from != block.Coinbase() {
return nil, nil, nil, fmt.Errorf("check system tx signature failed, wrong signer %s", from)
}
}
return allTransactions[:commonTransactionCount], systemTransactions, s.asSystemMessage, nil
}

// Change message from as valid system transaction sender
func(s *backend) asSystemMessage(tx *types.Transaction, baseFee *big.Int) types.Message {
gasPrice := new(big.Int).Set(tx.GasPrice())
if baseFee != nil {
gasPrice = math.BigMin(gasPrice.Add(tx.GasTipCap(), baseFee), tx.GasFeeCap())
}
return types.NewMessage(utils.SystemTxSender, tx.To(), tx.Nonce(), tx.Value(), tx.Gas(), gasPrice,
new(big.Int).Set(tx.GasFeeCap()), new(big.Int).Set(tx.GasTipCap()), tx.Data(), tx.AccessList(), true)
}

func (s *backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error {
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = nilUncleHash
return nil
Expand All @@ -134,53 +172,39 @@ func (s *backend) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header
receipts = make([]*types.Receipt, 0)
}

if s.HasSystemTxHook() {
if err := s.executeSystemTxs(chain, header, state, &txs, &receipts, nil, &header.GasUsed, true); err != nil {
systemTransantions, err := governance.AssembleSystemTransactions(state, header.Number.Uint64())
if err != nil {
return nil, nil, err
}

for _, tx := range systemTransantions {
chainContext := chainContext{Chain: chain, engine: s}
gp := new(core.GasPool).AddGas(header.GasLimit)
if err := gp.SubGas(header.GasUsed); err != nil {
return nil, nil, err
}
state.Prepare(tx.Hash(), common.Hash{}, len(txs))
receipt, err := core.ApplyTransactionWithCustomMessageProvider(s.asSystemMessage, s.chainConfig, chainContext, nil, gp, state, header, tx, &header.GasUsed, vm.Config{})
if err != nil {
return nil, nil, err
}
if receipt.Status != types.ReceiptStatusSuccessful {
return nil, nil, fmt.Errorf("unexpected reverted system transactions tx %d [%s], status %v", len(txs), tx.Hash(), receipt.Status)
}
signer := types.MakeSigner(s.chainConfig, header.Number)
tx, err = s.signer.SignTx(tx, signer)
if err != nil {
return nil, nil, err
}
txs = append(txs, tx)
receipts = append(receipts, receipt)
}

// Assemble and return the final block for sealing
block := packBlock(state, chain, header, txs, receipts)
return block, receipts, nil
}

type SystemTxFn func(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64, mining bool) error

func (s *backend) HasSystemTxHook() bool {
return s.systemTxHook != nil
}

// executeSystemTxs governance tx execution do not allow failure, the consensus will halt if tx failed and return error.
func (s *backend) executeSystemTxs(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB,
txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64, mining bool) error {

// genesis block DONT need to execute system transaction
if header.Number.Uint64() == 0 {
return nil
}

if err := s.reward(state, header.Number); err != nil {
return err
}

ctx := &systemTxContext{
chain: chain,
state: state,
header: header,
chainCtx: chainContext{Chain: chain, engine: s},
txs: txs,
sysTxs: systemTxs,
receipts: receipts,
usedGas: usedGas,
mining: mining,
}
if err := s.execEndBlock(ctx); err != nil {
return err
}
return s.execEpochChange(state, header, ctx)
}

func (s *backend) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) (err error) {
// update the block header timestamp and signature and propose the block to core engine
header := block.Header()
Expand Down
Loading

0 comments on commit 812a849

Please sign in to comment.