Skip to content

Commit

Permalink
Update precompile setup for warp (#390)
Browse files Browse the repository at this point in the history
* Migrate warp backend from Subnet-EVM

* warp/handlers: update duration greater than assertion to pass on windows

* Update precompile setup for warp

* Pass predicate context through block build / verify

* Fix up dynamic fee extra data window handling post D-Upgrade

* Remove duration stats test from warp network handler

* Fix trailing newline

* Update signature aggregator constructor comment

* Add back CanTransferMC and TransferMultiCoin

* Remove duplicate from reserved address space

* remove duplicate check in different package

* Update warp/aggregator/aggregator.go

Co-authored-by: Ceyhun Onur <[email protected]>
Signed-off-by: aaronbuchwald <[email protected]>

* core/vm: move native asset contracts to separate file

* Remove unused istanbul field from txpool

---------

Signed-off-by: aaronbuchwald <[email protected]>
Co-authored-by: Ceyhun Onur <[email protected]>
  • Loading branch information
aaronbuchwald and ceyonur authored Nov 28, 2023
1 parent 06c94d3 commit cbc0d3a
Show file tree
Hide file tree
Showing 64 changed files with 4,014 additions and 620 deletions.
19 changes: 12 additions & 7 deletions consensus/dummy/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade
if err != nil {
return fmt.Errorf("failed to calculate base fee: %w", err)
}
if !bytes.Equal(expectedRollupWindowBytes, header.Extra) {
if len(header.Extra) < len(expectedRollupWindowBytes) || !bytes.Equal(expectedRollupWindowBytes, header.Extra[:len(expectedRollupWindowBytes)]) {
return fmt.Errorf("expected rollup window bytes: %x, found %x", expectedRollupWindowBytes, header.Extra)
}
if header.BaseFee == nil {
Expand Down Expand Up @@ -194,16 +194,21 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header
if uncle {
return errUnclesUnsupported
}
// Ensure that the header's extra-data section is of a reasonable size
if !config.IsApricotPhase3(header.Time) {
switch {
case config.IsDUpgrade(header.Time):
if len(header.Extra) < params.DynamicFeeExtraDataSize {
return fmt.Errorf("expected extra-data field length >= %d, found %d", params.DynamicFeeExtraDataSize, len(header.Extra))
}
case config.IsApricotPhase3(header.Time):
if len(header.Extra) != params.DynamicFeeExtraDataSize {
return fmt.Errorf("expected extra-data field to be: %d, but found %d", params.DynamicFeeExtraDataSize, len(header.Extra))
}
default:
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
}
} else {
if uint64(len(header.Extra)) != params.ApricotPhase3ExtraDataSize {
return fmt.Errorf("expected extra-data field to be: %d, but found %d", params.ApricotPhase3ExtraDataSize, len(header.Extra))
}
}

// Ensure gas-related header fields are correct
if err := self.verifyHeaderGasFields(config, header, parent); err != nil {
return err
Expand Down
10 changes: 6 additions & 4 deletions consensus/dummy/dynamic_fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uin
isApricotPhase5 = config.IsApricotPhase5(parent.Time)
)
if !isApricotPhase3 || parent.Number.Cmp(common.Big0) == 0 {
initialSlice := make([]byte, params.ApricotPhase3ExtraDataSize)
initialSlice := make([]byte, params.DynamicFeeExtraDataSize)
initialBaseFee := big.NewInt(params.ApricotPhase3InitialBaseFee)
return initialSlice, initialBaseFee, nil
}
if uint64(len(parent.Extra)) != params.ApricotPhase3ExtraDataSize {
return nil, nil, fmt.Errorf("expected length of parent extra data to be %d, but found %d", params.ApricotPhase3ExtraDataSize, len(parent.Extra))

if uint64(len(parent.Extra)) < params.DynamicFeeExtraDataSize {
return nil, nil, fmt.Errorf("expected length of parent extra data to be %d, but found %d", params.DynamicFeeExtraDataSize, len(parent.Extra))
}
dynamicFeeWindow := parent.Extra[:params.DynamicFeeExtraDataSize]

if timestamp < parent.Time {
return nil, nil, fmt.Errorf("cannot calculate base fee for timestamp (%d) prior to parent timestamp (%d)", timestamp, parent.Time)
Expand All @@ -61,7 +63,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uin

// roll the window over by the difference between the timestamps to generate
// the new rollup window.
newRollupWindow, err := rollLongWindow(parent.Extra, int(roll))
newRollupWindow, err := rollLongWindow(dynamicFeeWindow, int(roll))
if err != nil {
return nil, nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
gas, _ := IntrinsicGas(data, nil, false, params.Rules{})
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)), gen.header.Time)
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, big.NewInt(225000000000), data), signer, benchRootKey)
gen.AddTx(tx)
Expand Down
13 changes: 12 additions & 1 deletion core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ func (b *BlockGen) SetExtra(data []byte) {
b.header.Extra = data
}

// AppendExtra appends data to the extra data field of the generated block.
func (b *BlockGen) AppendExtra(data []byte) {
b.header.Extra = append(b.header.Extra, data...)
}

// SetNonce sets the nonce field of the generated block.
func (b *BlockGen) SetNonce(nonce types.BlockNonce) {
b.header.Nonce = nonce
Expand All @@ -103,7 +108,8 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
b.SetCoinbase(common.Address{})
}
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
blockContext := NewEVMBlockContext(b.header, bc, &b.header.Coinbase)
receipt, err := ApplyTransaction(b.config, bc, blockContext, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -246,6 +252,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine}
b.header = makeHeader(chainreader, config, parent, gap, statedb, b.engine)

err := ApplyUpgrades(config, &parent.Header().Time, b, statedb)
if err != nil {
return nil, nil, fmt.Errorf("failed to configure precompiles %v", err)
}

// Execute any user modifications to the block
if gen != nil {
gen(i, b)
Expand Down
30 changes: 30 additions & 0 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import (
"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/predicate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
//"github.com/ethereum/go-ethereum/log"
)

Expand All @@ -48,6 +50,33 @@ 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 the DUpgrade, the VM enforces the extra data is smaller than or
// equal to this size. After the DUpgrade, 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
Expand All @@ -68,6 +97,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
Transfer: Transfer,
TransferMultiCoin: TransferMultiCoin,
GetHash: GetHashFn(header, chain),
PredicateResults: predicateResults,
Coinbase: beneficiary,
BlockNumber: new(big.Int).Set(header.Number),
Time: header.Time,
Expand Down
5 changes: 4 additions & 1 deletion core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,10 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block
}

// Configure any stateful precompiles that should be enabled in the genesis.
g.Config.CheckConfigurePrecompiles(nil, types.NewBlockWithHeader(head), statedb)
err = ApplyPrecompileActivations(g.Config, nil, types.NewBlockWithHeader(head), statedb)
if err != nil {
panic(fmt.Sprintf("unable to configure precompiles in genesis block: %v", err))
}

for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance)
Expand Down
61 changes: 61 additions & 0 deletions core/predicate_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package core

import (
"errors"
"fmt"

"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/precompile/precompileconfig"
"github.com/ava-labs/coreth/predicate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)

var ErrMissingPredicateContext = errors.New("missing predicate context")

// CheckPredicates verifies the predicates of [tx] and returns the result. Returning an error invalidates the block.
func CheckPredicates(rules params.Rules, predicateContext *precompileconfig.PredicateContext, tx *types.Transaction) (map[common.Address][]byte, error) {
// Check that the transaction can cover its IntrinsicGas (including the gas required by the predicate) before
// verifying the predicate.
intrinsicGas, err := IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, rules)
if err != nil {
return nil, err
}
if tx.Gas() < intrinsicGas {
return nil, fmt.Errorf("%w for predicate verification (%d) < intrinsic gas (%d)", ErrIntrinsicGas, tx.Gas(), intrinsicGas)
}

predicateResults := make(map[common.Address][]byte)
// Short circuit early if there are no precompile predicates to verify
if len(rules.Predicaters) == 0 {
return predicateResults, nil
}

// Prepare the predicate storage slots from the transaction's access list
predicateArguments := predicate.PreparePredicateStorageSlots(rules, tx.AccessList())

// If there are no predicates to verify, return early and skip requiring the proposervm block
// context to be populated.
if len(predicateArguments) == 0 {
return predicateResults, nil
}

if predicateContext == nil || predicateContext.ProposerVMBlockCtx == nil {
return nil, ErrMissingPredicateContext
}

for address, predicates := range predicateArguments {
// Since [address] is only added to [predicateArguments] when there's a valid predicate in the ruleset
// there's no need to check if the predicate exists here.
predicaterContract := rules.Predicaters[address]
res := predicaterContract.VerifyPredicate(predicateContext, predicates)
log.Debug("predicate verify", "tx", tx.Hash(), "address", address, "res", res)
predicateResults[address] = res
}

return predicateResults, nil
}
Loading

0 comments on commit cbc0d3a

Please sign in to comment.