Skip to content

Commit

Permalink
Implement PIP-12, Time Based StateSync Confirmations Delay (ethereum#864
Browse files Browse the repository at this point in the history
)

* updated Indore HF related changes by adding stateSyncConfirmationDelay

* converted StateSyncConfirmationDelay to map[string]uint64 and cleanup

* calculate last state ID from incoming state object with eth call (ethereum#883)

* removed IndoreBlock from configs

* fix

* remove code duplication and refactor

---------

Co-authored-by: Manav Darji <manavdarji.india@gmail.com>
  • Loading branch information
pratikspatil024 and manav2401 authored Jun 6, 2023
1 parent 3d30c34 commit 47f8e86
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 61 deletions.
2 changes: 2 additions & 0 deletions consensus/bor/api/caller.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"context"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
)

//go:generate mockgen -destination=./caller_mock.go -package=api . Caller
type Caller interface {
Call(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *ethapi.StateOverride) (hexutil.Bytes, error)
CallWithState(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, state *state.StateDB, overrides *ethapi.StateOverride) (hexutil.Bytes, error)
}
16 changes: 16 additions & 0 deletions consensus/bor/api/caller_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 29 additions & 9 deletions consensus/bor/bor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1184,22 +1184,42 @@ func (c *Bor) CommitStates(
fetchStart := time.Now()
number := header.Number.Uint64()

_lastStateID, err := c.GenesisContractsClient.LastStateId(number - 1)
if err != nil {
return nil, err
var (
lastStateIDBig *big.Int
from uint64
to time.Time
err error
)

if c.config.IsIndore(header.Number) {
// Fetch the LastStateId from contract via current state instance
lastStateIDBig, err = c.GenesisContractsClient.LastStateId(state.Copy(), number-1, header.ParentHash)
if err != nil {
return nil, err
}

stateSyncDelay := c.config.CalculateStateSyncDelay(number)
to = time.Unix(int64(header.Time-stateSyncDelay), 0)
} else {
lastStateIDBig, err = c.GenesisContractsClient.LastStateId(nil, number-1, header.ParentHash)
if err != nil {
return nil, err
}

to = time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.CalculateSprint(number)).Time), 0)
}

to := time.Unix(int64(chain.Chain.GetHeaderByNumber(number-c.config.CalculateSprint(number)).Time), 0)
lastStateID := _lastStateID.Uint64()
lastStateID := lastStateIDBig.Uint64()
from = lastStateID + 1

log.Info(
"Fetching state updates from Heimdall",
"fromID", lastStateID+1,
"fromID", from,
"to", to.Format(time.RFC3339))

eventRecords, err := c.HeimdallClient.StateSyncEvents(ctx, lastStateID+1, to.Unix())
eventRecords, err := c.HeimdallClient.StateSyncEvents(ctx, from, to.Unix())
if err != nil {
log.Error("Error occurred when fetching state sync events", "stateID", lastStateID+1, "error", err)
log.Error("Error occurred when fetching state sync events", "fromID", from, "to", to.Unix(), "err", err)
}

if c.config.OverrideStateSyncRecords != nil {
Expand All @@ -1222,7 +1242,7 @@ func (c *Bor) CommitStates(
}

if err = validateEventRecord(eventRecord, number, to, lastStateID, chainID); err != nil {
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID, "error", err.Error())
log.Error("while validating event record", "block", number, "to", to, "stateID", lastStateID+1, "error", err.Error())
break
}

Expand Down
10 changes: 6 additions & 4 deletions consensus/bor/contract/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ func (gc *GenesisContractsClient) CommitState(
return gasUsed, nil
}

func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int, error) {
blockNr := rpc.BlockNumber(snapshotNumber)
func (gc *GenesisContractsClient) LastStateId(state *state.StateDB, number uint64, hash common.Hash) (*big.Int, error) {
blockNr := rpc.BlockNumber(number)

const method = "lastStateId"

Expand All @@ -116,11 +116,13 @@ func (gc *GenesisContractsClient) LastStateId(snapshotNumber uint64) (*big.Int,
toAddress := common.HexToAddress(gc.StateReceiverContract)
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))

result, err := gc.ethAPI.Call(context.Background(), ethapi.TransactionArgs{
// Do a call with state so that we can fetch the last state ID from a given (incoming)
// state instead of local(canonical) chain.
result, err := gc.ethAPI.CallWithState(context.Background(), ethapi.TransactionArgs{
Gas: &gas,
To: &toAddress,
Data: &msgData,
}, rpc.BlockNumberOrHash{BlockNumber: &blockNr}, nil)
}, rpc.BlockNumberOrHash{BlockNumber: &blockNr, BlockHash: &hash}, state, nil)
if err != nil {
return nil, err
}
Expand Down
3 changes: 2 additions & 1 deletion consensus/bor/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bor
import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/bor/clerk"
"github.com/ethereum/go-ethereum/consensus/bor/statefull"
"github.com/ethereum/go-ethereum/core/state"
Expand All @@ -12,5 +13,5 @@ import (
//go:generate mockgen -destination=./genesis_contract_mock.go -package=bor . GenesisContract
type GenesisContract interface {
CommitState(event *clerk.EventRecordWithTime, state *state.StateDB, header *types.Header, chCtx statefull.ChainContext) (uint64, error)
LastStateId(snapshotNumber uint64) (*big.Int, error)
LastStateId(state *state.StateDB, number uint64, hash common.Hash) (*big.Int, error)
}
9 changes: 5 additions & 4 deletions consensus/bor/genesis_contract_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 17 additions & 17 deletions consensus/bor/span_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions graphql/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,8 @@ func (b *Block) Call(ctx context.Context, args struct {
return nil, err
}
}
result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, b.backend.RPCEVMTimeout(), b.backend.RPCGasCap())

result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, nil, b.backend.RPCEVMTimeout(), b.backend.RPCGasCap())
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1076,7 +1077,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
Data ethapi.TransactionArgs
}) (*CallResult, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, p.backend.RPCEVMTimeout(), p.backend.RPCGasCap())
result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, nil, p.backend.RPCEVMTimeout(), p.backend.RPCGasCap())
if err != nil {
return nil, err
}
Expand Down
50 changes: 44 additions & 6 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -987,16 +987,40 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
return nil
}

func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
func DoCall(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, state *state.StateDB, overrides *StateOverride, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())

state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return nil, err
var (
header *types.Header
err error
)

// Fetch the state and header from blockNumberOrHash if it's coming from normal eth_call path.
if state == nil {
state, header, err = b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil {
return nil, err
}
} else {
// Fetch the header from the given blockNumberOrHash. Note that this path is only taken
// when we're doing a call from bor consensus to fetch data from genesis contracts. It's
// necessary to fetch header using header hash as we might be experiencing a reorg and there
// can be multiple headers with same number.
header, err = b.HeaderByHash(ctx, *blockNrOrHash.BlockHash)
if header == nil || err != nil {
log.Warn("Error fetching header on CallWithState", "err", err)
return nil, err
}
}

if err := overrides.Apply(state); err != nil {
return nil, err
}

return doCallWithState(ctx, b, args, header, state, timeout, globalGasCap)
}

func doCallWithState(ctx context.Context, b Backend, args TransactionArgs, header *types.Header, state *state.StateDB, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
// Setup context so it may be cancelled the call has completed
// or, in case of unmetered gas, setup a context with a timeout.
var cancel context.CancelFunc
Expand Down Expand Up @@ -1080,7 +1104,20 @@ func (e *revertError) ErrorData() interface{} {
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
return s.CallWithState(ctx, args, blockNrOrHash, nil, overrides)
}

// CallWithState executes the given transaction on the given state for
// the given block number. Note that as it does an EVM call, fields in
// the underlying state will change. Make sure to handle it outside of
// this function (ideally by sending a copy of state).
//
// Additionally, the caller can specify a batch of contract for fields overriding.
//
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
func (s *PublicBlockChainAPI) CallWithState(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, state *state.StateDB, overrides *StateOverride) (hexutil.Bytes, error) {
result, err := DoCall(ctx, s.b, args, blockNrOrHash, state, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
if err != nil {
return nil, err
}
Expand All @@ -1093,6 +1130,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, bl
if len(result.Revert()) > 0 {
return nil, newRevertError(result)
}

return result.Return(), result.Err
}

Expand Down Expand Up @@ -1170,7 +1208,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
args.Gas = (*hexutil.Uint64)(&gas)

result, err := DoCall(ctx, b, args, blockNrOrHash, nil, 0, gasCap)
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, nil, 0, gasCap)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
Expand Down
Loading

0 comments on commit 47f8e86

Please sign in to comment.