-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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 and integrate an ECDSA signature verification cache into btcd #506
Conversation
@@ -116,6 +117,7 @@ type config struct { | |||
GetWorkKeys []string `long:"getworkkey" description:"DEPRECATED -- Use the --miningaddr option instead"` | |||
AddrIndex bool `long:"addrindex" description:"Build and maintain a full address index. Currently only supported by leveldb."` | |||
DropAddrIndex bool `long:"dropaddrindex" description:"Deletes the address-based transaction index from the database on start up, and the exits."` | |||
SigCacheMaxSize int `long:"sigcachemaxsize" description:"hello"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SigCacheMaxSize int long:"sigcachemaxsize" description:"hello"
--->
SigCacheMaxSize int long:"sigcachemaxsize" description:"Allows users to configure the amount of memory consumed by this new additional caching."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
The hello
was left over from when I just needed it all to compile :)
💯 |
@@ -1406,7 +1406,7 @@ func (state *gbtWorkState) templateUpdateChan(prevHash *wire.ShaHash, lastGenera | |||
// addresses. | |||
// | |||
// This function MUST be called with the state locked. | |||
func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool) error { | |||
func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool, sigCache *txscript.SigCache) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than passing the sigCache as a separate parameter, why not just access it via s.server.sigCache
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I can't recall why I previously passed the sigCache in as a separate parameter.
I've updated the fragment and surrounding code to just use: s.server.sigCache
.
The new parameter will need to be added to |
I think this should also be applied to if parsedSig.Verify(hash, parsedPubKey) { |
sigCacheToggle := []bool{true, false} | ||
for _, useSigCache := range sigCacheToggle { | ||
for i, test := range tests { | ||
var vm *Engine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be declared just before the if useSigCache
branch below. Also err
doesn't need to be declared since it will already exist by virtual of the previous statements doing foo, err := ...
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you noted fixed, but it's still here. Lost in a rebase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm seems some line numbers got mixed up. I corrected the issue in TestScriptValidTests
, but not TestScriptInvalidTests
. Should be all remedied now in the latest snapshot.
Also, looks like a few updates in the tests were missed:
|
Updated |
--rpckey= File containing the certificate key | ||
--rpcmaxclients= Max number of RPC clients for standard connections | ||
(10) | ||
--rpcmaxwebsockets= Max number of RPC clients for standard connections |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Look like another one got changed incorrectly. It should be Max number of RPC websocket connections (25)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Speaking of the |
@@ -366,7 +366,7 @@ func medianAdjustedTime(chainState *chainState, timeSource blockchain.MedianTime | |||
// | transactions (while block size | | | |||
// | <= cfg.BlockMinSize) | | | |||
// ----------------------------------- -- | |||
func NewBlockTemplate(mempool *txMemPool, payToAddress btcutil.Address) (*BlockTemplate, error) { | |||
func NewBlockTemplate(mempool *txMemPool, payToAddress btcutil.Address, sigCache *txscript.SigCache) (*BlockTemplate, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe change this to accept the server instead of the separate mempool
and sigCache
? I suggest that because I noticed all the callers have s.server.txMemPool
and s.server.sigCache
for the params. Then the callers could just pass s.server
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've changed NewBlockTemplate
to directly take the server
as a parameter as suggested.
|
||
sigCopy, _ := btcec.ParseSignature(sig.Serialize(), btcec.S256()) | ||
keyCopy, _ := btcec.ParsePubKey(key.SerializeCompressed(), btcec.S256()) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent blank line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
I'm done with this round of review. It looks like functionally, everything is good to go. I'm going to do some manual testing on it as well. |
// triplet is added to a full signature cache which should trigger randomized | ||
// eviction, followed by adding the new element to the cache. | ||
func TestSigCacheAddEvictEntry(t *testing.T) { | ||
// Create a sigcache that can hold up to 10 entries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment is incorrect here.
For reference, I've done a full chain download with checkpoints disabled (to force every script in the chain to be executed), and I've also confirmed that it's using cached signatures (by adding a temporary logging statement) for transactions in the memory pool that are included in new block as expected. Aside from the incorrect comment, this is ready. Please fix the comment and rebase/squash to the latest master. |
ee27e62
to
c942901
Compare
I've fixed the comment (also clarifying that the cache is only for valid signatures), and rebase/squashed to the latest master 👍 |
ea20ec2
to
422da66
Compare
OK |
// if they've already been seen and verified within the mempool. | ||
type SigCache struct { | ||
sync.RWMutex | ||
validSigs map[sigInfo]struct{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might not work as you expect since sigInfo contains string fields (the shallow equality operator might not work if the two strings are different e.g. one is a static string, and the other was created at runtime).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nevermind: http://play.golang.org/p/Phwr-adqy7
Latest changes look good. OK after rebase/squash. |
Introduce an ECDSA signature verification into btcd in order to mitigate a certain DoS attack and as a performance optimization. The benefits of SigCache are two fold. Firstly, usage of SigCache mitigates a DoS attack wherein an attacker causes a victim's client to hang due to worst-case behavior triggered while processing attacker crafted invalid transactions. A detailed description of the mitigated DoS attack can be found here: https://bitslog.wordpress.com/2013/01/23/fixed-bitcoin-vulnerability-explanation-why-the-signature-cache-is-a-dos-protection/ Secondly, usage of the SigCache introduces a signature verification optimization which speeds up the validation of transactions within a block, if they've already been seen and verified within the mempool. The server itself manages the sigCache instance. The blockManager and txMempool respectively now receive pointers to the created sigCache instance. All read (sig triplet existence) operations on the sigCache will not block unless a separate goroutine is adding an entry (writing) to the sigCache. GetBlockTemplate generation now also utilizes the sigCache in order to avoid unnecessarily double checking signatures when generating a template after previously accepting a txn to the mempool. Consequently, the CPU miner now also employs the same optimization. The maximum number of entries for the sigCache has been introduced as a config parameter in order to allow users to configure the amount of memory consumed by this new additional caching.
This caused unhelpful clutter in travis output. Closes btcsuite#506
This PR proposes the addition of an ECDSA signature verification cache
with a randomized entry eviction policy into btcd.
The benefits of SigCache are two fold. Firstly, usage of SigCache mitigates
a DoS attack wherein an attacker causes a victim's client to hang due to worst-case
behavior triggered while processing attacker crafted invalid transactions. A
detailed description of the mitigated DoS attack can be found here.
Secondly, usage of the SigCache introduces a signature verification
optimization which speeds up the validation of transactions within a block,
if they've already once been seen and verified within the mempool.
Implementation wise:
The server itself manages the sigCache instance. The blockManager and
txMempool both respectively now receive pointers to the created sigCache
instance. All read (sig triplet existence) operations on the sigCache
will not block unless a separate goroutine is adding an entry (writing)
to the sigCache. GetBlockTemplate generation now also utilizes the
sigCache in order to avoid unnecessarily double checking signatures
when generating a template after previously accepting a txn to the
mempool. Consequently, the CPU miner now also employs the same
optimization.
The maximum number of entires for the sigCache has been introduced as a
config parameter in order to allow users to configure the amount of
memory consumed by this new additional caching.