github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/transactionpool/standard.go (about) 1 package transactionpool 2 3 import ( 4 "errors" 5 6 "SiaPrime/encoding" 7 "SiaPrime/modules" 8 "SiaPrime/types" 9 ) 10 11 // standard.go adds extra rules to transactions which help preserve network 12 // health and provides flexibility for future soft forks and tweaks to the 13 // network. 14 // 15 // Rule: Transaction size is limited 16 // There is a DoS vector where large transactions can both contain many 17 // signatures, and have each signature's CoveredFields object cover a 18 // unique but large portion of the transaction. A 1mb transaction could 19 // force a verifier to hash very large volumes of data, which takes a long 20 // time on nonspecialized hardware. 21 // 22 // Rule: Foreign signature algorithms are rejected. 23 // There are plans to add newer, faster signature algorithms to Sia as the 24 // project matures and the need for increased verification speed grows. 25 // Foreign signatures are allowed into the blockchain, where they are 26 // accepted as valid. Hoewver, if there has been a soft-fork, the foreign 27 // signatures might actually be invalid. This rule protects legacy miners 28 // from including potentially invalid transactions in their blocks. 29 // 30 // Rule: The types of allowed arbitrary data are limited 31 // The arbitrary data field can be used to orchestrate soft-forks to Sia 32 // that add features. Legacy miners are at risk of creating invalid blocks 33 // if they include arbitrary data which has meanings that the legacy miner 34 // doesn't understand. 35 // 36 // Rule: The transaction set size is limited. 37 // A group of dependent transactions cannot exceed 100kb to limit how 38 // quickly the transaction pool can be filled with new transactions. 39 40 // checkUnlockConditions looks at the UnlockConditions and verifies that all 41 // public keys are recognized. Unrecognized public keys are automatically 42 // accepted as valid by the consnensus set, but rejected by the transaction 43 // pool. This allows new types of keys to be added via a softfork without 44 // alienating all of the older nodes. 45 func checkUnlockConditions(uc types.UnlockConditions) error { 46 for _, pk := range uc.PublicKeys { 47 if pk.Algorithm != types.SignatureEntropy && 48 pk.Algorithm != types.SignatureEd25519 { 49 return errors.New("unrecognized key type in transaction") 50 } 51 } 52 53 return nil 54 } 55 56 // isStandardTransaction enforces extra rules such as a transaction size limit. 57 // These rules can be altered without disrupting consensus. 58 // 59 // The size of the transaction is returned so that the transaction does not need 60 // to be encoded multiple times. 61 func isStandardTransaction(t types.Transaction) (uint64, error) { 62 // Check that the size of the transaction does not exceed the standard 63 // established in Standard.md. Larger transactions are a DOS vector, 64 // because someone can fill a large transaction with a bunch of signatures 65 // that require hashing the entire transaction. Several hundred megabytes 66 // of hashing can be required of a verifier. Enforcing this rule makes it 67 // more difficult for attackers to exploid this DOS vector, though a miner 68 // with sufficient power could still create unfriendly blocks. 69 tlen := len(encoding.Marshal(t)) 70 if tlen > modules.TransactionSizeLimit { 71 return 0, modules.ErrLargeTransaction 72 } 73 74 // Check that all public keys are of a recognized type. Need to check all 75 // of the UnlockConditions, which currently can appear in 3 separate fields 76 // of the transaction. Unrecognized types are ignored because a softfork 77 // may make certain unrecognized signatures invalid, and this node cannot 78 // tell which signatures are the invalid ones. 79 for _, sci := range t.SiacoinInputs { 80 err := checkUnlockConditions(sci.UnlockConditions) 81 if err != nil { 82 return 0, err 83 } 84 } 85 for _, fcr := range t.FileContractRevisions { 86 err := checkUnlockConditions(fcr.UnlockConditions) 87 if err != nil { 88 return 0, err 89 } 90 } 91 for _, sfi := range t.SiafundInputs { 92 err := checkUnlockConditions(sfi.UnlockConditions) 93 if err != nil { 94 return 0, err 95 } 96 } 97 98 // Check that all arbitrary data is prefixed using the recognized set of 99 // prefixes. The allowed prefixes include a 'NonSia' prefix for truly 100 // arbitrary data. Blocking all other prefixes allows arbitrary data to be 101 // used to orchestrate more complicated soft forks in the future without 102 // putting older nodes at risk of violating the new rules. 103 var prefix types.Specifier 104 for _, arb := range t.ArbitraryData { 105 // Check for a whilelisted prefix. 106 copy(prefix[:], arb) 107 if prefix == modules.PrefixHostAnnouncement || 108 prefix == modules.PrefixNonSia { 109 continue 110 } 111 112 return 0, modules.ErrInvalidArbPrefix 113 } 114 return uint64(tlen), nil 115 } 116 117 // isStandardTransactionSet checks that all transacitons of a set follow the 118 // IsStandard guidelines, and that the set as a whole follows the guidelines as 119 // well. 120 // 121 // The size of the transaction set is returned so that the encoding only needs 122 // to happen once. 123 func isStandardTransactionSet(ts []types.Transaction) (uint64, error) { 124 // Check that each transaction is acceptable, while also making sure that 125 // the size of the whole set is legal. 126 var totalSize uint64 127 for i := range ts { 128 tSize, err := isStandardTransaction(ts[i]) 129 if err != nil { 130 return 0, err 131 } 132 totalSize += tSize 133 if totalSize > modules.TransactionSetSizeLimit { 134 return 0, modules.ErrLargeTransactionSet 135 } 136 137 } 138 return totalSize, nil 139 }