Skip to content
This repository has been archived by the owner on Aug 7, 2023. It is now read-only.

Commit

Permalink
Merge in btcd commit '3b39edcaa1e867efc4223d95ca1496aaadf8eca3'
Browse files Browse the repository at this point in the history
Merges in 3b39edc from btcd.
  • Loading branch information
cjepson committed Sep 22, 2016
2 parents 7e52a13 + 3b39edc commit 02bb512
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 44 deletions.
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const (
defaultAllowOldVotes = false
defaultMaxOrphanTransactions = 1000
defaultMaxOrphanTxSize = 5000
defaultSigCacheMaxSize = 50000
defaultSigCacheMaxSize = 100000
sampleConfigFilename = "sample-dcrd.conf"
defaultTxIndex = false
defaultNoExistsAddrIndex = false
Expand Down
8 changes: 8 additions & 0 deletions dcrec/secp256k1/pubkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ func (p PublicKey) SerializeHybrid() []byte {
return paddedAppend(32, b, p.Y.Bytes())
}

// IsEqual compares this PublicKey instance to the one passed, returning true if
// both PublicKeys are equivalent. A PublicKey is equivalent to another, if they
// both have the same X and Y coordinate.
func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
return p.X.Cmp(otherPubKey.X) == 0 &&
p.Y.Cmp(otherPubKey.Y) == 0
}

// paddedAppend appends the src byte slice to dst, returning the new slice.
// If the length of the source is smaller than the passed size, leading zero
// bytes are appended to the dst slice before appending src.
Expand Down
36 changes: 36 additions & 0 deletions dcrec/secp256k1/pubkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,39 @@ func TestPubKeys(t *testing.T) {
}
}
}

func TestPublicKeyIsEqual(t *testing.T) {
pubKey1, err := secp256k1.ParsePubKey(
[]byte{0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33,
0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34,
0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4,
0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e,
},
secp256k1.S256(),
)
if err != nil {
t.Fatalf("failed to parse raw bytes for pubKey1: %v", err)
}

pubKey2, err := secp256k1.ParsePubKey(
[]byte{0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b,
0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1,
0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21,
0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d,
},
secp256k1.S256(),
)
if err != nil {
t.Fatalf("failed to parse raw bytes for pubKey2: %v", err)
}

if !pubKey1.IsEqual(pubKey1) {
t.Fatalf("value of IsEqual is incorrect, %v is "+
"equal to %v", pubKey1, pubKey1)
}

if pubKey1.IsEqual(pubKey2) {
t.Fatalf("value of IsEqual is incorrect, %v is not "+
"equal to %v", pubKey1, pubKey2)
}
}
8 changes: 8 additions & 0 deletions dcrec/secp256k1/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ func (sig *Signature) Verify(hash []byte, pubKey *PublicKey) bool {
return ecdsa.Verify(pubKey.ToECDSA(), hash, sig.GetR(), sig.GetS())
}

// IsEqual compares this Signature instance to the one passed, returning true
// if both Signatures are equivalent. A signature is equivalent to another, if
// they both have the same scalar value for R and S.
func (sig *Signature) IsEqual(otherSig *Signature) bool {
return sig.R.Cmp(otherSig.R) == 0 &&
sig.S.Cmp(otherSig.S) == 0
}

func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error) {
// Originally this code used encoding/asn1 in order to parse the
// signature, but a number of problems were found with this approach.
Expand Down
21 changes: 21 additions & 0 deletions dcrec/secp256k1/signature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -589,3 +589,24 @@ func TestRFC6979(t *testing.T) {
}
}
}

func TestSignatureIsEqual(t *testing.T) {
sig1 := &secp256k1.Signature{
R: fromHex("0082235e21a2300022738dabb8e1bbd9d19cfb1e7ab8c30a23b0afbb8d178abcf3"),
S: fromHex("24bf68e256c534ddfaf966bf908deb944305596f7bdcc38d69acad7f9c868724"),
}
sig2 := &secp256k1.Signature{
R: fromHex("4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41"),
S: fromHex("181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09"),
}

if !sig1.IsEqual(sig1) {
t.Fatalf("value of IsEqual is incorrect, %v is "+
"equal to %v", sig1, sig1)
}

if sig1.IsEqual(sig2) {
t.Fatalf("value of IsEqual is incorrect, %v is not "+
"equal to %v", sig1, sig2)
}
}
3 changes: 3 additions & 0 deletions txscript/reference_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ func TestScriptInvalidTests(t *testing.T) {
return
}
sigCache := NewSigCache(10)

sigCacheToggle := []bool{true, false}
for _, useSigCache := range sigCacheToggle {
for i, test := range tests {
Expand Down Expand Up @@ -265,7 +266,9 @@ func TestScriptValidTests(t *testing.T) {
err)
return
}

sigCache := NewSigCache(10)

sigCacheToggle := []bool{true, false}
for _, useSigCache := range sigCacheToggle {
for i, test := range tests {
Expand Down
79 changes: 36 additions & 43 deletions txscript/sigcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@ package txscript

import (
"bytes"
"crypto/rand"
"sync"

"github.com/decred/dcrd/chaincfg/chainec"
"github.com/decred/dcrd/chaincfg/chainhash"
)

// sigInfo represents an entry in the SigCache. Entries in the sigcache are a
// 3-tuple: (sigHash, sig, pubKey).
type sigInfo struct {
sigHash chainhash.Hash
sig string
pubKey string
// sigCacheEntry represents an entry in the SigCache. Entries within the
// SigCache are keyed according to the sigHash of the signature. In the
// scenario of a cache-hit (according to the sigHash), an additional comparison
// of the signature, and public key will be executed in order to ensure a complete
// match. In the occasion that two sigHashes collide, the newer sigHash will
// simply overwrite the existing entry.
type sigCacheEntry struct {
sig chainec.Signature
pubKey chainec.PublicKey
}

// SigCache implements an ECDSA signature verification cache with a randomized
Expand All @@ -34,7 +36,7 @@ type sigInfo struct {
// if they've already been seen and verified within the mempool.
type SigCache struct {
sync.RWMutex
validSigs map[sigInfo]struct{}
validSigs map[chainhash.Hash]sigCacheEntry
maxEntries uint
}

Expand All @@ -44,7 +46,10 @@ type SigCache struct {
// to make room for new entries that would cause the number of entries in the
// cache to exceed the max.
func NewSigCache(maxEntries uint) *SigCache {
return &SigCache{validSigs: make(map[sigInfo]struct{}), maxEntries: maxEntries}
return &SigCache{
validSigs: make(map[chainhash.Hash]sigCacheEntry, maxEntries),
maxEntries: maxEntries,
}
}

// Exists returns true if an existing entry of 'sig' over 'sigHash' for public
Expand All @@ -53,13 +58,17 @@ func NewSigCache(maxEntries uint) *SigCache {
// NOTE: This function is safe for concurrent access. Readers won't be blocked
// unless there exists a writer, adding an entry to the SigCache.
func (s *SigCache) Exists(sigHash chainhash.Hash, sig chainec.Signature, pubKey chainec.PublicKey) bool {
info := sigInfo{sigHash, string(sig.Serialize()),
string(pubKey.SerializeCompressed())}

s.RLock()
_, ok := s.validSigs[info]
s.RUnlock()
return ok
defer s.RUnlock()

if entry, ok := s.validSigs[sigHash]; ok {
pkEqual := bytes.Equal(entry.pubKey.SerializeCompressed(),
pubKey.SerializeCompressed())
sigEqual := bytes.Equal(entry.sig.Serialize(), sig.Serialize())
return pkEqual && sigEqual
}

return false
}

// Add adds an entry for a signature over 'sigHash' under public key 'pubKey'
Expand All @@ -80,35 +89,19 @@ func (s *SigCache) Add(sigHash chainhash.Hash, sig chainec.Signature, pubKey cha
// If adding this new entry will put us over the max number of allowed
// entries, then evict an entry.
if uint(len(s.validSigs)+1) > s.maxEntries {
// Generate a cryptographically random hash.
randHashBytes := make([]byte, chainhash.HashSize)
_, err := rand.Read(randHashBytes)
if err != nil {
// Failure to read a random hash results in the proposed
// entry not being added to the cache since we are
// unable to evict any existing entries.
return
}

// Try to find the first entry that is greater than the random
// hash. Use the first entry (which is already pseudo random due
// to Go's range statement over maps) as a fall back if none of
// the hashes in the rejected transactions pool are larger than
// the random hash.
var foundEntry sigInfo
// Remove a random entry from the map relaying on the random
// starting point of Go's map iteration. It's worth noting that
// the random iteration starting point is not 100% guaranteed
// by the spec, however most Go compilers support it.
// Ultimately, the iteration order isn't important here because
// in order to manipulate which items are evicted, an adversary
// would need to be able to execute preimage attacks on the
// hashing function in order to start eviction at a specific
// entry.
for sigEntry := range s.validSigs {
if foundEntry.sig == "" {
foundEntry = sigEntry
}
if bytes.Compare(sigEntry.sigHash.Bytes(), randHashBytes) > 0 {
foundEntry = sigEntry
break
}
delete(s.validSigs, sigEntry)
break
}
delete(s.validSigs, foundEntry)
}

info := sigInfo{sigHash, string(sig.Serialize()),
string(pubKey.SerializeCompressed())}
s.validSigs[info] = struct{}{}
s.validSigs[sigHash] = sigCacheEntry{sig, pubKey}
}

0 comments on commit 02bb512

Please sign in to comment.