Skip to content

Commit

Permalink
Precompiled contract secp256r1 (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
CoderZhi authored and dustinxie committed Aug 12, 2022
1 parent 75d7609 commit dcc10c7
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 35 deletions.
10 changes: 5 additions & 5 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,6 @@ func (s *StateDB) Empty(addr common.Address) bool {
return so == nil || so.empty()
}

// InitNonce returns the initial nonce
func (s *StateDB) InitNonce() uint64 {
return 0
}

// GetBalance retrieves the balance from the given address or 0 if object not found
func (s *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := s.getStateObject(addr)
Expand All @@ -271,6 +266,11 @@ func (s *StateDB) GetBalance(addr common.Address) *big.Int {
return common.Big0
}

// IsNewAccount returns true if this is a new account
func (s *StateDB) IsNewAccount(addr common.Address) bool {
return s.GetNonce(addr) == 0
}

func (s *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := s.getStateObject(addr)
if stateObject != nil {
Expand Down
51 changes: 42 additions & 9 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package vm

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"encoding/binary"
"errors"
Expand Down Expand Up @@ -81,15 +83,16 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{128, 1}): &secp256r1{},
}

// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
Expand Down Expand Up @@ -1043,3 +1046,33 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
// Encode the G2 point to 256 bytes
return g.EncodePoint(r), nil
}

var (
errSecp256r1InvalidInputLength = errors.New("invalid input length")
errSecp256r1InvalidCurvePoint = errors.New("invalid curve point")
)

// secp256r1 implements secp256r1 signature verification
type secp256r1 struct{}

func (s *secp256r1) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}

func (sec *secp256r1) Run(input []byte) ([]byte, error) {
if len(input) <= 96 {
return nil, errSecp256r1InvalidInputLength
}
hash := input[:32]
r := new(big.Int).SetBytes(input[32:64])
s := new(big.Int).SetBytes(input[64:96])
curve := elliptic.P256()
x, y := elliptic.Unmarshal(curve, input[96:])
if x == nil || y == nil {
return nil, errSecp256r1InvalidCurvePoint
}
if ecdsa.Verify(&ecdsa.PublicKey{Curve: curve, X: x, Y: y}, hash, r, s) {
return []byte{1}, nil
}
return []byte{0}, nil
}
74 changes: 55 additions & 19 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ package vm

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -46,25 +51,26 @@ 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[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{10}): &bls12381G1Add{},
common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
common.BytesToAddress([]byte{13}): &bls12381G2Add{},
common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
common.BytesToAddress([]byte{16}): &bls12381Pairing{},
common.BytesToAddress([]byte{17}): &bls12381MapG1{},
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{0xf5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{10}): &bls12381G1Add{},
common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
common.BytesToAddress([]byte{13}): &bls12381G2Add{},
common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
common.BytesToAddress([]byte{16}): &bls12381Pairing{},
common.BytesToAddress([]byte{17}): &bls12381MapG1{},
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
common.BytesToAddress([]byte{128, 1}): &secp256r1{},
}

// EIP-152 test vectors
Expand Down Expand Up @@ -191,6 +197,28 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
})
}

// Benchmarks the sample inputs from the Secp256r1 precompile
func BenchmarkPrecompiledSecp256r1(bench *testing.B) {
priKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
hashed := sha256.Sum256([]byte("testing"))
r, s, err := ecdsa.Sign(rand.Reader, priKey, hashed[:])
if err != nil {
bench.Error(err)
return
}
rb := make([]byte, 32)
r.FillBytes(rb)
sb := make([]byte, 32)
s.FillBytes(sb)
pubKey := priKey.PublicKey
t := precompiledTest{
Input: hex.EncodeToString(append(append(append(hashed[:], rb...), sb...), elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y)...)),
Expected: "01",
Name: "",
}
benchmarkPrecompiled("8001", t, bench)
}

// Benchmarks the sample inputs from the ECRECOVER precompile.
func BenchmarkPrecompiledEcrecover(bench *testing.B) {
t := precompiledTest{
Expand Down Expand Up @@ -272,6 +300,14 @@ func TestPrecompileBlake2FMalformedInput(t *testing.T) {

func TestPrecompiledEcrecover(t *testing.T) { testJson("ecRecover", "01", t) }

func TestPrecompiledSecp256r1Fail(t *testing.T) {
testJsonFail("secp256r1", "8001", t)
}

func TestPrecompiledSecp256r1(t *testing.T) {
testJson("secp256r1", "8001", t)
}

func testJson(name, addr string, t *testing.T) {
tests, err := loadJson(name)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
// Ensure there's no existing contract already at the designated address
contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != evm.StateDB.InitNonce() || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
if !evm.StateDB.IsNewAccount(address) || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
return nil, common.Address{}, 0, ErrContractAddressCollision
}
// Create a new account on the state
Expand Down
2 changes: 1 addition & 1 deletion core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type StateDB interface {
AddBalance(common.Address, *big.Int)
GetBalance(common.Address) *big.Int

InitNonce() uint64
IsNewAccount(common.Address) bool
GetNonce(common.Address) uint64
SetNonce(common.Address, uint64)

Expand Down
23 changes: 23 additions & 0 deletions core/vm/testdata/precompiles/fail-secp256r1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"Input": "cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a9037c416673d9a1548b5b79e1529e9e0bca5b3019af6462547c86c5f2d8d5c91511bbaa47b73f668f9a1c59201d1e453679420577ed9548b95836bd20b5d821da9",
"Gas": 3000,
"ExpectedError": "invalid input length",
"Name": "InvalidInputLength",
"NoBenchmark": false
},
{
"Input": "cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a9037c416673d9a1548b5b79e1529e9e0bca5b3019af6462547c86c5f2d8d5c91511bbaa47b73f668f9a1c59201d1e453679420577ed9548b95836bd20b5d821da904f9f37b3e660e01eb3f3f5535da7c96cffc620e3027883f312cda7ad59a8be1b3cc8e7334a8c0174a7e0db5265e62624cfe1f113a6ed9826247ae1debc33f",
"Gas": 3000,
"ExpectedError": "invalid curve point",
"Name": "InvalidCurvePoint",
"NoBenchmark": false
},
{
"Input": "cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a9037c416673d9a1548b5b79e1529e9e0bca5b3019af6462547c86c5f2d8d5c91511bbaa47b73f668f9a1c59201d1e453679420577ed9548b95836bd20b5d821da904f9f37b3e660e01eb3f3f5535da7c96cffc620e3027883f312cda7ad59a8be1b3cc8e7334a8c0174a7e0db5265e62624cfe1f113a6ed9826247ae1debc33faa2eaa",
"Gas": 3000,
"ExpectedError": "invalid curve point",
"Name": "InvalidCurvePoint",
"NoBenchmark": false
}
]
30 changes: 30 additions & 0 deletions core/vm/testdata/precompiles/secp256r1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[
{
"Input": "cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a9037c416673d9a1548b5b79e1529e9e0bca5b3019af6462547c86c5f2d8d5c91511bbaa47b73f668f9a1c59201d1e453679420577ed9548b95836bd20b5d821da904f9f37b3e660e01eb3f3f5535da7c96cffc620e3027883f312cda7ad59a8be1b3cc8e7334a8c0174a7e0db5265e62624cfe1f113a6ed9826247ae1debc33faa2e",
"Gas": 3000,
"Expected": "01",
"Name": "ValidKey",
"NoBenchmark": false
},
{
"Input": "af80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a9037c416673d9a1548b5b79e1529e9e0bca5b3019af6462547c86c5f2d8d5c91511bbaa47b73f668f9a1c59201d1e453679420577ed9548b95836bd20b5d821da904f9f37b3e660e01eb3f3f5535da7c96cffc620e3027883f312cda7ad59a8be1b3cc8e7334a8c0174a7e0db5265e62624cfe1f113a6ed9826247ae1debc33faa2e",
"Gas": 3000,
"Expected": "00",
"Name": "Invalid Hash",
"NoBenchmark": false
},
{
"Input": "cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a9047c416673d9a1548b5b79e1529e9e0bca5b3019af6462547c86c5f2d8d5c91511bbaa47b73f668f9a1c59201d1e453679420577ed9548b95836bd20b5d821da904f9f37b3e660e01eb3f3f5535da7c96cffc620e3027883f312cda7ad59a8be1b3cc8e7334a8c0174a7e0db5265e62624cfe1f113a6ed9826247ae1debc33faa2e",
"Gas": 3000,
"Expected": "00",
"Name": "Invalid r",
"NoBenchmark": false
},
{
"Input": "cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a9037c416673d9a1548b5b79e1529e9e0bca5b3019af6462547c86c5f2d8d5c91512bbaa47b73f668f9a1c59201d1e453679420577ed9548b95836bd20b5d821da904f9f37b3e660e01eb3f3f5535da7c96cffc620e3027883f312cda7ad59a8be1b3cc8e7334a8c0174a7e0db5265e62624cfe1f113a6ed9826247ae1debc33faa2e",
"Gas": 3000,
"Expected": "00",
"Name": "Invalid s",
"NoBenchmark": false
}
]

0 comments on commit dcc10c7

Please sign in to comment.