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

EIP7212 / PIP27 #1069

Merged
merged 16 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"github.com/ethereum/go-ethereum/crypto/bls12381"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/crypto/secp256r1"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/crypto/ripemd160"
)
Expand Down Expand Up @@ -121,7 +122,14 @@
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
}

// PrecompiledContractsNapoli contains the precompiled Ethereum
// contract specified in EIP-7212. This is exported for testing purposes.
var PrecompiledContractsNapoli = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{19}): &p256Verify{},
}
anshalshukla marked this conversation as resolved.
Show resolved Hide resolved

var (
PrecompiledAddressesNapoli []common.Address
PrecompiledAddressesCancun []common.Address
PrecompiledAddressesBerlin []common.Address
PrecompiledAddressesIstanbul []common.Address
Expand All @@ -148,11 +156,17 @@
for k := range PrecompiledContractsCancun {
PrecompiledAddressesCancun = append(PrecompiledAddressesCancun, k)
}

for k := range PrecompiledContractsNapoli {
PrecompiledAddressesNapoli = append(PrecompiledAddressesNapoli, k)
}
}

// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsNapoli:
return PrecompiledAddressesNapoli

Check warning on line 169 in core/vm/contracts.go

View check run for this annotation

Codecov / codecov/patch

core/vm/contracts.go#L168-L169

Added lines #L168 - L169 were not covered by tests
case rules.IsCancun:
return PrecompiledAddressesCancun
case rules.IsBerlin:
Expand Down Expand Up @@ -1194,3 +1208,37 @@

return h
}

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

Check warning on line 1229 in core/vm/contracts.go

View check run for this annotation

Codecov / codecov/patch

core/vm/contracts.go#L1227-L1229

Added lines #L1227 - L1229 were not covered by tests

// 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(common.Big1.Bytes(), 32), nil
} else {
// Signature is invalid
return nil, nil
}

Check warning on line 1243 in core/vm/contracts.go

View check run for this annotation

Codecov / codecov/patch

core/vm/contracts.go#L1241-L1243

Added lines #L1241 - L1243 were not covered by tests
}
17 changes: 17 additions & 0 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var allPrecompiles = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x0f, 0x10}): &bls12381Pairing{},
common.BytesToAddress([]byte{0x0f, 0x11}): &bls12381MapG1{},
common.BytesToAddress([]byte{0x0f, 0x12}): &bls12381MapG2{},
common.BytesToAddress([]byte{19}): &p256Verify{},
}

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

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

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

testJson("p256Verify", "13", t)
}
9 changes: 9 additions & 0 deletions core/vm/testdata/precompiles/p256Verify.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"Input": "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e",
"Expected": "0000000000000000000000000000000000000000000000000000000000000001",
"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)
}
15 changes: 11 additions & 4 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ type ChainConfig struct {
CancunBlock *big.Int `json:"cancunBlock,omitempty"` // Cancun switch Block (nil = no fork, 0 = already on cancun)
PragueBlock *big.Int `json:"pragueBlock,omitempty"` // Prague switch Block (nil = no fork, 0 = already on prague)
VerkleBlock *big.Int `json:"verkleBlock,omitempty"` // Verkle switch Block (nil = no fork, 0 = already on verkle)
NapoliBlock *big.Int `json:"napoliBlock,omitempty"` // Napoli switch Block (nil = no fork, 0 = already on Napoli)

// TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade.
Expand Down Expand Up @@ -842,21 +843,26 @@ func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *bi
return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0
}

// IsShanghai returns whether time is either equal to the Shanghai fork time or greater.
// IsShanghai returns whether num is either equal to the Shanghai fork block or greater.
func (c *ChainConfig) IsShanghai(num *big.Int) bool {
return isBlockForked(c.ShanghaiBlock, num)
}

// IsCancun returns whether num is either equal to the Cancun fork time or greater.
// IsCancun returns whether num is either equal to the Cancun fork block or greater.
func (c *ChainConfig) IsCancun(num *big.Int) bool {
return isBlockForked(c.CancunBlock, num)
}

// IsPrague returns whether num is either equal to the Prague fork time or greater.
// IsPrague returns whether num is either equal to the Prague fork block or greater.
func (c *ChainConfig) IsPrague(num *big.Int) bool {
return isBlockForked(c.PragueBlock, num)
}

// IsNapoli returns whether num is either equal to the Napoli fork block or greater.
func (c *ChainConfig) IsNapoli(num *big.Int) bool {
return isBlockForked(c.NapoliBlock, num)
}

// CheckCompatible checks whether scheduled fork transitions have been imported
// with a mismatching chain configuration.
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError {
Expand Down Expand Up @@ -1145,7 +1151,7 @@ type Rules struct {
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon bool
IsMerge, IsShanghai, IsCancun, IsPrague bool
IsVerkle bool
IsVerkle, IsNapoli bool
}

// Rules ensures c's ChainID is not nil.
Expand All @@ -1171,5 +1177,6 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules
IsShanghai: c.IsShanghai(num),
IsCancun: c.IsCancun(num),
IsPrague: c.IsPrague(num),
IsNapoli: c.IsNapoli(num),
}
}
2 changes: 2 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ const (
Bls12381MapG1Gas uint64 = 5500 // Gas price for BLS12-381 mapping field element to G1 operation
Bls12381MapG2Gas uint64 = 110000 // Gas price for BLS12-381 mapping field element to G2 operation

P256VerifyGas uint64 = 3450 // secp256r1 elliptic curve signature verifier gas price

// The Refund Quotient is the cap on how much of the used gas can be refunded. Before EIP-3529,
// up to half the consumed gas could be refunded. Redefined as 1/5th in EIP-3529
RefundQuotient uint64 = 2
Expand Down
Loading