mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
Move predicate header parsing
See https://github.com/ava-labs/coreth/pull/824
This commit is contained in:
committed by
Quentin Mc Gaw
parent
b05cd69548
commit
a414698c8f
+21
-39
@@ -35,7 +35,6 @@ import (
|
||||
"github.com/ava-labs/subnet-evm/core/vm"
|
||||
"github.com/ava-labs/subnet-evm/predicate"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
@@ -51,33 +50,6 @@ type ChainContext interface {
|
||||
|
||||
// NewEVMBlockContext creates a new context for use in the EVM.
|
||||
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address) vm.BlockContext {
|
||||
predicateBytes, ok := predicate.GetPredicateResultBytes(header.Extra)
|
||||
if !ok {
|
||||
return newEVMBlockContext(header, chain, author, nil)
|
||||
}
|
||||
// Prior to Durango, the VM enforces the extra data is smaller than or
|
||||
// equal to this size. After Durango, the VM pre-verifies the extra
|
||||
// data past the dynamic fee rollup window is valid.
|
||||
predicateResults, err := predicate.ParseResults(predicateBytes)
|
||||
if err != nil {
|
||||
log.Error("failed to parse predicate results creating new block context", "err", err, "extra", header.Extra)
|
||||
// As mentioned above, we pre-verify the extra data to ensure this never happens.
|
||||
// If we hit an error, construct a new block context rather than use a potentially half initialized value
|
||||
// as defense in depth.
|
||||
return newEVMBlockContext(header, chain, author, nil)
|
||||
}
|
||||
return newEVMBlockContext(header, chain, author, predicateResults)
|
||||
}
|
||||
|
||||
// NewEVMBlockContextWithPredicateResults creates a new context for use in the EVM with an override for the predicate results that is not present
|
||||
// in header.Extra.
|
||||
// This function is used to create a BlockContext when the header Extra data is not fully formed yet and it's more efficient to pass in predicateResults
|
||||
// directly rather than re-encode the latest results when executing each individaul transaction.
|
||||
func NewEVMBlockContextWithPredicateResults(header *types.Header, chain ChainContext, author *common.Address, predicateResults *predicate.Results) vm.BlockContext {
|
||||
return newEVMBlockContext(header, chain, author, predicateResults)
|
||||
}
|
||||
|
||||
func newEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, predicateResults *predicate.Results) vm.BlockContext {
|
||||
var (
|
||||
beneficiary common.Address
|
||||
baseFee *big.Int
|
||||
@@ -97,20 +69,30 @@ func newEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
||||
blobBaseFee = eip4844.CalcBlobFee(*header.ExcessBlobGas)
|
||||
}
|
||||
return vm.BlockContext{
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
GetHash: GetHashFn(header, chain),
|
||||
PredicateResults: predicateResults,
|
||||
Coinbase: beneficiary,
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: header.Time,
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
BaseFee: baseFee,
|
||||
BlobBaseFee: blobBaseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
GetHash: GetHashFn(header, chain),
|
||||
Extra: header.Extra,
|
||||
Coinbase: beneficiary,
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: header.Time,
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
BaseFee: baseFee,
|
||||
BlobBaseFee: blobBaseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEVMBlockContextWithPredicateResults creates a new context for use in the EVM with an override for the predicate results that is not present
|
||||
// in header.Extra.
|
||||
// This function is used to create a BlockContext when the header Extra data is not fully formed yet and it's more efficient to pass in predicateResults
|
||||
// directly rather than re-encode the latest results when executing each individaul transaction.
|
||||
func NewEVMBlockContextWithPredicateResults(header *types.Header, chain ChainContext, author *common.Address, predicateResults *predicate.Results) vm.BlockContext {
|
||||
blockContext := NewEVMBlockContext(header, chain, author)
|
||||
blockContext.PredicateResults = predicateResults
|
||||
return blockContext
|
||||
}
|
||||
|
||||
// NewEVMTxContext creates a new transaction context for a single transaction.
|
||||
func NewEVMTxContext(msg *Message) vm.TxContext {
|
||||
ctx := vm.TxContext{
|
||||
|
||||
+35
-2
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/ava-labs/subnet-evm/constants"
|
||||
"github.com/ava-labs/subnet-evm/core/types"
|
||||
"github.com/ava-labs/subnet-evm/params"
|
||||
"github.com/ava-labs/subnet-evm/plugin/evm/header"
|
||||
"github.com/ava-labs/subnet-evm/precompile/contract"
|
||||
"github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist"
|
||||
"github.com/ava-labs/subnet-evm/precompile/modules"
|
||||
@@ -43,6 +44,7 @@ import (
|
||||
"github.com/ava-labs/subnet-evm/vmerrs"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
@@ -111,9 +113,14 @@ type BlockContext struct {
|
||||
Transfer TransferFunc
|
||||
// GetHash returns the hash corresponding to n
|
||||
GetHash GetHashFunc
|
||||
// PredicateResults are the results of predicate verification available throughout the EVM's execution.
|
||||
// PredicateResults may be nil if it is not encoded in the block's header.
|
||||
// PredicateResults are the results of predicate verification available
|
||||
// throughout the EVM's execution.
|
||||
//
|
||||
// PredicateResults may be nil if it is not encoded in the extra field of
|
||||
// the block's header or if the extra field has not been parsed yet.
|
||||
PredicateResults *predicate.Results
|
||||
// Extra is the extra field from the block header.
|
||||
Extra []byte
|
||||
|
||||
// Block information
|
||||
Coinbase common.Address // Provides information for COINBASE
|
||||
@@ -209,6 +216,32 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Time),
|
||||
}
|
||||
evm.interpreter = NewEVMInterpreter(evm)
|
||||
|
||||
// If the predicate results were set by the miner, use them.
|
||||
if blockCtx.PredicateResults != nil {
|
||||
return evm
|
||||
}
|
||||
|
||||
// Parse the predicate results from the extra field and store them in the
|
||||
// block context.
|
||||
predicateBytes := header.PredicateBytesFromExtra(blockCtx.Extra)
|
||||
if len(predicateBytes) == 0 {
|
||||
return evm
|
||||
}
|
||||
|
||||
// The VM has already verified the correctness of the results during header
|
||||
// validation.
|
||||
results, err := predicate.ParseResults(predicateBytes)
|
||||
if err != nil {
|
||||
log.Error("Unexpected error parsing predicate results",
|
||||
"err", err,
|
||||
)
|
||||
return evm
|
||||
}
|
||||
|
||||
// Because the BlockContext is pass-by-value, this does not cache the
|
||||
// results for future calls to NewEVM.
|
||||
evm.Context.PredicateResults = results
|
||||
return evm
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/ava-labs/subnet-evm/core/rawdb"
|
||||
"github.com/ava-labs/subnet-evm/core/types"
|
||||
"github.com/ava-labs/subnet-evm/params"
|
||||
"github.com/ava-labs/subnet-evm/plugin/evm/header"
|
||||
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"
|
||||
"github.com/ava-labs/subnet-evm/predicate"
|
||||
|
||||
@@ -242,10 +243,7 @@ func (b *Block) verifyPredicates(predicateContext *precompileconfig.PredicateCon
|
||||
return fmt.Errorf("failed to marshal predicate results: %w", err)
|
||||
}
|
||||
extraData := b.ethBlock.Extra()
|
||||
headerPredicateResultsBytes, ok := predicate.GetPredicateResultBytes(extraData)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to find predicate results in extra data: %x", extraData)
|
||||
}
|
||||
headerPredicateResultsBytes := header.PredicateBytesFromExtra(extraData)
|
||||
if !bytes.Equal(headerPredicateResultsBytes, predicateResultsBytes) {
|
||||
return fmt.Errorf("%w (remote: %x local: %x)", errInvalidHeaderPredicateResults, headerPredicateResultsBytes, predicateResultsBytes)
|
||||
}
|
||||
|
||||
@@ -68,3 +68,16 @@ func VerifyExtra(rules params.AvalancheRules, extra []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PredicateBytesFromExtra returns the predicate result bytes from the header's
|
||||
// extra data. If the extra data is not long enough, an empty slice is returned.
|
||||
func PredicateBytesFromExtra(extra []byte) []byte {
|
||||
// Prior to Durango, the VM enforces the extra data is smaller than or equal
|
||||
// to this size.
|
||||
// After Durango, the VM pre-verifies the extra data past the dynamic fee
|
||||
// rollup window is valid.
|
||||
if len(extra) <= FeeWindowSize {
|
||||
return nil
|
||||
}
|
||||
return extra[FeeWindowSize:]
|
||||
}
|
||||
|
||||
@@ -183,3 +183,40 @@ func TestVerifyExtra(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPredicateBytesFromExtra(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
extra []byte
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
name: "empty_extra",
|
||||
extra: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "too_short",
|
||||
extra: make([]byte, FeeWindowSize-1),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "empty_predicate",
|
||||
extra: make([]byte, FeeWindowSize),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "non_empty_predicate",
|
||||
extra: []byte{
|
||||
FeeWindowSize: 5,
|
||||
},
|
||||
expected: []byte{5},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := PredicateBytesFromExtra(test.extra)
|
||||
require.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/ava-labs/subnet-evm/core/types"
|
||||
"github.com/ava-labs/subnet-evm/eth/tracers"
|
||||
"github.com/ava-labs/subnet-evm/params"
|
||||
customheader "github.com/ava-labs/subnet-evm/plugin/evm/header"
|
||||
"github.com/ava-labs/subnet-evm/plugin/evm/message"
|
||||
"github.com/ava-labs/subnet-evm/precompile/contract"
|
||||
warpcontract "github.com/ava-labs/subnet-evm/precompile/contracts/warp"
|
||||
@@ -668,8 +669,7 @@ func testReceiveWarpMessage(
|
||||
|
||||
// Require the block was built with a successful predicate result
|
||||
ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock
|
||||
headerPredicateResultsBytes, ok := predicate.GetPredicateResultBytes(ethBlock.Extra())
|
||||
require.True(ok)
|
||||
headerPredicateResultsBytes := customheader.PredicateBytesFromExtra(ethBlock.Extra())
|
||||
results, err := predicate.ParseResults(headerPredicateResultsBytes)
|
||||
require.NoError(err)
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ package predicate
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ava-labs/subnet-evm/plugin/evm/header"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
@@ -50,15 +49,3 @@ func UnpackPredicate(paddedPredicate []byte) ([]byte, error) {
|
||||
|
||||
return trimmedPredicateBytes[:len(trimmedPredicateBytes)-1], nil
|
||||
}
|
||||
|
||||
// GetPredicateResultBytes returns the predicate result bytes from the extra data and
|
||||
// true iff the predicate results bytes have non-zero length.
|
||||
func GetPredicateResultBytes(extraData []byte) ([]byte, bool) {
|
||||
// Prior to Durango, the VM enforces the extra data is smaller than or equal to this size.
|
||||
// After Durango, the VM pre-verifies the extra data past the dynamic fee rollup window is
|
||||
// valid.
|
||||
if len(extraData) <= header.FeeWindowSize {
|
||||
return nil, false
|
||||
}
|
||||
return extraData[header.FeeWindowSize:], true
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ava-labs/avalanchego/utils"
|
||||
"github.com/ava-labs/subnet-evm/plugin/evm/header"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -49,18 +48,3 @@ func TestUnpackInvalidPredicate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPredicateResultsBytes(t *testing.T) {
|
||||
require := require.New(t)
|
||||
dataTooShort := utils.RandomBytes(header.FeeWindowSize - 1)
|
||||
_, ok := GetPredicateResultBytes(dataTooShort)
|
||||
require.False(ok)
|
||||
|
||||
preDurangoData := utils.RandomBytes(header.FeeWindowSize)
|
||||
_, ok = GetPredicateResultBytes(preDurangoData)
|
||||
require.False(ok)
|
||||
postDurangoData := utils.RandomBytes(header.FeeWindowSize + 2)
|
||||
resultBytes, ok := GetPredicateResultBytes(postDurangoData)
|
||||
require.True(ok)
|
||||
require.Equal(resultBytes, postDurangoData[header.FeeWindowSize:])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user