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{},
}

// PrecompiledContractsQianxuesen contains the default set of pre-compiled Ethereum
// contracts used in the Qainxuesen release.
var PrecompiledContractsQianxuesen = 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 (
PrecompiledAddressesQianxuesen []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 PrecompiledContractsQianxuesen {
PrecompiledAddressesQianxuesen = append(PrecompiledAddressesQianxuesen, k)
}
}

// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsQianxuesen:
return PrecompiledAddressesQianxuesen
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
}
33 changes: 19 additions & 14 deletions core/vm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,22 +455,27 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
lower = upper - 256
}
if num64 >= lower && num64 < upper {
chainId := interpreter.evm.ChainConfig().ChainID
chainIdBuf := make([]byte, 8)
binary.BigEndian.PutUint64(chainIdBuf, chainId.Uint64())
num64Buf := make([]byte, 8)
binary.BigEndian.PutUint64(num64Buf, num64)

if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
if interpreter.evm.chainRules.IsQianxuesen {
res := interpreter.evm.Context.GetHash(num64)
num.SetBytes(res[:])
} else {
interpreter.hasher.Reset()
chainId := interpreter.evm.ChainConfig().ChainID
chainIdBuf := make([]byte, 8)
binary.BigEndian.PutUint64(chainIdBuf, chainId.Uint64())
num64Buf := make([]byte, 8)
binary.BigEndian.PutUint64(num64Buf, num64)

if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
} else {
interpreter.hasher.Reset()
}
interpreter.hasher.Write(chainIdBuf)
interpreter.hasher.Write(num64Buf)
interpreter.hasher.Read(interpreter.hasherBuf[:])

num.SetBytes(interpreter.hasherBuf[:])
}
interpreter.hasher.Write(chainIdBuf)
interpreter.hasher.Write(num64Buf)
interpreter.hasher.Read(interpreter.hasherBuf[:])

num.SetBytes(interpreter.hasherBuf[:])
} else {
num.Clear()
}
Expand Down
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.IsQianxuesen:
jt = qianxuesenInstructionSet
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()
qianxuesenInstructionSet = newQianxuesenInstructionSet()
)

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

// newQianXueSenInstructionSet returns the frontier, homestead, byzantium,
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin and qianxuesen instructions.
func newQianxuesenInstructionSet() 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)
QianxuesenTime *uint64 `json:"qianxuesenTime,omitempty"` // Qianxuesen switch time (nil = no fork, 0 = already on qianxuesen)

// 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)
}

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

// 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, IsQianxuesen 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),
IsQianxuesen: c.IsQianxuesen(time),
}
}