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

Add Precompile for secp256r1 conditionally based on ArbOS version #303

Merged
merged 13 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
57 changes: 52 additions & 5 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"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 @@ -107,6 +108,12 @@ var PrecompiledContractsCancun = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{},
}

// PrecompiledContractsP256Verify contains the precompiled Ethereum
// contract specified in EIP-7212. This is exported for testing purposes.
var PrecompiledContractsP256Verify = map[common.Address]PrecompiledContract{
common.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[common.Address]PrecompiledContract{
Expand All @@ -122,11 +129,12 @@ var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
}

var (
PrecompiledAddressesCancun []common.Address
PrecompiledAddressesBerlin []common.Address
PrecompiledAddressesIstanbul []common.Address
PrecompiledAddressesByzantium []common.Address
PrecompiledAddressesHomestead []common.Address
PrecompiledAddressesP256Verify []common.Address
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need this? looks like we include those addresses in PrecompiledAddressesArbOS30 instead

Copy link
Collaborator

@PlasmaPower PlasmaPower Apr 29, 2024

Choose a reason for hiding this comment

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

Removed, I think it might've been needed in an older version of how precompile activation worked

PrecompiledAddressesCancun []common.Address
PrecompiledAddressesBerlin []common.Address
PrecompiledAddressesIstanbul []common.Address
PrecompiledAddressesByzantium []common.Address
PrecompiledAddressesHomestead []common.Address
)

func init() {
Expand All @@ -145,12 +153,18 @@ func init() {
for k := range PrecompiledContractsCancun {
PrecompiledAddressesCancun = append(PrecompiledAddressesCancun, k)
}
for k := range PrecompiledContractsP256Verify {
PrecompiledAddressesP256Verify = append(PrecompiledAddressesP256Verify, k)
}
}

// ActivePrecompiles returns the precompiles enabled with the current configuration.
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsArbitrum:
if rules.ArbOSVersion >= 30 {
return PrecompiledAddressesArbOS30
}
return PrecompiledAddressesArbitrum
case rules.IsCancun:
return PrecompiledAddressesCancun
Expand Down Expand Up @@ -1156,3 +1170,36 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {

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
}

// 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
}
// Signature is invalid.
return nil, nil
}
2 changes: 2 additions & 0 deletions core/vm/contracts_arbitrum.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import "github.com/ethereum/go-ethereum/common"
var (
PrecompiledContractsArbitrum = make(map[common.Address]PrecompiledContract)
PrecompiledAddressesArbitrum []common.Address
PrecompiledContractsArbOS30 = make(map[common.Address]PrecompiledContract)
PrecompiledAddressesArbOS30 []common.Address
)
14 changes: 14 additions & 0 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ var allPrecompiles = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{0x0a}): &kzgPointEvaluation{},

common.BytesToAddress([]byte{0x01, 0x00}): &p256Verify{},

common.BytesToAddress([]byte{0x0f, 0x0a}): &bls12381G1Add{},
common.BytesToAddress([]byte{0x0f, 0x0b}): &bls12381G1Mul{},
common.BytesToAddress([]byte{0x0f, 0x0c}): &bls12381G1MultiExp{},
Expand Down Expand Up @@ -395,3 +397,15 @@ 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("100", t, bench)
}

func TestPrecompiledP256Verify(t *testing.T) { testJson("p256Verify", "100", t) }
4 changes: 4 additions & 0 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
switch {
case evm.chainRules.IsArbitrum:
if evm.chainRules.ArbOSVersion >= 30 {
precompiles = PrecompiledContractsArbOS30
break
}
precompiles = PrecompiledContractsArbitrum
case evm.chainRules.IsCancun:
precompiles = PrecompiledContractsCancun
Expand Down
Loading
Loading