Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: remove the restriction on pre-compile #177

Merged
merged 11 commits into from
Feb 18, 2025
96 changes: 87 additions & 9 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"math/big"

"github.com/morph-l2/go-ethereum/common"
Expand All @@ -28,6 +29,7 @@ import (
"github.com/morph-l2/go-ethereum/crypto/blake2b"
"github.com/morph-l2/go-ethereum/crypto/bls12381"
"github.com/morph-l2/go-ethereum/crypto/bn256"
"github.com/morph-l2/go-ethereum/crypto/kzg4844"
"github.com/morph-l2/go-ethereum/params"

//lint:ignore SA1019 Needed for precompile
Expand Down Expand Up @@ -125,6 +127,21 @@ var PrecompiledContractsBernoulli = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{9}): &blake2FDisabled{},
}

// PrecompiledContractsMorph203 contains the default set of pre-compiled Ethereum
// contracts used in the Morph203 release.
var PrecompiledContractsMorph203 = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{},
}

// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
// contracts specified in EIP-2537. These are exported for testing purposes.
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
Expand All @@ -140,6 +157,7 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
}

var (
PrecompiledAddressesMorph203 []common.Address
PrecompiledAddressesBernoulli []common.Address
PrecompiledAddressesArchimedes []common.Address
PrecompiledAddressesBerlin []common.Address
Expand Down Expand Up @@ -167,11 +185,16 @@ func init() {
for k := range PrecompiledContractsBernoulli {
PrecompiledAddressesBernoulli = append(PrecompiledAddressesBernoulli, k)
}
for k := range PrecompiledContractsMorph203 {
PrecompiledAddressesMorph203 = append(PrecompiledAddressesMorph203, k)
}
}

// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsMorph203:
return PrecompiledAddressesMorph203
case rules.IsBernoulli:
return PrecompiledAddressesBernoulli
case rules.IsArchimedes:
Expand Down Expand Up @@ -437,11 +460,6 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
expLen = expLenBigInt.Uint64()
modLen = modLenBigInt.Uint64()
)
// Check that all inputs are `u256` (32 - bytes) or less, revert otherwise
var lenLimit = new(big.Int).SetInt64(32)
if baseLenBigInt.Cmp(lenLimit) > 0 || expLenBigInt.Cmp(lenLimit) > 0 || modLenBigInt.Cmp(lenLimit) > 0 {
return nil, errModexpUnsupportedInput
}
if len(input) > 96 {
input = input[96:]
} else {
Expand Down Expand Up @@ -578,10 +596,6 @@ var (
// runBn256Pairing implements the Bn256Pairing precompile, referenced by both
// Byzantium and Istanbul operations.
func runBn256Pairing(input []byte) ([]byte, error) {
// Allow at most 4 inputs
if len(input) > 4*192 {
return nil, errBadPairingInput
}
// Handle some corner cases cheaply
if len(input)%192 > 0 {
return nil, errBadPairingInput
Expand Down Expand Up @@ -1130,3 +1144,67 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
// Encode the G2 point to 256 bytes
return g.EncodePoint(r), nil
}

// kzgPointEvaluation implements the EIP-4844 point evaluation precompile.
type kzgPointEvaluation struct{}

// RequiredGas estimates the gas required for running the point evaluation precompile.
func (b *kzgPointEvaluation) RequiredGas(input []byte) uint64 {
return params.BlobTxPointEvaluationPrecompileGas
}

const (
blobVerifyInputLength = 192 // Max input length for the point evaluation precompile.
blobCommitmentVersionKZG uint8 = 0x01 // Version byte for the point evaluation precompile.
blobPrecompileReturnValue = "000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"
)

var (
errBlobVerifyInvalidInputLength = errors.New("invalid input length")
errBlobVerifyMismatchedVersion = errors.New("mismatched versioned hash")
errBlobVerifyKZGProof = errors.New("error verifying kzg proof")
)

// Run executes the point evaluation precompile.
func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
if len(input) != blobVerifyInputLength {
return nil, errBlobVerifyInvalidInputLength
}
// versioned hash: first 32 bytes
var versionedHash common.Hash
copy(versionedHash[:], input[:])

var (
point kzg4844.Point
claim kzg4844.Claim
)
// Evaluation point: next 32 bytes
copy(point[:], input[32:])
// Expected output: next 32 bytes
copy(claim[:], input[64:])

// input kzg point: next 48 bytes
var commitment kzg4844.Commitment
copy(commitment[:], input[96:])
if kZGToVersionedHash(commitment) != versionedHash {
return nil, errBlobVerifyMismatchedVersion
}

// Proof: next 48 bytes
var proof kzg4844.Proof
copy(proof[:], input[144:])

if err := kzg4844.VerifyProof(commitment, point, claim, proof); err != nil {
return nil, fmt.Errorf("%w: %v", errBlobVerifyKZGProof, err)
}

return common.Hex2Bytes(blobPrecompileReturnValue), nil
}

// kZGToVersionedHash implements kzg_to_versioned_hash from EIP-4844
func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {
h := sha256.Sum256(kzg[:])
h[0] = blobCommitmentVersionKZG

return h
}
2 changes: 2 additions & 0 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
if cfg.JumpTable[STOP] == nil {
var jt JumpTable
switch {
case evm.chainRules.IsMorph203:
jt = morph203InstructionSet
case evm.chainRules.IsDarwin:
jt = darwinInstructionSet
case evm.chainRules.IsCurie:
Expand Down
8 changes: 8 additions & 0 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,19 @@ var (
shanghaiInstructionSet = newShanghaiInstructionSet()
curieInstructionSet = newCurieInstructionSet()
darwinInstructionSet = newDarwinInstructionSet()
morph203InstructionSet = newMorph203InstructionSet()
)

// JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation

// newMorph203InstructionSet returns the frontier, homestead, byzantium,
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin and morph203 instructions.
func newMorph203InstructionSet() JumpTable {
instructionSet := newDarwinInstructionSet()
return instructionSet
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update the function comment to accurately reflect its implementation.

The function comment suggests that it includes instructions from multiple forks, but the implementation simply returns the Darwin instruction set without any modifications. Either:

  1. Add new instructions specific to Morph203, or
  2. Update the comment to clarify that it currently inherits all instructions from Darwin.

Apply this diff to fix the comment:

-// newMorph203InstructionSet returns the frontier, homestead, byzantium,
-// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin and morph203 instructions.
+// newMorph203InstructionSet returns the Darwin instruction set.
+// TODO: Add Morph203-specific instructions if needed.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// newMorph203InstructionSet returns the frontier, homestead, byzantium,
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin and morph203 instructions.
func newMorph203InstructionSet() JumpTable {
instructionSet := newDarwinInstructionSet()
return instructionSet
}
// newMorph203InstructionSet returns the Darwin instruction set.
// TODO: Add Morph203-specific instructions if needed.
func newMorph203InstructionSet() JumpTable {
instructionSet := newDarwinInstructionSet()
return instructionSet
}


// newDarwinInstructionSet returns the frontier, homestead, byzantium,
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, and darwin instructions.
func newDarwinInstructionSet() JumpTable {
Expand Down
9 changes: 8 additions & 1 deletion params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ type ChainConfig struct {
BernoulliBlock *big.Int `json:"bernoulliBlock,omitempty"` // Bernoulli switch block (nil = no fork, 0 = already on bernoulli)
CurieBlock *big.Int `json:"curieBlock,omitempty"` // Curie switch block (nil = no fork, 0 = already on curie)
DarwinTime *uint64 `json:"darwinTime,omitempty"` // Darwin switch time (nil = no fork, 0 = already on darwin)
Morph203Time *uint64 `json:"morph203Time,omitempty"` // Morph203Time switch time (nil = no fork, 0 = already on morph203)

// TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade.
Expand Down Expand Up @@ -748,6 +749,11 @@ func (c *ChainConfig) IsDarwin(now uint64) bool {
return isForkedTime(now, c.DarwinTime)
}

// IsMorph203 returns whether num is either equal to the Morph203 fork block or greater.
func (c *ChainConfig) IsMorph203(now uint64) bool {
return isForkedTime(now, c.Morph203Time)
}

// IsTerminalPoWBlock returns whether the given block is the last block of PoW stage.
func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool {
if c.TerminalTotalDifficulty == nil {
Expand Down Expand Up @@ -960,7 +966,7 @@ type Rules struct {
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon, IsArchimedes, IsShanghai bool
IsBernoulli, IsCurie, IsDarwin bool
IsBernoulli, IsCurie, IsDarwin, IsMorph203 bool
}

// Rules ensures c's ChainID is not nil.
Expand All @@ -986,5 +992,6 @@ func (c *ChainConfig) Rules(num *big.Int, time uint64) Rules {
IsBernoulli: c.IsBernoulli(num),
IsCurie: c.IsCurie(num),
IsDarwin: c.IsDarwin(time),
IsMorph203: c.IsMorph203(time),
}
}
6 changes: 3 additions & 3 deletions params/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ import (
)

const (
VersionMajor = 5 // Major version component of the current release
VersionMinor = 5 // Minor version component of the current release
VersionPatch = 1 // Patch version component of the current release
VersionMajor = 2 // Major version component of the current release
VersionMinor = 0 // Minor version component of the current release
VersionPatch = 3 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string
)

Expand Down