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

Implement PIP-33: Napoli Hardfork #8975

Merged
merged 13 commits into from
Jan 17, 2024
9 changes: 7 additions & 2 deletions core/forkid/forkid.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,13 @@ func GatherForks(config *chain.Config, genesisTime uint64) (heightForks []uint64
heightForks = append(heightForks, *config.Aura.PosdaoTransition)
}

if config.Bor != nil && config.Bor.GetAgraBlock() != nil {
heightForks = append(heightForks, config.Bor.GetAgraBlock().Uint64())
if config.Bor != nil {
if config.Bor.GetAgraBlock() != nil {
heightForks = append(heightForks, config.Bor.GetAgraBlock().Uint64())
}
if config.Bor.GetNapoliBlock() != nil {
heightForks = append(heightForks, config.Bor.GetNapoliBlock().Uint64())
}
}

// Sort the fork block numbers & times to permit chronological XOR
Expand Down
2 changes: 1 addition & 1 deletion core/types/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import (

// the following 2 functions are replica for the test
// This is a replica of `bor.GetValidatorBytes` function
// This was needed because currently, `IsParallelUniverse` will always return false.
// This was needed because currently, `IsNapoli` will always return false.
func GetValidatorBytesTest(h *Header) []byte {
if len(h.Extra) < ExtraVanityLength+ExtraSealLength {
log.Error("length of extra is less than vanity and seal")
Expand Down
55 changes: 54 additions & 1 deletion core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import (
"crypto/sha256"
"encoding/binary"
"errors"
"github.com/ledgerwatch/erigon-lib/crypto/blake2b"
"math/big"

"github.com/ledgerwatch/erigon-lib/crypto/blake2b"

"github.com/holiman/uint256"

"github.com/ledgerwatch/erigon-lib/chain"
Expand All @@ -34,6 +35,7 @@ import (
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/crypto/bls12381"
"github.com/ledgerwatch/erigon/crypto/bn256"
"github.com/ledgerwatch/erigon/crypto/secp256r1"

"github.com/ledgerwatch/erigon/params"

Expand Down Expand Up @@ -112,6 +114,20 @@ var PrecompiledContractsCancun = map[libcommon.Address]PrecompiledContract{
libcommon.BytesToAddress([]byte{0x0a}): &pointEvaluation{},
}

var PrecompiledContractsNapoli = map[libcommon.Address]PrecompiledContract{
libcommon.BytesToAddress([]byte{0x01}): &ecrecover{},
libcommon.BytesToAddress([]byte{0x02}): &sha256hash{},
libcommon.BytesToAddress([]byte{0x03}): &ripemd160hash{},
libcommon.BytesToAddress([]byte{0x04}): &dataCopy{},
libcommon.BytesToAddress([]byte{0x05}): &bigModExp{eip2565: true},
libcommon.BytesToAddress([]byte{0x06}): &bn256AddIstanbul{},
libcommon.BytesToAddress([]byte{0x07}): &bn256ScalarMulIstanbul{},
libcommon.BytesToAddress([]byte{0x08}): &bn256PairingIstanbul{},
libcommon.BytesToAddress([]byte{0x09}): &blake2F{},
libcommon.BytesToAddress([]byte{0x0a}): &pointEvaluation{},
libcommon.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{},
}

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

var (
PrecompiledAddressesNapoli []libcommon.Address
PrecompiledAddressesCancun []libcommon.Address
PrecompiledAddressesBerlin []libcommon.Address
PrecompiledAddressesIstanbul []libcommon.Address
Expand Down Expand Up @@ -155,6 +172,8 @@ func init() {
// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules *chain.Rules) []libcommon.Address {
switch {
case rules.IsNapoli:
return PrecompiledAddressesNapoli
case rules.IsCancun:
return PrecompiledAddressesCancun
case rules.IsBerlin:
Expand Down Expand Up @@ -1098,3 +1117,37 @@ func (c *pointEvaluation) RequiredGas(input []byte) uint64 {
func (c *pointEvaluation) Run(input []byte) ([]byte, error) {
return libkzg.PointEvaluationPrecompile(input)
}

// P256VERIFY (secp256r1 signature verification)
// implemented as a native contract
type p256Verify struct{}

// RequiredGas returns the gas required to execute the precompiled contract
func (c *p256Verify) RequiredGas(input []byte) uint64 {
return params.P256VerifyGas
}

// Run executes the precompiled contract with given 160 bytes of param, returning the output and the used gas
func (c *p256Verify) Run(input []byte) ([]byte, error) {
// Required input length is 160 bytes
const p256VerifyInputLength = 160
// Check the input length
if len(input) != p256VerifyInputLength {
// Input length is invalid
return nil, nil
}

// Extract the hash, r, s, x, y from the input
hash := input[0:32]
r, s := new(big.Int).SetBytes(input[32:64]), new(big.Int).SetBytes(input[64:96])
x, y := new(big.Int).SetBytes(input[96:128]), new(big.Int).SetBytes(input[128:160])

// Verify the secp256r1 signature
if secp256r1.Verify(hash, r, s, x, y) {
// Signature is valid
return common.LeftPadBytes(big.NewInt(1).Bytes(), 32), nil
} else {
// Signature is invalid
return nil, nil
}
}
57 changes: 37 additions & 20 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,27 @@ type precompiledFailureTest struct {
// allPrecompiles does not map to the actual set of precompiles, as it also contains
// repriced versions of precompiles at certain slots
var allPrecompiles = map[libcommon.Address]PrecompiledContract{
libcommon.BytesToAddress([]byte{1}): &ecrecover{},
libcommon.BytesToAddress([]byte{2}): &sha256hash{},
libcommon.BytesToAddress([]byte{3}): &ripemd160hash{},
libcommon.BytesToAddress([]byte{4}): &dataCopy{},
libcommon.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
libcommon.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true},
libcommon.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
libcommon.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
libcommon.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
libcommon.BytesToAddress([]byte{9}): &blake2F{},
libcommon.BytesToAddress([]byte{10}): &bls12381G1Add{},
libcommon.BytesToAddress([]byte{11}): &bls12381G1Mul{},
libcommon.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
libcommon.BytesToAddress([]byte{13}): &bls12381G2Add{},
libcommon.BytesToAddress([]byte{14}): &bls12381G2Mul{},
libcommon.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
libcommon.BytesToAddress([]byte{16}): &bls12381Pairing{},
libcommon.BytesToAddress([]byte{17}): &bls12381MapG1{},
libcommon.BytesToAddress([]byte{18}): &bls12381MapG2{},
libcommon.BytesToAddress([]byte{20}): &pointEvaluation{},
libcommon.BytesToAddress([]byte{1}): &ecrecover{},
libcommon.BytesToAddress([]byte{2}): &sha256hash{},
libcommon.BytesToAddress([]byte{3}): &ripemd160hash{},
libcommon.BytesToAddress([]byte{4}): &dataCopy{},
libcommon.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
libcommon.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true},
libcommon.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
libcommon.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
libcommon.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
libcommon.BytesToAddress([]byte{9}): &blake2F{},
libcommon.BytesToAddress([]byte{10}): &bls12381G1Add{},
libcommon.BytesToAddress([]byte{11}): &bls12381G1Mul{},
libcommon.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
libcommon.BytesToAddress([]byte{13}): &bls12381G2Add{},
libcommon.BytesToAddress([]byte{14}): &bls12381G2Mul{},
libcommon.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
libcommon.BytesToAddress([]byte{16}): &bls12381Pairing{},
libcommon.BytesToAddress([]byte{17}): &bls12381MapG1{},
libcommon.BytesToAddress([]byte{18}): &bls12381MapG2{},
libcommon.BytesToAddress([]byte{20}): &pointEvaluation{},
libcommon.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{},
}

// EIP-152 test vectors
Expand Down Expand Up @@ -400,3 +401,19 @@ func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.B) {
}
benchmarkPrecompiled(b, "0f", testcase)
}

// Benchmarks the sample inputs from the P256VERIFY precompile.
func BenchmarkPrecompiledP256Verify(b *testing.B) {
testcase := precompiledTest{
Input: "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e",
Expected: "0000000000000000000000000000000000000000000000000000000000000001",
Name: "p256Verify",
}
benchmarkPrecompiled(b, "100", testcase)
}

func TestPrecompiledP256Verify(t *testing.T) {
t.Parallel()

testJson("p256Verify", "100", t)
}
2 changes: 2 additions & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ var emptyCodeHash = crypto.Keccak256Hash(nil)
func (evm *EVM) precompile(addr libcommon.Address) (PrecompiledContract, bool) {
var precompiles map[libcommon.Address]PrecompiledContract
switch {
case evm.chainRules.IsNapoli:
precompiles = PrecompiledContractsNapoli
case evm.chainRules.IsCancun:
precompiles = PrecompiledContractsCancun
case evm.chainRules.IsBerlin:
Expand Down
2 changes: 2 additions & 0 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
jt = &pragueInstructionSet
case evm.ChainRules().IsCancun:
jt = &cancunInstructionSet
case evm.ChainRules().IsNapoli:
jt = &napoliInstructionSet
case evm.ChainRules().IsShanghai:
jt = &shanghaiInstructionSet
case evm.ChainRules().IsLondon:
Expand Down
11 changes: 9 additions & 2 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ var (
berlinInstructionSet = newBerlinInstructionSet()
londonInstructionSet = newLondonInstructionSet()
shanghaiInstructionSet = newShanghaiInstructionSet()
napoliInstructionSet = newNapoliInstructionSet()
cancunInstructionSet = newCancunInstructionSet()
pragueInstructionSet = newPragueInstructionSet()
)
Expand Down Expand Up @@ -99,12 +100,18 @@ func newPragueInstructionSet() JumpTable {
// constantinople, istanbul, petersburg, berlin, london, paris, shanghai,
// and cancun instructions.
func newCancunInstructionSet() JumpTable {
instructionSet := newNapoliInstructionSet()
enable4844(&instructionSet) // BLOBHASH opcode
enable7516(&instructionSet) // BLOBBASEFEE opcode
validateAndFillMaxStack(&instructionSet)
return instructionSet
}

func newNapoliInstructionSet() JumpTable {
instructionSet := newShanghaiInstructionSet()
enable1153(&instructionSet) // Transient storage opcodes
enable4844(&instructionSet) // BLOBHASH opcode
enable5656(&instructionSet) // MCOPY opcode
enable6780(&instructionSet) // SELFDESTRUCT only in same transaction
enable7516(&instructionSet) // BLOBBASEFEE opcode
validateAndFillMaxStack(&instructionSet)
return instructionSet
}
Expand Down
37 changes: 37 additions & 0 deletions core/vm/testdata/precompiles/p256Verify.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[
{
"Input": "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e",
"Expected": "0000000000000000000000000000000000000000000000000000000000000001",
"Gas": 3450,
"Name": "CallP256Verify",
"NoBenchmark": false
},
{
"Input": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9414de3726ee4d237b410c1d85ebcb05553dc578561d9f7942b7250795beb9b9027b657067322fc00ab35263fde0acabf998cd9fcf1282df9555f85dba7bdbbe2dc90f74c9e210bc3e0c60aeaa03729c9e6acde4a048ee58fd2e466c1e7b0374e606b8c22ad2985df7d792ff344f03ce94a079da801006b13640bc5af7932a7b9",
"Expected": "0000000000000000000000000000000000000000000000000000000000000001",
"Gas": 3450,
"Name": "CallP256Verify",
"NoBenchmark": false
},
{
"Input": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9b35d6a4f7f6fc5620c97d4287696f5174b3d37fa537b74b5fc26997ba79c725d62fe5e5fe6da76eec924e822c5ef853ede6c17069a9e9133a38f87d61599f68e7d5f3c812a255436846ee84a262b79ec4d0783afccf2433deabdca9ecf62bef5ff24e90988c7f139d378549c3a8bc6c94e6a1c911c1e02e6f48ed65aaf3d296e",
"Expected": "0000000000000000000000000000000000000000000000000000000000000001",
"Gas": 3450,
"Name": "CallP256Verify",
"NoBenchmark": false
},
{
"Input": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9c29c3df6ce3431b6f030b1b68b1589508ad9d1a149830489c638653aa4b08af93f6e86a9a7643403b6f5c593410d9f7234a8cd27309bce90447073ce17476850615ff147863bc8652be1e369444f90bbc5f9df05a26362e609f73ab1f1839fe3cd34fd2ae672c110671d49115825fc56b5148321aabe5ba39f2b46f71149cff9",
"Expected": "",
"Gas": 3450,
"Name": "CallP256Verify",
"NoBenchmark": false
},
{
"Input": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
"Expected": "",
"Gas": 3450,
"Name": "CallP256Verify",
"NoBenchmark": false
}
]
26 changes: 26 additions & 0 deletions crypto/secp256r1/publickey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package secp256r1

import (
"crypto/ecdsa"
"crypto/elliptic"
"math/big"
)

// Generates approptiate public key format from given coordinates
func newPublicKey(x, y *big.Int) *ecdsa.PublicKey {
// Check if the given coordinates are valid
if x == nil || y == nil || !elliptic.P256().IsOnCurve(x, y) {
return nil
}

// Check if the given coordinates are the reference point (infinity)
if x.Sign() == 0 && y.Sign() == 0 {
return nil
}

return &ecdsa.PublicKey{
Curve: elliptic.P256(),
X: x,
Y: y,
}
}
21 changes: 21 additions & 0 deletions crypto/secp256r1/verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package secp256r1

import (
"crypto/ecdsa"
"math/big"
)

// Verifies the given signature (r, s) for the given hash and public key (x, y).
func Verify(hash []byte, r, s, x, y *big.Int) bool {
// Create the public key format
publicKey := newPublicKey(x, y)

// Check if they are invalid public key coordinates
if publicKey == nil {
return false
}

// Verify the signature with the public key,
// then return true if it's valid, false otherwise
return ecdsa.Verify(publicKey, hash, r, s)
}
20 changes: 15 additions & 5 deletions erigon-lib/chain/chain_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ type BorConfig interface {
fmt.Stringer
IsAgra(num uint64) bool
GetAgraBlock() *big.Int
IsNapoli(num uint64) bool
GetNapoliBlock() *big.Int
}

func (c *Config) String() string {
Expand Down Expand Up @@ -214,6 +216,11 @@ func (c *Config) IsAgra(num uint64) bool {
return (c != nil) && (c.Bor != nil) && c.Bor.IsAgra(num)
}

// Refer to https://forum.polygon.technology/t/pip-33-napoli-upgrade
func (c *Config) IsNapoli(num uint64) bool {
return (c != nil) && (c.Bor != nil) && c.Bor.IsNapoli(num)
}

// IsCancun returns whether time is either equal to the Cancun fork time or greater.
func (c *Config) IsCancun(time uint64) bool {
return isForked(c.CancunTime, time)
Expand Down Expand Up @@ -484,11 +491,13 @@ func borKeyValueConfigHelper[T uint64 | common.Address](field map[string]T, numb
// Rules is a one time interface meaning that it shouldn't be used in between transition
// phases.
type Rules struct {
ChainID *big.Int
IsHomestead, IsTangerineWhistle, IsSpuriousDragon bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon, IsShanghai, IsCancun, IsPrague bool
IsAura bool
ChainID *big.Int
IsHomestead, IsTangerineWhistle, IsSpuriousDragon bool
IsByzantium, IsConstantinople, IsPetersburg bool
IsIstanbul, IsBerlin, IsLondon, IsShanghai bool
IsCancun, IsNapoli bool
IsPrague bool
IsAura bool
}

// Rules ensures c's ChainID is not nil and returns a new Rules instance
Expand All @@ -511,6 +520,7 @@ func (c *Config) Rules(num uint64, time uint64) *Rules {
IsLondon: c.IsLondon(num),
IsShanghai: c.IsShanghai(time) || c.IsAgra(num),
IsCancun: c.IsCancun(time),
IsNapoli: c.IsNapoli(num),
IsPrague: c.IsPrague(time),
IsAura: c.Aura != nil,
}
Expand Down
2 changes: 1 addition & 1 deletion erigon-lib/txpool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ func (p *TxPool) isShanghai() bool {
}

func (p *TxPool) isAgra() bool {
// once this flag has been set for the first time we no longer need to check the timestamp
// once this flag has been set for the first time we no longer need to check the block
set := p.isPostAgra.Load()
if set {
return true
Expand Down
3 changes: 3 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ const (

// EIP-4844: Shard Blob Transactions
PointEvaluationGas uint64 = 50000

// PIP-27: secp256r1 elliptic curve signature verifier gas price
P256VerifyGas uint64 = 3450
)

// EIP-4788: Beacon block root in the EVM
Expand Down
Loading
Loading