github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/transactionpool/standard.go (about)

     1  package transactionpool
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/NebulousLabs/Sia/encoding"
     7  	"github.com/NebulousLabs/Sia/modules"
     8  	"github.com/NebulousLabs/Sia/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 transaciton. 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 (tp *TransactionPool) 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  func (tp *TransactionPool) IsStandardTransaction(t types.Transaction) error {
    59  	// Check that the size of the transaction does not exceed the standard
    60  	// established in Standard.md. Larger transactions are a DOS vector,
    61  	// because someone can fill a large transaction with a bunch of signatures
    62  	// that require hashing the entire transaction. Several hundred megabytes
    63  	// of hashing can be required of a verifier. Enforcing this rule makes it
    64  	// more difficult for attackers to exploid this DOS vector, though a miner
    65  	// with sufficient power could still create unfriendly blocks.
    66  	if len(encoding.Marshal(t)) > modules.TransactionSizeLimit {
    67  		return modules.ErrLargeTransaction
    68  	}
    69  
    70  	// Check that all public keys are of a recognized type. Need to check all
    71  	// of the UnlockConditions, which currently can appear in 3 separate fields
    72  	// of the transaction. Unrecognized types are ignored because a softfork
    73  	// may make certain unrecognized signatures invalid, and this node cannot
    74  	// tell which sigantures are the invalid ones.
    75  	for _, sci := range t.SiacoinInputs {
    76  		err := tp.checkUnlockConditions(sci.UnlockConditions)
    77  		if err != nil {
    78  			return err
    79  		}
    80  	}
    81  	for _, fcr := range t.FileContractRevisions {
    82  		err := tp.checkUnlockConditions(fcr.UnlockConditions)
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  	for _, sfi := range t.SiafundInputs {
    88  		err := tp.checkUnlockConditions(sfi.UnlockConditions)
    89  		if err != nil {
    90  			return err
    91  		}
    92  	}
    93  
    94  	// Check that all arbitrary data is prefixed using the recognized set of
    95  	// prefixes. The allowed prefixes include a 'NonSia' prefix for truly
    96  	// arbitrary data. Blocking all other prefixes allows arbitrary data to be
    97  	// used to orchestrate more complicated soft forks in the future without
    98  	// putting older nodes at risk of violating the new rules.
    99  	var prefix types.Specifier
   100  	for _, arb := range t.ArbitraryData {
   101  		// Check for a whilelisted prefix.
   102  		copy(prefix[:], arb)
   103  		if prefix == modules.PrefixHostAnnouncement ||
   104  			prefix == modules.PrefixNonSia {
   105  			continue
   106  		}
   107  
   108  		return modules.ErrInvalidArbPrefix
   109  	}
   110  	return nil
   111  }
   112  
   113  // IsStandardTransactionSet checks that all transacitons of a set follow the
   114  // IsStandard guidelines, and that the set as a whole follows the guidelines as
   115  // well.
   116  func (tp *TransactionPool) IsStandardTransactionSet(ts []types.Transaction) error {
   117  	// Check that the set is a reasonable size.
   118  	totalSize := 0
   119  	for i := range ts {
   120  		totalSize += len(encoding.Marshal(ts[i]))
   121  		if totalSize > modules.TransactionSetSizeLimit {
   122  			return modules.ErrLargeTransactionSet
   123  		}
   124  	}
   125  
   126  	// Check that each transaction is acceptable.
   127  	for i := range ts {
   128  		err := tp.IsStandardTransaction(ts[i])
   129  		if err != nil {
   130  			return err
   131  		}
   132  	}
   133  	return nil
   134  }