forked from decred/dcrd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mempool.go
2194 lines (1927 loc) · 72.3 KB
/
mempool.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2013-2014 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"container/list"
"crypto/rand"
"fmt"
"math"
"math/big"
"sort"
"sync"
"time"
"github.com/decred/dcrd/blockchain"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
"github.com/decred/dcrd/txscript"
"github.com/decred/dcrd/wire"
"github.com/decred/dcrutil"
)
const (
// mempoolHeight is the height used for the "block" height field of the
// contextual transaction information provided in a transaction store.
mempoolHeight = 0x7fffffff
// maxOrphanTransactions is the maximum number of orphan transactions
// that can be queued.
maxOrphanTransactions = 1000
// maxOrphanTxSize is the maximum size allowed for orphan transactions.
// This helps prevent memory exhaustion attacks from sending a lot of
// of big orphans.
maxOrphanTxSize = 5000
// maxSigOpsPerTx is the maximum number of signature operations
// in a single transaction we will relay or mine. It is a fraction
// of the max signature operations for a block.
maxSigOpsPerTx = blockchain.MaxSigOpsPerBlock / 5
// maxStandardTxSize is the maximum size allowed for transactions that
// are considered standard and will therefore be relayed and considered
// for mining.
maxStandardTxSize = 100000
// maxStandardSigScriptSize is the maximum size allowed for a
// transaction input signature script to be considered standard. This
// value allows for a 15-of-15 CHECKMULTISIG pay-to-script-hash with
// compressed keys.
//
// The form of the overall script is: OP_0 <15 signatures> OP_PUSHDATA2
// <2 bytes len> [OP_15 <15 pubkeys> OP_15 OP_CHECKMULTISIG]
//
// For the p2sh script portion, each of the 15 compressed pubkeys are
// 33 bytes (plus one for the OP_DATA_33 opcode), and the thus it totals
// to (15*34)+3 = 513 bytes. Next, each of the 15 signatures is a max
// of 73 bytes (plus one for the OP_DATA_73 opcode). Also, there is one
// extra byte for the initial extra OP_0 push and 3 bytes for the
// OP_PUSHDATA2 needed to specify the 513 bytes for the script push.
// That brings the total to 1+(15*74)+3+513 = 1627. This value also
// adds a few extra bytes to provide a little buffer.
// (1 + 15*74 + 3) + (15*34 + 3) + 23 = 1650
maxStandardSigScriptSize = 1650
// maxStandardMultiSigKeys is the maximum number of public keys allowed
// in a multi-signature transaction output script for it to be
// considered standard.
maxStandardMultiSigKeys = 3
// minTxRelayFeeMainNet is the minimum fee in atoms that is required for a
// transaction to be treated as free for relay and mining purposes. It
// is also used to help determine if a transaction is considered dust
// and as a base for calculating minimum required fees per KB for larger
// transactions. This value is in Atom/1000 bytes.
minTxRelayFeeMainNet = 1e5
// minTxRelayFeeTestNet is the minimum relay fee for the Test and Simulation
// networks.
minTxRelayFeeTestNet = 1e3
// minTxFeeForMempoolMainNet is the minimum fee per KB in atoms that is
// required for a transaction to enter the mempool on MainNet.
minTxFeeForMempoolMainNet = 1e6
// minTxFeeForMempoolMainNet is the minimum fee per KB in atoms that is
// required for a transaction to enter the mempool on TestNet or SimNet.
minTxFeeForMempoolTestNet = 1e2
// maxSSGensDoubleSpends is the maximum number of SSGen double spends
// allowed in the pool.
maxSSGensDoubleSpends = 5
// heightDiffToPruneTicket is the number of blocks to pass by in terms
// of height before old tickets are pruned.
// TODO Set this based up the stake difficulty retargeting interval?
heightDiffToPruneTicket = 288
// heightDiffToPruneVotes is the number of blocks to pass by in terms
// of height before SSGen relating to that block are pruned.
heightDiffToPruneVotes = 10
// maxNullDataOutputs is the maximum number of OP_RETURN null data
// pushes in a transaction, after which it is considered non-standard.
maxNullDataOutputs = 4
)
// TxDesc is a descriptor containing a transaction in the mempool and the
// metadata we store about it.
type TxDesc struct {
Tx *dcrutil.Tx // Transaction.
Type stake.TxType // Transcation type.
Added time.Time // Time when added to pool.
Height int64 // Blockheight when added to pool.
Fee int64 // Transaction fees.
startingPriority float64 // Priority when added to the pool.
}
// GetType returns what TxType a given TxDesc is.
func (td *TxDesc) GetType() stake.TxType {
return td.Type
}
// VoteTx is a struct describing a block vote (SSGen).
type VoteTx struct {
SsgenHash chainhash.Hash // Vote
SstxHash chainhash.Hash // Ticket
Vote bool
}
// txMemPool is used as a source of transactions that need to be mined into
// blocks and relayed to other peers. It is safe for concurrent access from
// multiple peers.
type txMemPool struct {
sync.RWMutex
server *server
pool map[chainhash.Hash]*TxDesc
orphans map[chainhash.Hash]*dcrutil.Tx
orphansByPrev map[chainhash.Hash]*list.List
addrindex map[string]map[chainhash.Hash]struct{} // maps address to txs
outpoints map[wire.OutPoint]*dcrutil.Tx
// Votes on blocks.
votes map[chainhash.Hash][]*VoteTx
votesMtx sync.Mutex
lastUpdated time.Time // last time pool was updated.
pennyTotal float64 // exponentially decaying total for penny spends.
lastPennyUnix int64 // unix time of last ``penny spend''
}
// insertVote inserts a vote into the map of block votes.
// This function is safe for concurrent access.
func (mp *txMemPool) insertVote(ssgen *dcrutil.Tx) error {
voteHash := ssgen.Sha()
msgTx := ssgen.MsgTx()
ticketHash := &msgTx.TxIn[1].PreviousOutPoint.Hash
// Get the block it is voting on; here we're agnostic of height.
blockHash, blockHeight, err := stake.GetSSGenBlockVotedOn(ssgen)
if err != nil {
return err
}
voteBits := stake.GetSSGenVoteBits(ssgen)
vote := dcrutil.IsFlagSet16(voteBits, dcrutil.BlockValid)
voteTx := &VoteTx{*voteHash, *ticketHash, vote}
vts, exists := mp.votes[blockHash]
// If there are currently no votes for this block,
// start a new buffered slice and store it.
if !exists {
minrLog.Debugf("Accepted vote %v for block hash %v (height %v), "+
"voting %v on the transaction tree",
voteHash, blockHash, blockHeight, vote)
slice := make([]*VoteTx, int(mp.server.chainParams.TicketsPerBlock),
int(mp.server.chainParams.TicketsPerBlock))
slice[0] = voteTx
mp.votes[blockHash] = slice
return nil
}
// We already have a vote for this ticket; break.
for _, vt := range vts {
// At the end.
if vt == nil {
break
}
if vt.SstxHash.IsEqual(ticketHash) {
return nil
}
}
// Add the new vote in. Find where the first empty
// slot is and insert it.
for i, vt := range vts {
// At the end.
if vt == nil {
mp.votes[blockHash][i] = voteTx
break
}
}
minrLog.Debugf("Accepted vote %v for block hash %v (height %v), "+
"voting %v on the transaction tree",
voteHash, blockHash, blockHeight, vote)
return nil
}
// InsertVote calls insertVote, but makes it safe for concurrent access.
func (mp *txMemPool) InsertVote(ssgen *dcrutil.Tx) error {
mp.votesMtx.Lock()
defer mp.votesMtx.Unlock()
err := mp.insertVote(ssgen)
return err
}
// getVoteHashesForBlock gets the transaction hashes of all the known votes for
// some block on the blockchain.
func (mp *txMemPool) getVoteHashesForBlock(block chainhash.Hash) ([]chainhash.Hash,
error) {
var hashes []chainhash.Hash
vts, exists := mp.votes[block]
if !exists {
return nil, fmt.Errorf("couldn't find block requested in mp.votes")
}
if len(vts) == 0 {
return nil, fmt.Errorf("block found in mp.votes, but contains no votes")
}
zeroHash := &chainhash.Hash{}
for _, vt := range vts {
if vt == nil {
break
}
if vt.SsgenHash.IsEqual(zeroHash) {
return nil, fmt.Errorf("unset vote hash in vote info")
}
hashes = append(hashes, vt.SsgenHash)
}
return hashes, nil
}
// GetVoteHashesForBlock calls getVoteHashesForBlock, but makes it safe for
// concurrent access.
func (mp *txMemPool) GetVoteHashesForBlock(block chainhash.Hash) ([]chainhash.Hash,
error) {
mp.votesMtx.Lock()
defer mp.votesMtx.Unlock()
hashes, err := mp.getVoteHashesForBlock(block)
return hashes, err
}
// TODO Pruning of the votes map DECRED
func getNumberOfVotesOnBlock(blockVoteTxs []*VoteTx) int {
numVotes := 0
for _, vt := range blockVoteTxs {
if vt == nil {
break
}
numVotes++
}
return numVotes
}
// blockWithLenVotes is a block with the number of votes currently present
// for that block. Just used for sorting.
type blockWithLenVotes struct {
Block chainhash.Hash
Votes uint16
}
// ByNumberOfVotes defines the methods needed to satisify sort.Interface to
// sort a slice of Blocks by their number of votes.
type ByNumberOfVotes []*blockWithLenVotes
func (b ByNumberOfVotes) Len() int { return len(b) }
func (b ByNumberOfVotes) Less(i, j int) bool { return b[i].Votes < b[j].Votes }
func (b ByNumberOfVotes) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
// sortParentsByVotes takes a list of block header hashes and sorts them
// by the number of votes currently available for them in the votes map of
// mempool. It then returns all blocks that are eligible to be used (have
// at least a majority number of votes) sorted by number of votes, descending.
func (mp *txMemPool) sortParentsByVotes(currentTopBlock chainhash.Hash,
blocks []chainhash.Hash) ([]chainhash.Hash, error) {
lenBlocks := len(blocks)
if lenBlocks == 0 {
return nil, fmt.Errorf("no blocks to sort")
}
bwlvs := make([]*blockWithLenVotes, lenBlocks, lenBlocks)
for i, blockHash := range blocks {
votes, exists := mp.votes[blockHash]
if exists {
bwlv := &blockWithLenVotes{
blockHash,
uint16(getNumberOfVotesOnBlock(votes)),
}
bwlvs[i] = bwlv
} else {
bwlv := &blockWithLenVotes{
blockHash,
uint16(0),
}
bwlvs[i] = bwlv
}
}
// Blocks with the most votes appear at the top of the list.
sort.Sort(sort.Reverse(ByNumberOfVotes(bwlvs)))
var sortedUsefulBlocks []chainhash.Hash
minimumVotesRequired := uint16((mp.server.chainParams.TicketsPerBlock / 2) + 1)
for _, bwlv := range bwlvs {
if bwlv.Votes >= minimumVotesRequired {
sortedUsefulBlocks = append(sortedUsefulBlocks, bwlv.Block)
}
}
if sortedUsefulBlocks == nil {
return nil, miningRuleError(ErrNotEnoughVoters,
"no block had enough votes to build on top of")
}
// Make sure we don't reorganize the chain needlessly if the top block has
// the same amount of votes as the current leader after the sort. After this
// point, all blocks listed in sortedUsefulBlocks definitely also have the
// minimum number of votes required.
topBlockVotes, exists := mp.votes[currentTopBlock]
topBlockVotesLen := 0
if exists {
topBlockVotesLen = getNumberOfVotesOnBlock(topBlockVotes)
}
if bwlvs[0].Votes == uint16(topBlockVotesLen) {
if !bwlvs[0].Block.IsEqual(¤tTopBlock) {
// Find our block in the list.
pos := 0
for i, bwlv := range bwlvs {
if bwlv.Block.IsEqual(¤tTopBlock) {
pos = i
break
}
}
if pos == 0 { // Should never happen...
return nil, fmt.Errorf("couldn't find top block in list")
}
// Swap the top block into the first position. We directly access
// sortedUsefulBlocks useful blocks here with the assumption that
// since the values were accumulated from blvs, they should be
// in the same positions and we shouldn't be able to access anything
// out of bounds.
sortedUsefulBlocks[0], sortedUsefulBlocks[pos] =
sortedUsefulBlocks[pos], sortedUsefulBlocks[0]
}
}
return sortedUsefulBlocks, nil
}
// SortParentsByVotes is the concurrency safe exported version of
// sortParentsByVotes.
func (mp *txMemPool) SortParentsByVotes(currentTopBlock chainhash.Hash,
blocks []chainhash.Hash) ([]chainhash.Hash, error) {
mp.votesMtx.Lock()
defer mp.votesMtx.Unlock()
sortedBlocks, err := mp.sortParentsByVotes(currentTopBlock, blocks)
return sortedBlocks, err
}
// isDust returns whether or not the passed transaction output amount is
// considered dust or not. Dust is defined in terms of the minimum transaction
// relay fee. In particular, if the cost to the network to spend coins is more
// than 1/3 of the minimum transaction relay fee, it is considered dust.
func isDust(txOut *wire.TxOut, params *chaincfg.Params) bool {
// Unspendable outputs are considered dust.
if txscript.IsUnspendable(txOut.PkScript) {
return true
}
// The total serialized size consists of the output and the associated
// input script to redeem it. Since there is no input script
// to redeem it yet, use the minimum size of a typical input script.
//
// Pay-to-pubkey-hash bytes breakdown:
//
// Output to hash (38 bytes):
// 2 script version, 8 value, 1 script len, 25 script
// [1 OP_DUP, 1 OP_HASH_160, 1 OP_DATA_20, 20 hash,
// 1 OP_EQUALVERIFY, 1 OP_CHECKSIG]
//
// Input with compressed pubkey (165 bytes):
// 37 prev outpoint, 16 fraud proof, 1 script len,
// 107 script [1 OP_DATA_72, 72 sig, 1 OP_DATA_33,
// 33 compressed pubkey], 4 sequence
//
// Input with uncompressed pubkey (198 bytes):
// 37 prev outpoint, 16 fraud proof, 1 script len,
// 139 script [1 OP_DATA_72, 72 sig, 1 OP_DATA_65,
// 65 compressed pubkey], 4 sequence, 1 witness
// append
//
// Pay-to-pubkey bytes breakdown:
//
// Output to compressed pubkey (46 bytes):
// 2 script version, 8 value, 1 script len, 35 script
// [1 OP_DATA_33, 33 compressed pubkey, 1 OP_CHECKSIG]
//
// Output to uncompressed pubkey (76 bytes):
// 2 script version, 8 value, 1 script len, 67 script
// [1 OP_DATA_65, 65 pubkey, 1 OP_CHECKSIG]
//
// Input (133 bytes):
// 37 prev outpoint, 16 fraud proof, 1 script len, 73
// script [1 OP_DATA_72, 72 sig], 4 sequence, 1 witness
// append
//
// Theoretically this could examine the script type of the output script
// and use a different size for the typical input script size for
// pay-to-pubkey vs pay-to-pubkey-hash inputs per the above breakdowns,
// but the only combinination which is less than the value chosen is
// a pay-to-pubkey script with a compressed pubkey, which is not very
// common.
//
// The most common scripts are pay-to-pubkey-hash, and as per the above
// breakdown, the minimum size of a p2pkh input script is 165 bytes. So
// that figure is used.
totalSize := txOut.SerializeSize() + 165
// The output is considered dust if the cost to the network to spend the
// coins is more than 1/3 of the minimum free transaction relay fee.
// minFreeTxRelayFee is in Atom/KB, so multiply by 1000 to
// convert to bytes.
//
// Using the typical values for a pay-to-pubkey-hash transaction from
// the breakdown above and the default minimum free transaction relay
// fee of 5000000, this equates to values less than 546 atoms being
// considered dust.
//
// The following is equivalent to (value/totalSize) * (1/3) * 1000
// without needing to do floating point math.
var minTxRelayFee dcrutil.Amount
switch {
case params == &chaincfg.MainNetParams:
minTxRelayFee = minTxRelayFeeMainNet
case params == &chaincfg.MainNetParams:
minTxRelayFee = minTxRelayFeeTestNet
default:
minTxRelayFee = minTxRelayFeeTestNet
}
return txOut.Value*1000/(3*int64(totalSize)) < int64(minTxRelayFee)
}
// checkPkScriptStandard performs a series of checks on a transaction ouput
// script (public key script) to ensure it is a "standard" public key script.
// A standard public key script is one that is a recognized form, and for
// multi-signature scripts, only contains from 1 to maxStandardMultiSigKeys
// public keys.
func checkPkScriptStandard(version uint16, pkScript []byte,
scriptClass txscript.ScriptClass) error {
// Only default Bitcoin-style script is standard except for
// null data outputs.
if version != wire.DefaultPkScriptVersion {
str := fmt.Sprintf("versions other than default pkscript version " +
"are currently non-standard except for provably unspendable " +
"outputs")
return txRuleError(wire.RejectNonstandard, str)
}
switch scriptClass {
case txscript.MultiSigTy:
numPubKeys, numSigs, err := txscript.CalcMultiSigStats(pkScript)
if err != nil {
str := fmt.Sprintf("multi-signature script parse "+
"failure: %v", err)
return txRuleError(wire.RejectNonstandard, str)
}
// A standard multi-signature public key script must contain
// from 1 to maxStandardMultiSigKeys public keys.
if numPubKeys < 1 {
str := "multi-signature script with no pubkeys"
return txRuleError(wire.RejectNonstandard, str)
}
if numPubKeys > maxStandardMultiSigKeys {
str := fmt.Sprintf("multi-signature script with %d "+
"public keys which is more than the allowed "+
"max of %d", numPubKeys, maxStandardMultiSigKeys)
return txRuleError(wire.RejectNonstandard, str)
}
// A standard multi-signature public key script must have at
// least 1 signature and no more signatures than available
// public keys.
if numSigs < 1 {
return txRuleError(wire.RejectNonstandard,
"multi-signature script with no signatures")
}
if numSigs > numPubKeys {
str := fmt.Sprintf("multi-signature script with %d "+
"signatures which is more than the available "+
"%d public keys", numSigs, numPubKeys)
return txRuleError(wire.RejectNonstandard, str)
}
case txscript.NonStandardTy:
return txRuleError(wire.RejectNonstandard,
"non-standard script form")
}
return nil
}
// checkTransactionStandard performs a series of checks on a transaction to
// ensure it is a "standard" transaction. A standard transaction is one that
// conforms to several additional limiting cases over what is considered a
// "sane" transaction such as having a version in the supported range, being
// finalized, conforming to more stringent size constraints, having scripts
// of recognized forms, and not containing "dust" outputs (those that are
// so small it costs more to process them than they are worth).
func (mp *txMemPool) checkTransactionStandard(tx *dcrutil.Tx, txType stake.TxType,
height int64) error {
msgTx := tx.MsgTx()
// The transaction must be a currently supported version.
if !wire.IsSupportedMsgTxVersion(msgTx) {
str := fmt.Sprintf("transaction version %d is not in the "+
"valid range of %d-%d", msgTx.Version, 1,
wire.TxVersion)
return txRuleError(wire.RejectNonstandard, str)
}
// The transaction must be finalized to be standard and therefore
// considered for inclusion in a block.
adjustedTime := mp.server.timeSource.AdjustedTime()
if !blockchain.IsFinalizedTransaction(tx, height, adjustedTime) {
return txRuleError(wire.RejectNonstandard,
"transaction is not finalized")
}
// Since extremely large transactions with a lot of inputs can cost
// almost as much to process as the sender fees, limit the maximum
// size of a transaction. This also helps mitigate CPU exhaustion
// attacks.
serializedLen := msgTx.SerializeSize()
if serializedLen > maxStandardTxSize {
str := fmt.Sprintf("transaction size of %v is larger than max "+
"allowed size of %v", serializedLen, maxStandardTxSize)
return txRuleError(wire.RejectNonstandard, str)
}
for i, txIn := range msgTx.TxIn {
// Each transaction input signature script must not exceed the
// maximum size allowed for a standard transaction. See
// the comment on maxStandardSigScriptSize for more details.
sigScriptLen := len(txIn.SignatureScript)
if sigScriptLen > maxStandardSigScriptSize {
str := fmt.Sprintf("transaction input %d: signature "+
"script size of %d bytes is large than max "+
"allowed size of %d bytes", i, sigScriptLen,
maxStandardSigScriptSize)
return txRuleError(wire.RejectNonstandard, str)
}
// Each transaction input signature script must only contain
// opcodes which push data onto the stack.
if !txscript.IsPushOnlyScript(txIn.SignatureScript) {
str := fmt.Sprintf("transaction input %d: signature "+
"script is not push only", i)
return txRuleError(wire.RejectNonstandard, str)
}
}
// None of the output public key scripts can be a non-standard script or
// be "dust" (except when the script is a null data script).
numNullDataOutputs := 0
for i, txOut := range msgTx.TxOut {
scriptClass := txscript.GetScriptClass(txOut.Version, txOut.PkScript)
err := checkPkScriptStandard(txOut.Version, txOut.PkScript, scriptClass)
if err != nil {
// Attempt to extract a reject code from the error so
// it can be retained. When not possible, fall back to
// a non standard error.
rejectCode, found := extractRejectCode(err)
if !found {
rejectCode = wire.RejectNonstandard
}
str := fmt.Sprintf("transaction output %d: %v", i, err)
return txRuleError(rejectCode, str)
}
// Accumulate the number of outputs which only carry data. For
// all other script types, ensure the output value is not
// "dust".
if scriptClass == txscript.NullDataTy {
numNullDataOutputs++
} else if isDust(txOut, mp.server.chainParams) &&
txType != stake.TxTypeSStx {
str := fmt.Sprintf("transaction output %d: payment "+
"of %d is dust", i, txOut.Value)
return txRuleError(wire.RejectDust, str)
}
}
// A standard transaction must not have more than one output script that
// only carries data. However, certain types of standard stake transactions
// are allowed to have multiple OP_RETURN outputs, so only throw an error here
// if the tx is TxTypeRegular.
if numNullDataOutputs > maxNullDataOutputs && txType == stake.TxTypeRegular {
str := "more than one transaction output in a nulldata script for a " +
"regular type tx"
return txRuleError(wire.RejectNonstandard, str)
}
return nil
}
// checkInputsStandard performs a series of checks on a transaction's inputs
// to ensure they are "standard". A standard transaction input is one that
// that consumes the expected number of elements from the stack and that number
// is the same as the output script pushes. This help prevent resource
// exhaustion attacks by "creative" use of scripts that are super expensive to
// process like OP_DUP OP_CHECKSIG OP_DROP repeated a large number of times
// followed by a final OP_TRUE.
// Decred TODO: I think this is okay, but we'll see with simnet.
func checkInputsStandard(tx *dcrutil.Tx,
txType stake.TxType,
txStore blockchain.TxStore) error {
// NOTE: The reference implementation also does a coinbase check here,
// but coinbases have already been rejected prior to calling this
// function so no need to recheck.
for i, txIn := range tx.MsgTx().TxIn {
if i == 0 && txType == stake.TxTypeSSGen {
continue
}
// It is safe to elide existence and index checks here since
// they have already been checked prior to calling this
// function.
prevOut := txIn.PreviousOutPoint
originTx := txStore[prevOut.Hash].Tx.MsgTx()
originPkScript := originTx.TxOut[prevOut.Index].PkScript
// Calculate stats for the script pair.
scriptInfo, err := txscript.CalcScriptInfo(txIn.SignatureScript,
originPkScript, true)
if err != nil {
str := fmt.Sprintf("transaction input #%d script parse "+
"failure: %v", i, err)
return txRuleError(wire.RejectNonstandard, str)
}
// A negative value for expected inputs indicates the script is
// non-standard in some way.
if scriptInfo.ExpectedInputs < 0 {
str := fmt.Sprintf("transaction input #%d expects %d "+
"inputs", i, scriptInfo.ExpectedInputs)
return txRuleError(wire.RejectNonstandard, str)
}
// The script pair is non-standard if the number of available
// inputs does not match the number of expected inputs.
if scriptInfo.NumInputs != scriptInfo.ExpectedInputs {
str := fmt.Sprintf("transaction input #%d expects %d "+
"inputs, but referenced output script provides "+
"%d", i, scriptInfo.ExpectedInputs,
scriptInfo.NumInputs)
return txRuleError(wire.RejectNonstandard, str)
}
}
return nil
}
// calcMinRequiredTxRelayFee returns the minimum transaction fee required for a
// transaction with the passed serialized size to be accepted into the memory
// pool and relayed.
func calcMinRequiredTxRelayFee(serializedSize, minRelayTxFee int64) int64 {
// Calculate the minimum fee for a transaction to be allowed into the
// mempool and relayed by scaling the base fee (which is the minimum
// free transaction relay fee). minTxRelayFee is in Atom/KB, so
// divide the transaction size by 1000 to convert to kilobytes. Also,
// integer division is used so fees only increase on full kilobyte
// boundaries.
minFee := (serializedSize * minRelayTxFee) / 1000
if minFee == 0 && minRelayTxFee > 0 {
minFee = minRelayTxFee
}
// Set the minimum fee to the maximum possible value if the calculated
// fee is not in the valid range for monetary amounts.
if minFee < 0 || minFee > dcrutil.MaxAmount {
minFee = dcrutil.MaxAmount
}
return minFee
}
// removeOrphan is the internal function which implements the public
// RemoveOrphan. See the comment for RemoveOrphan for more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) removeOrphan(txHash *chainhash.Hash) {
txmpLog.Tracef("Removing orphan transaction %v", txHash)
// Nothing to do if passed tx is not an orphan.
tx, exists := mp.orphans[*txHash]
if !exists {
return
}
// Remove the reference from the previous orphan index.
for _, txIn := range tx.MsgTx().TxIn {
originTxHash := txIn.PreviousOutPoint.Hash
if orphans, exists := mp.orphansByPrev[originTxHash]; exists {
for e := orphans.Front(); e != nil; e = e.Next() {
if e.Value.(*dcrutil.Tx) == tx {
orphans.Remove(e)
break
}
}
// Remove the map entry altogether if there are no
// longer any orphans which depend on it.
if orphans.Len() == 0 {
delete(mp.orphansByPrev, originTxHash)
}
}
}
// Remove the transaction from the orphan pool.
delete(mp.orphans, *txHash)
}
// RemoveOrphan removes the passed orphan transaction from the orphan pool and
// previous orphan index.
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveOrphan(txHash *chainhash.Hash) {
mp.Lock()
mp.removeOrphan(txHash)
mp.Unlock()
}
// limitNumOrphans limits the number of orphan transactions by evicting a random
// orphan if adding a new one would cause it to overflow the max allowed.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) limitNumOrphans() error {
if len(mp.orphans)+1 > cfg.MaxOrphanTxs && cfg.MaxOrphanTxs > 0 {
// Generate a cryptographically random hash.
randHashBytes := make([]byte, chainhash.HashSize)
_, err := rand.Read(randHashBytes)
if err != nil {
return err
}
randHashNum := new(big.Int).SetBytes(randHashBytes)
// Try to find the first entry that is greater than the random
// hash. Use the first entry (which is already pseudorandom due
// to Go's range statement over maps) as a fallback if none of
// the hashes in the orphan pool are larger than the random
// hash.
var foundHash *chainhash.Hash
for txHash := range mp.orphans {
if foundHash == nil {
foundHash = &txHash
}
txHashNum := blockchain.ShaHashToBig(&txHash)
if txHashNum.Cmp(randHashNum) > 0 {
foundHash = &txHash
break
}
}
mp.removeOrphan(foundHash)
}
return nil
}
// addOrphan adds an orphan transaction to the orphan pool.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) addOrphan(tx *dcrutil.Tx) {
// Limit the number orphan transactions to prevent memory exhaustion. A
// random orphan is evicted to make room if needed.
mp.limitNumOrphans()
mp.orphans[*tx.Sha()] = tx
for _, txIn := range tx.MsgTx().TxIn {
originTxHash := txIn.PreviousOutPoint.Hash
if mp.orphansByPrev[originTxHash] == nil {
mp.orphansByPrev[originTxHash] = list.New()
}
mp.orphansByPrev[originTxHash].PushBack(tx)
}
txmpLog.Debugf("Stored orphan transaction %v (total: %d)", tx.Sha(),
len(mp.orphans))
}
// maybeAddOrphan potentially adds an orphan to the orphan pool.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAddOrphan(tx *dcrutil.Tx) error {
// Ignore orphan transactions that are too large. This helps avoid
// a memory exhaustion attack based on sending a lot of really large
// orphans. In the case there is a valid transaction larger than this,
// it will ultimtely be rebroadcast after the parent transactions
// have been mined or otherwise received.
//
// Note that the number of orphan transactions in the orphan pool is
// also limited, so this equates to a maximum memory used of
// maxOrphanTxSize * cfg.MaxOrphanTxs (which is ~5MB using the default
// values at the time this comment was written).
serializedLen := tx.MsgTx().SerializeSize()
if serializedLen > maxOrphanTxSize {
str := fmt.Sprintf("orphan transaction size of %d bytes is "+
"larger than max allowed size of %d bytes",
serializedLen, maxOrphanTxSize)
return txRuleError(wire.RejectNonstandard, str)
}
// Add the orphan if the none of the above disqualified it.
mp.addOrphan(tx)
return nil
}
// isTransactionInPool returns whether or not the passed transaction already
// exists in the main pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) isTransactionInPool(hash *chainhash.Hash) bool {
if _, exists := mp.pool[*hash]; exists {
return true
}
return false
}
// IsTransactionInPool returns whether or not the passed transaction already
// exists in the main pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) IsTransactionInPool(hash *chainhash.Hash) bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
return mp.isTransactionInPool(hash)
}
// isOrphanInPool returns whether or not the passed transaction already exists
// in the orphan pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) isOrphanInPool(hash *chainhash.Hash) bool {
if _, exists := mp.orphans[*hash]; exists {
return true
}
return false
}
// IsOrphanInPool returns whether or not the passed transaction already exists
// in the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) IsOrphanInPool(hash *chainhash.Hash) bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
return mp.isOrphanInPool(hash)
}
// haveTransaction returns whether or not the passed transaction already exists
// in the main pool or in the orphan pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) haveTransaction(hash *chainhash.Hash) bool {
return mp.isTransactionInPool(hash) || mp.isOrphanInPool(hash)
}
// HaveTransaction returns whether or not the passed transaction already exists
// in the main pool or in the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) HaveTransaction(hash *chainhash.Hash) bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
return mp.haveTransaction(hash)
}
// haveTransactions returns whether or not the passed transactions already exist
// in the main pool or in the orphan pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) haveTransactions(hashes []*chainhash.Hash) []bool {
have := make([]bool, len(hashes))
for i := range hashes {
have[i] = mp.haveTransaction(hashes[i])
}
return have
}
// HaveTransactions returns whether or not the passed transactions already exist
// in the main pool or in the orphan pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) HaveTransactions(hashes []*chainhash.Hash) []bool {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
return mp.haveTransactions(hashes)
}
// removeTransaction is the internal function which implements the public
// RemoveTransaction. See the comment for RemoveTransaction for more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) removeTransaction(tx *dcrutil.Tx, removeRedeemers bool) {
txmpLog.Tracef("Removing transaction %v", tx.Sha())
txHash := tx.Sha()
var txType stake.TxType
if removeRedeemers {
// Remove any transactions which rely on this one.
txType = stake.DetermineTxType(tx)
tree := dcrutil.TxTreeRegular
if txType != stake.TxTypeRegular {
tree = dcrutil.TxTreeStake
}
for i := uint32(0); i < uint32(len(tx.MsgTx().TxOut)); i++ {
outpoint := wire.NewOutPoint(txHash, i, tree)
if txRedeemer, exists := mp.outpoints[*outpoint]; exists {
mp.removeTransaction(txRedeemer, true)
}
}
}
// Remove the transaction and mark the referenced outpoints as unspent
// by the pool.
if txDesc, exists := mp.pool[*txHash]; exists {
for _, txIn := range txDesc.Tx.MsgTx().TxIn {
delete(mp.outpoints, txIn.PreviousOutPoint)
}
delete(mp.pool, *txHash)
mp.lastUpdated = time.Now()
}
// Remove the transaction and its addresses from the address index.
mp.pruneTxFromAddrIndex(tx, txType)
}
// RemoveTransaction removes the passed transaction from the mempool. If
// removeRedeemers flag is set, any transactions that redeem outputs from the
// removed transaction will also be removed recursively from the mempool, as
// they would otherwise become orphan.
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveTransaction(tx *dcrutil.Tx, removeRedeemers bool) {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()