2015-07-07 00:54:22 +00:00
// Copyright 2014 The go-ethereum Authors
2015-07-22 16:48:40 +00:00
// This file is part of the go-ethereum library.
2015-07-07 00:54:22 +00:00
//
2015-07-23 16:35:11 +00:00
// The go-ethereum library is free software: you can redistribute it and/or modify
2015-07-07 00:54:22 +00:00
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
2015-07-22 16:48:40 +00:00
// The go-ethereum library is distributed in the hope that it will be useful,
2015-07-07 00:54:22 +00:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2015-07-22 16:48:40 +00:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2015-07-07 00:54:22 +00:00
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
2015-07-22 16:48:40 +00:00
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
2015-07-07 00:54:22 +00:00
2015-07-07 03:08:16 +00:00
// Package core implements the Ethereum consensus protocol.
2014-12-04 09:28:02 +00:00
package core
2014-02-14 22:56:09 +00:00
import (
2021-03-27 21:43:38 +00:00
"encoding/json"
2014-09-24 09:39:17 +00:00
"fmt"
2021-03-27 21:43:38 +00:00
"os"
2015-04-04 14:35:23 +00:00
"time"
2014-07-29 22:31:15 +00:00
2021-07-29 10:27:46 +00:00
metrics2 "github.com/VictoriaMetrics/metrics"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/mclock"
2021-06-25 18:13:40 +00:00
"github.com/ledgerwatch/erigon/common/u256"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/consensus/misc"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
2021-07-12 15:27:25 +00:00
"github.com/ledgerwatch/erigon/core/types/accounts"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/core/vm"
"github.com/ledgerwatch/erigon/params"
2021-07-29 10:23:23 +00:00
"github.com/ledgerwatch/log/v3"
2014-02-14 22:56:09 +00:00
)
2015-03-03 08:44:41 +00:00
var (
2021-07-29 10:27:46 +00:00
blockExecutionTimer = metrics2 . GetOrCreateSummary ( "chain_execution_seconds" )
blockReorgInvalidatedTx = metrics2 . GetOrCreateCounter ( "chain_reorg_invalidTx" )
2015-03-03 08:44:41 +00:00
)
2014-06-23 11:54:10 +00:00
2015-04-20 18:37:40 +00:00
const (
2021-05-24 13:43:16 +00:00
TriesInMemory = 128
2015-04-20 18:37:40 +00:00
)
2015-01-02 11:07:54 +00:00
2019-05-27 13:51:49 +00:00
// statsReportLimit is the time limit during import and export after which we
// always print out progress. This avoids the user wondering what's going on.
const statsReportLimit = 8 * time . Second
// report prints statistics if some number of blocks have been processed
// or more than a few seconds have passed since the last message.
2020-10-21 17:01:40 +00:00
func ( st * InsertStats ) Report ( logPrefix string , chain [ ] * types . Block , index int , toCommit bool ) {
2019-05-27 13:51:49 +00:00
// Fetch the timings for the batch
2016-10-18 08:18:07 +00:00
var (
2019-05-27 13:51:49 +00:00
now = mclock . Now ( )
2020-06-29 16:26:33 +00:00
elapsed = time . Duration ( now ) - time . Duration ( st . StartTime )
2016-10-18 08:18:07 +00:00
)
2019-05-27 13:51:49 +00:00
// If we're at the last block of the batch or report period reached, log
2020-03-01 09:00:14 +00:00
if index == len ( chain ) - 1 || elapsed >= statsReportLimit || toCommit {
2019-05-27 13:51:49 +00:00
// Count the number of transactions in this segment
var txs int
for _ , block := range chain [ st . lastIndex : index + 1 ] {
txs += len ( block . Transactions ( ) )
2018-11-20 12:15:26 +00:00
}
2019-05-27 13:51:49 +00:00
end := chain [ index ]
context := [ ] interface { } {
2020-07-15 06:15:48 +00:00
"blocks" , st . Processed , "txs" , txs ,
"elapsed" , common . PrettyDuration ( elapsed ) ,
2020-07-19 08:11:53 +00:00
"number" , end . Number ( ) , "hash" , end . Hash ( ) ,
2019-05-27 13:51:49 +00:00
}
if timestamp := time . Unix ( int64 ( end . Time ( ) ) , 0 ) ; time . Since ( timestamp ) > time . Minute {
context = append ( context , [ ] interface { } { "age" , common . PrettyAge ( timestamp ) } ... )
}
if st . queued > 0 {
context = append ( context , [ ] interface { } { "queued" , st . queued } ... )
}
if st . ignored > 0 {
context = append ( context , [ ] interface { } { "ignored" , st . ignored } ... )
}
2020-10-21 17:01:40 +00:00
log . Info ( fmt . Sprintf ( "[%s] Imported new chain segment" , logPrefix ) , context ... )
2020-06-29 16:26:33 +00:00
* st = InsertStats { StartTime : now , lastIndex : index + 1 }
2016-10-07 12:25:01 +00:00
}
}
2020-06-09 13:11:09 +00:00
// ExecuteBlockEphemerally runs a block from provided stateReader and
2020-04-26 16:02:38 +00:00
// writes the result to the provided stateWriter
2020-06-09 13:11:09 +00:00
func ExecuteBlockEphemerally (
2020-05-03 12:39:50 +00:00
chainConfig * params . ChainConfig ,
2020-05-07 05:59:00 +00:00
vmConfig * vm . Config ,
2021-04-06 09:52:53 +00:00
getHeader func ( hash common . Hash , number uint64 ) * types . Header ,
2020-05-03 12:39:50 +00:00
engine consensus . Engine ,
block * types . Block ,
stateReader state . StateReader ,
2020-05-15 07:52:45 +00:00
stateWriter state . WriterWithChangeSets ,
2021-07-12 15:27:25 +00:00
epochReader consensus . EpochReader ,
2021-07-21 11:13:26 +00:00
chainReader consensus . ChainHeaderReader ,
2021-08-10 02:48:56 +00:00
contractHasTEVM func ( codeHash common . Hash ) ( bool , error ) ,
2020-06-09 13:11:09 +00:00
) ( types . Receipts , error ) {
2021-07-29 10:27:46 +00:00
defer blockExecutionTimer . UpdateDuration ( time . Now ( ) )
2021-03-23 09:00:07 +00:00
block . Uncles ( )
2020-04-26 16:02:38 +00:00
ibs := state . New ( stateReader )
header := block . Header ( )
var receipts types . Receipts
usedGas := new ( uint64 )
2021-04-28 14:32:48 +00:00
gp := new ( GasPool )
2021-05-11 09:54:20 +00:00
gp . AddGas ( block . GasLimit ( ) )
2020-04-26 16:02:38 +00:00
2021-07-08 12:40:43 +00:00
if ! vmConfig . ReadOnly {
2021-07-21 11:13:26 +00:00
if err := InitializeBlockExecution ( engine , chainReader , epochReader , block . Header ( ) , block . Transactions ( ) , block . Uncles ( ) , chainConfig , ibs ) ; err != nil {
2021-07-08 12:40:43 +00:00
return nil , err
}
}
2020-04-26 16:02:38 +00:00
if chainConfig . DAOForkSupport && chainConfig . DAOForkBlock != nil && chainConfig . DAOForkBlock . Cmp ( block . Number ( ) ) == 0 {
misc . ApplyDAOHardFork ( ibs )
}
2020-04-26 21:58:26 +00:00
noop := state . NewNoopWriter ( )
2021-07-12 15:27:25 +00:00
//fmt.Printf("====txs processing start: %d====\n", block.NumberU64())
2020-04-27 06:42:21 +00:00
for i , tx := range block . Transactions ( ) {
2021-05-04 06:23:54 +00:00
ibs . Prepare ( tx . Hash ( ) , block . Hash ( ) , i )
2021-03-27 21:43:38 +00:00
writeTrace := false
if vmConfig . Debug && vmConfig . Tracer == nil {
vmConfig . Tracer = vm . NewStructLogger ( & vm . LogConfig { } )
writeTrace = true
}
2021-03-28 14:40:27 +00:00
2021-08-10 02:48:56 +00:00
receipt , _ , err := ApplyTransaction ( chainConfig , getHeader , engine , nil , gp , ibs , noop , header , tx , usedGas , * vmConfig , contractHasTEVM )
2021-03-27 21:43:38 +00:00
if writeTrace {
w , err1 := os . Create ( fmt . Sprintf ( "txtrace_%x.txt" , tx . Hash ( ) ) )
if err1 != nil {
panic ( err1 )
}
encoder := json . NewEncoder ( w )
logs := FormatLogs ( vmConfig . Tracer . ( * vm . StructLogger ) . StructLogs ( ) )
if err2 := encoder . Encode ( logs ) ; err2 != nil {
panic ( err2 )
}
if err2 := w . Close ( ) ; err2 != nil {
panic ( err2 )
}
vmConfig . Tracer = nil
}
2020-04-26 16:02:38 +00:00
if err != nil {
2021-04-28 14:32:48 +00:00
return nil , fmt . Errorf ( "could not apply tx %d from block %d [%v]: %w" , i , block . NumberU64 ( ) , tx . Hash ( ) . Hex ( ) , err )
2020-04-26 16:02:38 +00:00
}
2020-10-12 08:39:04 +00:00
if ! vmConfig . NoReceipts {
receipts = append ( receipts , receipt )
}
2020-04-26 16:02:38 +00:00
}
2021-04-22 17:11:37 +00:00
if chainConfig . IsByzantium ( header . Number . Uint64 ( ) ) && ! vmConfig . NoReceipts {
2020-04-26 16:02:38 +00:00
receiptSha := types . DeriveSha ( receipts )
if receiptSha != block . Header ( ) . ReceiptHash {
2020-06-09 13:11:09 +00:00
return nil , fmt . Errorf ( "mismatched receipt headers for block %d" , block . NumberU64 ( ) )
2020-04-26 16:02:38 +00:00
}
}
2020-12-08 09:44:29 +00:00
if * usedGas != header . GasUsed {
return nil , fmt . Errorf ( "gas used by execution: %d, in header: %d" , * usedGas , header . GasUsed )
2020-07-01 07:23:15 +00:00
}
2020-12-08 09:44:29 +00:00
if ! vmConfig . NoReceipts {
bloom := types . CreateBloom ( receipts )
if bloom != header . Bloom {
return nil , fmt . Errorf ( "bloom computed by execution: %x, in header: %x" , bloom , header . Bloom )
2020-07-01 07:23:15 +00:00
}
2020-07-02 18:44:08 +00:00
}
2021-06-14 18:51:57 +00:00
if ! vmConfig . ReadOnly {
2021-07-24 09:50:42 +00:00
if err := FinalizeBlockExecution ( engine , stateReader , block . Header ( ) , block . Transactions ( ) , block . Uncles ( ) , stateWriter , chainConfig , ibs , receipts , epochReader , chainReader ) ; err != nil {
2021-06-14 18:51:57 +00:00
return nil , err
}
}
2020-12-08 09:44:29 +00:00
return receipts , nil
2020-07-01 07:23:15 +00:00
}
2021-03-23 09:00:07 +00:00
2021-07-08 12:40:43 +00:00
func SysCallContract ( contract common . Address , data [ ] byte , chainConfig params . ChainConfig , ibs * state . IntraBlockState , header * types . Header , engine consensus . Engine ) ( result [ ] byte , err error ) {
2021-07-24 09:50:42 +00:00
gp := new ( GasPool ) . AddGas ( 50_000_000 )
2021-07-08 12:40:43 +00:00
if chainConfig . DAOForkSupport && chainConfig . DAOForkBlock != nil && chainConfig . DAOForkBlock . Cmp ( header . Number ) == 0 {
misc . ApplyDAOHardFork ( ibs )
}
2021-07-24 09:50:42 +00:00
msg := types . NewMessage (
state . SystemAddress ,
& contract ,
0 , u256 . Num0 ,
50_000_000 , u256 . Num0 ,
nil , nil ,
data , nil , false ,
)
vmConfig := vm . Config { NoReceipts : true }
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext ( header , nil , engine , & state . SystemAddress , nil )
evm := vm . NewEVM ( blockContext , NewEVMTxContext ( msg ) , ibs , & chainConfig , vmConfig )
res , err := ApplyMessage ( evm , msg , gp , true /* refunds */ , false /* gasBailout */ )
2021-07-08 12:40:43 +00:00
if err != nil {
2021-07-24 09:50:42 +00:00
return nil , err
2021-07-08 12:40:43 +00:00
}
2021-07-24 09:50:42 +00:00
return res . ReturnData , nil
2021-07-08 12:40:43 +00:00
}
// from the null sender, with 50M gas.
2021-07-24 04:04:07 +00:00
func SysCallContractTx ( contract common . Address , data [ ] byte ) ( tx types . Transaction , err error ) {
2021-07-08 12:40:43 +00:00
//nonce := ibs.GetNonce(SystemAddress)
tx = types . NewTransaction ( 0 , contract , u256 . Num0 , 50_000_000 , u256 . Num0 , data )
2021-07-24 09:50:42 +00:00
return tx . FakeSign ( state . SystemAddress )
2021-07-08 12:40:43 +00:00
}
2021-06-25 18:13:40 +00:00
func CallContract ( contract common . Address , data [ ] byte , chainConfig params . ChainConfig , ibs * state . IntraBlockState , header * types . Header , engine consensus . Engine ) ( result [ ] byte , err error ) {
gp := new ( GasPool )
gp . AddGas ( 50_000_000 )
var gasUsed uint64
if chainConfig . DAOForkSupport && chainConfig . DAOForkBlock != nil && chainConfig . DAOForkBlock . Cmp ( header . Number ) == 0 {
misc . ApplyDAOHardFork ( ibs )
}
noop := state . NewNoopWriter ( )
tx , err := CallContractTx ( contract , data , ibs )
if err != nil {
2021-07-08 12:40:43 +00:00
return nil , fmt . Errorf ( "SysCallContract: %w " , err )
2021-06-25 18:13:40 +00:00
}
2021-07-24 09:50:42 +00:00
vmConfig := vm . Config { NoReceipts : true }
_ , result , err = ApplyTransaction ( & chainConfig , nil , engine , & state . SystemAddress , gp , ibs , noop , header , tx , & gasUsed , vmConfig , nil )
2021-06-25 18:13:40 +00:00
if err != nil {
2021-07-08 12:40:43 +00:00
return result , fmt . Errorf ( "SysCallContract: %w " , err )
2021-06-25 18:13:40 +00:00
}
return result , nil
}
// from the null sender, with 50M gas.
func CallContractTx ( contract common . Address , data [ ] byte , ibs * state . IntraBlockState ) ( tx types . Transaction , err error ) {
2021-07-08 12:40:43 +00:00
from := common . Address { }
2021-06-25 18:13:40 +00:00
nonce := ibs . GetNonce ( from )
2021-07-08 12:40:43 +00:00
tx = types . NewTransaction ( nonce , contract , u256 . Num0 , 50_000_000 , u256 . Num0 , data )
2021-06-25 18:13:40 +00:00
return tx . FakeSign ( from )
}
2021-07-24 09:50:42 +00:00
func FinalizeBlockExecution ( engine consensus . Engine , stateReader state . StateReader , header * types . Header , txs types . Transactions , uncles [ ] * types . Header , stateWriter state . WriterWithChangeSets , cc * params . ChainConfig , ibs * state . IntraBlockState , receipts types . Receipts , e consensus . EpochReader , headerReader consensus . ChainHeaderReader ) error {
2021-07-12 15:27:25 +00:00
//ibs.Print(cc.Rules(header.Number.Uint64()))
//fmt.Printf("====tx processing end====\n")
2021-07-21 11:13:26 +00:00
if err := engine . Finalize ( cc , header , ibs , txs , uncles , receipts , e , headerReader , func ( contract common . Address , data [ ] byte ) ( [ ] byte , error ) {
return SysCallContract ( contract , data , * cc , ibs , header , engine )
} ) ; err != nil {
return err
}
2021-07-24 09:50:42 +00:00
//fmt.Printf("====finalize start %d====\n", header.Number.Uint64())
2021-07-12 15:27:25 +00:00
//ibs.Print(cc.Rules(header.Number.Uint64()))
//fmt.Printf("====finalize end====\n")
2021-03-23 09:00:07 +00:00
2021-07-24 09:50:42 +00:00
var originalSystemAcc * accounts . Account
if cc . ChainID . Uint64 ( ) == 77 { // hack for Sokol - don't understand why eip158 is enabled, but OE still save SystemAddress with nonce=0
n := ibs . GetNonce ( state . SystemAddress ) //hack - because syscall must use ApplyMessage instead of ApplyTx (and don't create tx at all). But CallContract must create tx.
if n > 0 {
var err error
originalSystemAcc , err = stateReader . ReadAccountData ( state . SystemAddress )
if err != nil {
return err
}
}
}
2021-07-05 18:52:50 +00:00
if err := ibs . CommitBlock ( cc . Rules ( header . Number . Uint64 ( ) ) , stateWriter ) ; err != nil {
2021-03-23 09:00:07 +00:00
return fmt . Errorf ( "committing block %d failed: %v" , header . Number . Uint64 ( ) , err )
}
2021-07-24 09:50:42 +00:00
if originalSystemAcc != nil { // hack for Sokol - don't understand why eip158 is enabled, but OE still save SystemAddress with nonce=0
2021-07-12 15:27:25 +00:00
acc := accounts . NewAccount ( )
acc . Nonce = 0
2021-07-24 09:50:42 +00:00
if err := stateWriter . UpdateAccountData ( state . SystemAddress , originalSystemAcc , & acc ) ; err != nil {
return err
}
2021-07-12 15:27:25 +00:00
}
2021-03-23 09:00:07 +00:00
if err := stateWriter . WriteChangeSets ( ) ; err != nil {
return fmt . Errorf ( "writing changesets for block %d failed: %v" , header . Number . Uint64 ( ) , err )
}
return nil
}
2021-07-08 12:40:43 +00:00
2021-07-21 11:13:26 +00:00
func InitializeBlockExecution ( engine consensus . Engine , chain consensus . ChainHeaderReader , epochReader consensus . EpochReader , header * types . Header , txs types . Transactions , uncles [ ] * types . Header , cc * params . ChainConfig , ibs * state . IntraBlockState ) error {
2021-07-08 12:40:43 +00:00
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
2021-07-21 11:13:26 +00:00
engine . Initialize ( cc , chain , epochReader , header , txs , uncles , func ( contract common . Address , data [ ] byte ) ( [ ] byte , error ) {
2021-07-08 12:40:43 +00:00
return SysCallContract ( contract , data , * cc , ibs , header , engine )
} )
2021-07-12 15:27:25 +00:00
//fmt.Printf("====InitializeBlockExecution start %d====\n", header.Number.Uint64())
//ibs.Print(cc.Rules(header.Number.Uint64()))
//fmt.Printf("====InitializeBlockExecution end====\n")
2021-07-08 12:40:43 +00:00
return nil
}