github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/types/validtransaction_test.go (about)

     1  package types
     2  
     3  import (
     4  	"testing"
     5  )
     6  
     7  // TestTransactionCorrectFileContracts probes the correctFileContracts function
     8  // of the Transaction type.
     9  func TestTransactionCorrectFileContracts(t *testing.T) {
    10  	// Try a transaction with a FileContract that is correct.
    11  	txn := Transaction{
    12  		FileContracts: []FileContract{
    13  			{
    14  				WindowStart: 35,
    15  				WindowEnd:   40,
    16  				Payout:      NewCurrency64(1e6),
    17  				ValidProofOutputs: []SiacoinOutput{
    18  					{Value: NewCurrency64(70e3)},
    19  					{Value: NewCurrency64(900e3)},
    20  				},
    21  				MissedProofOutputs: []SiacoinOutput{
    22  					{Value: NewCurrency64(70e3)},
    23  					{Value: NewCurrency64(900e3)},
    24  				},
    25  			},
    26  		},
    27  	}
    28  	err := txn.correctFileContracts(30)
    29  	if err != nil {
    30  		t.Error(err)
    31  	}
    32  
    33  	// Try when the start height was missed.
    34  	err = txn.correctFileContracts(35)
    35  	if err != ErrFileContractWindowStartViolation {
    36  		t.Error(err)
    37  	}
    38  	err = txn.correctFileContracts(135)
    39  	if err != ErrFileContractWindowStartViolation {
    40  		t.Error(err)
    41  	}
    42  
    43  	// Try when the expiration equal to and less than the start.
    44  	txn.FileContracts[0].WindowEnd = 35
    45  	err = txn.correctFileContracts(30)
    46  	if err != ErrFileContractWindowEndViolation {
    47  		t.Error(err)
    48  	}
    49  	txn.FileContracts[0].WindowEnd = 35
    50  	err = txn.correctFileContracts(30)
    51  	if err != ErrFileContractWindowEndViolation {
    52  		t.Error(err)
    53  	}
    54  	txn.FileContracts[0].WindowEnd = 40
    55  
    56  	// Attempt under and over output sums.
    57  	txn.FileContracts[0].ValidProofOutputs[0].Value = NewCurrency64(69e3)
    58  	err = txn.correctFileContracts(30)
    59  	if err != ErrFileContractOutputSumViolation {
    60  		t.Error(err)
    61  	}
    62  	txn.FileContracts[0].ValidProofOutputs[0].Value = NewCurrency64(71e3)
    63  	err = txn.correctFileContracts(30)
    64  	if err != ErrFileContractOutputSumViolation {
    65  		t.Error(err)
    66  	}
    67  	txn.FileContracts[0].ValidProofOutputs[0].Value = NewCurrency64(70e3)
    68  
    69  	txn.FileContracts[0].MissedProofOutputs[0].Value = NewCurrency64(69e3)
    70  	err = txn.correctFileContracts(30)
    71  	if err != ErrFileContractOutputSumViolation {
    72  		t.Error(err)
    73  	}
    74  	txn.FileContracts[0].MissedProofOutputs[0].Value = NewCurrency64(71e3)
    75  	err = txn.correctFileContracts(30)
    76  	if err != ErrFileContractOutputSumViolation {
    77  		t.Error(err)
    78  	}
    79  	txn.FileContracts[0].MissedProofOutputs[0].Value = NewCurrency64(70e3)
    80  
    81  	// Try the payouts when the value of the contract is too low to incur a
    82  	// fee.
    83  	txn.FileContracts = append(txn.FileContracts, FileContract{
    84  		WindowStart: 35,
    85  		WindowEnd:   40,
    86  		Payout:      NewCurrency64(1e3),
    87  		ValidProofOutputs: []SiacoinOutput{
    88  			{Value: NewCurrency64(1e3)},
    89  		},
    90  		MissedProofOutputs: []SiacoinOutput{
    91  			{Value: NewCurrency64(1e3)},
    92  		},
    93  	})
    94  	err = txn.correctFileContracts(30)
    95  	if err != nil {
    96  		t.Error(err)
    97  	}
    98  }
    99  
   100  // TestCorrectFileContractRevisions probes the correctFileContractRevisions
   101  // method of the Transaction type.
   102  func TestCorrectFileContractRevisions(t *testing.T) {
   103  	// Try a revision that starts in the past.
   104  	txn := Transaction{
   105  		FileContractRevisions: []FileContractRevision{{}},
   106  	}
   107  	err := txn.correctFileContractRevisions(0)
   108  	if err != ErrFileContractWindowStartViolation {
   109  		t.Error(err)
   110  	}
   111  
   112  	// Try a revision that has a window which ends before it starts.
   113  	txn = Transaction{
   114  		FileContractRevisions: []FileContractRevision{
   115  			{NewWindowStart: 1},
   116  		},
   117  	}
   118  	err = txn.correctFileContractRevisions(0)
   119  	if err != ErrFileContractWindowEndViolation {
   120  		t.Error(err)
   121  	}
   122  
   123  	// Try a revision with misaligned payouts.
   124  	txn.FileContractRevisions = []FileContractRevision{
   125  		{
   126  			NewWindowStart: 1,
   127  			NewWindowEnd:   2,
   128  			NewMissedProofOutputs: []SiacoinOutput{
   129  				{Value: NewCurrency64(10)},
   130  			},
   131  		},
   132  	}
   133  	err = txn.correctFileContractRevisions(0)
   134  	if err != ErrFileContractOutputSumViolation {
   135  		t.Error("Expecting ErrFileContractOutputSumViolation:", err)
   136  	}
   137  }
   138  
   139  // TestTransactionFitsInABlock probes the fitsInABlock method of the
   140  // Transaction type.
   141  func TestTransactionFitsInABlock(t *testing.T) {
   142  	// Try a transaction that will fit in a block, followed by one that won't.
   143  	data := make([]byte, BlockSizeLimit/2)
   144  	txn := Transaction{ArbitraryData: [][]byte{data}}
   145  	err := txn.fitsInABlock()
   146  	if err != nil {
   147  		t.Error(err)
   148  	}
   149  	data = make([]byte, BlockSizeLimit)
   150  	txn.ArbitraryData[0] = data
   151  	err = txn.fitsInABlock()
   152  	if err != ErrTransactionTooLarge {
   153  		t.Error(err)
   154  	}
   155  }
   156  
   157  // TestTransactionFollowsMinimumValues probes the followsMinimumValues method
   158  // of the Transaction type.
   159  func TestTransactionFollowsMinimumValues(t *testing.T) {
   160  	// Start with a transaction that follows all of minimum-values rules.
   161  	txn := Transaction{
   162  		SiacoinOutputs: []SiacoinOutput{{Value: NewCurrency64(1)}},
   163  		FileContracts:  []FileContract{{Payout: NewCurrency64(1)}},
   164  		SiafundOutputs: []SiafundOutput{{Value: NewCurrency64(1)}},
   165  		MinerFees:      []Currency{NewCurrency64(1)},
   166  	}
   167  	err := txn.followsMinimumValues()
   168  	if err != nil {
   169  		t.Error(err)
   170  	}
   171  
   172  	// Try a zero value for each type.
   173  	txn.SiacoinOutputs[0].Value = ZeroCurrency
   174  	err = txn.followsMinimumValues()
   175  	if err != ErrZeroOutput {
   176  		t.Error(err)
   177  	}
   178  	txn.SiacoinOutputs[0].Value = NewCurrency64(1)
   179  	txn.FileContracts[0].Payout = ZeroCurrency
   180  	err = txn.followsMinimumValues()
   181  	if err != ErrZeroOutput {
   182  		t.Error(err)
   183  	}
   184  	txn.FileContracts[0].Payout = NewCurrency64(1)
   185  	txn.SiafundOutputs[0].Value = ZeroCurrency
   186  	err = txn.followsMinimumValues()
   187  	if err != ErrZeroOutput {
   188  		t.Error(err)
   189  	}
   190  	txn.SiafundOutputs[0].Value = NewCurrency64(1)
   191  	txn.MinerFees[0] = ZeroCurrency
   192  	err = txn.followsMinimumValues()
   193  	if err != ErrZeroMinerFee {
   194  		t.Error(err)
   195  	}
   196  	txn.MinerFees[0] = NewCurrency64(1)
   197  
   198  	// Try a non-zero value for the ClaimStart field of a siafund output.
   199  	txn.SiafundOutputs[0].ClaimStart = NewCurrency64(1)
   200  	err = txn.followsMinimumValues()
   201  	if err != ErrNonZeroClaimStart {
   202  		t.Error(err)
   203  	}
   204  	txn.SiafundOutputs[0].ClaimStart = ZeroCurrency
   205  }
   206  
   207  // TestTransactionFollowsStorageProofRules probes the followsStorageProofRules
   208  // method of the Transaction type.
   209  func TestTransactionFollowsStorageProofRules(t *testing.T) {
   210  	// Try a transaction with no storage proofs.
   211  	txn := Transaction{}
   212  	err := txn.followsStorageProofRules()
   213  	if err != nil {
   214  		t.Error(err)
   215  	}
   216  
   217  	// Try a transaction with a legal storage proof.
   218  	txn.StorageProofs = append(txn.StorageProofs, StorageProof{})
   219  	err = txn.followsStorageProofRules()
   220  	if err != nil {
   221  		t.Error(err)
   222  	}
   223  
   224  	// Try a transaction with a storage proof and a SiacoinOutput.
   225  	txn.SiacoinOutputs = append(txn.SiacoinOutputs, SiacoinOutput{})
   226  	err = txn.followsStorageProofRules()
   227  	if err != ErrStorageProofWithOutputs {
   228  		t.Error(err)
   229  	}
   230  	txn.SiacoinOutputs = nil
   231  
   232  	// Try a transaction with a storage proof and a FileContract.
   233  	txn.FileContracts = append(txn.FileContracts, FileContract{})
   234  	err = txn.followsStorageProofRules()
   235  	if err != ErrStorageProofWithOutputs {
   236  		t.Error(err)
   237  	}
   238  	txn.FileContracts = nil
   239  
   240  	// Try a transaction with a storage proof and a FileContractRevision.
   241  	txn.FileContractRevisions = append(txn.FileContractRevisions, FileContractRevision{})
   242  	err = txn.followsStorageProofRules()
   243  	if err != ErrStorageProofWithOutputs {
   244  		t.Error(err)
   245  	}
   246  	txn.FileContractRevisions = nil
   247  
   248  	// Try a transaction with a storage proof and a FileContractRevision.
   249  	txn.SiafundOutputs = append(txn.SiafundOutputs, SiafundOutput{})
   250  	err = txn.followsStorageProofRules()
   251  	if err != ErrStorageProofWithOutputs {
   252  		t.Error(err)
   253  	}
   254  	txn.SiafundOutputs = nil
   255  }
   256  
   257  // TestTransactionNoRepeats probes the noRepeats method of the Transaction
   258  // type.
   259  func TestTransactionNoRepeats(t *testing.T) {
   260  	// Try a transaction all the repeatable types but no conflicts.
   261  	txn := Transaction{
   262  		SiacoinInputs:         []SiacoinInput{{}},
   263  		StorageProofs:         []StorageProof{{}},
   264  		FileContractRevisions: []FileContractRevision{{}},
   265  		SiafundInputs:         []SiafundInput{{}},
   266  	}
   267  	txn.FileContractRevisions[0].ParentID[0] = 1 // Otherwise it will conflict with the storage proof.
   268  	err := txn.noRepeats()
   269  	if err != nil {
   270  		t.Error(err)
   271  	}
   272  
   273  	// Try a transaction double spending a siacoin output.
   274  	txn.SiacoinInputs = append(txn.SiacoinInputs, SiacoinInput{})
   275  	err = txn.noRepeats()
   276  	if err != ErrDoubleSpend {
   277  		t.Error(err)
   278  	}
   279  	txn.SiacoinInputs = txn.SiacoinInputs[:1]
   280  
   281  	// Try double spending a file contract, checking that both storage proofs
   282  	// and terminations can conflict with each other.
   283  	txn.StorageProofs = append(txn.StorageProofs, StorageProof{})
   284  	err = txn.noRepeats()
   285  	if err != ErrDoubleSpend {
   286  		t.Error(err)
   287  	}
   288  	txn.StorageProofs = txn.StorageProofs[:1]
   289  
   290  	// Have the storage proof conflict with the file contract termination.
   291  	txn.StorageProofs[0].ParentID[0] = 1
   292  	err = txn.noRepeats()
   293  	if err != ErrDoubleSpend {
   294  		t.Error(err)
   295  	}
   296  	txn.StorageProofs[0].ParentID[0] = 0
   297  
   298  	// Have the file contract termination conflict with itself.
   299  	txn.FileContractRevisions = append(txn.FileContractRevisions, FileContractRevision{})
   300  	txn.FileContractRevisions[1].ParentID[0] = 1
   301  	err = txn.noRepeats()
   302  	if err != ErrDoubleSpend {
   303  		t.Error(err)
   304  	}
   305  	txn.FileContractRevisions = txn.FileContractRevisions[:1]
   306  
   307  	// Try a transaction double spending a siafund output.
   308  	txn.SiafundInputs = append(txn.SiafundInputs, SiafundInput{})
   309  	err = txn.noRepeats()
   310  	if err != ErrDoubleSpend {
   311  		t.Error(err)
   312  	}
   313  	txn.SiafundInputs = txn.SiafundInputs[:1]
   314  }
   315  
   316  // TestValudUnlockConditions probes the validUnlockConditions function.
   317  func TestValidUnlockConditions(t *testing.T) {
   318  	// The only thing to check is the timelock.
   319  	uc := UnlockConditions{Timelock: 3}
   320  	err := validUnlockConditions(uc, 2)
   321  	if err != ErrTimelockNotSatisfied {
   322  		t.Error(err)
   323  	}
   324  	err = validUnlockConditions(uc, 3)
   325  	if err != nil {
   326  		t.Error(err)
   327  	}
   328  	err = validUnlockConditions(uc, 4)
   329  	if err != nil {
   330  		t.Error(err)
   331  	}
   332  }
   333  
   334  // TestTransactionValidUnlockConditions probes the validUnlockConditions method
   335  // of the transaction type.
   336  func TestTransactionValidUnlockConditions(t *testing.T) {
   337  	// Create a transaction with each type of valid unlock condition.
   338  	txn := Transaction{
   339  		SiacoinInputs: []SiacoinInput{
   340  			{UnlockConditions: UnlockConditions{Timelock: 3}},
   341  		},
   342  		FileContractRevisions: []FileContractRevision{
   343  			{UnlockConditions: UnlockConditions{Timelock: 3}},
   344  		},
   345  		SiafundInputs: []SiafundInput{
   346  			{UnlockConditions: UnlockConditions{Timelock: 3}},
   347  		},
   348  	}
   349  	err := txn.validUnlockConditions(4)
   350  	if err != nil {
   351  		t.Error(err)
   352  	}
   353  
   354  	// Try with illegal conditions in the siacoin inputs.
   355  	txn.SiacoinInputs[0].UnlockConditions.Timelock = 5
   356  	err = txn.validUnlockConditions(4)
   357  	if err == nil {
   358  		t.Error(err)
   359  	}
   360  	txn.SiacoinInputs[0].UnlockConditions.Timelock = 3
   361  
   362  	// Try with illegal conditions in the siafund inputs.
   363  	txn.FileContractRevisions[0].UnlockConditions.Timelock = 5
   364  	err = txn.validUnlockConditions(4)
   365  	if err == nil {
   366  		t.Error(err)
   367  	}
   368  	txn.FileContractRevisions[0].UnlockConditions.Timelock = 3
   369  
   370  	// Try with illegal conditions in the siafund inputs.
   371  	txn.SiafundInputs[0].UnlockConditions.Timelock = 5
   372  	err = txn.validUnlockConditions(4)
   373  	if err == nil {
   374  		t.Error(err)
   375  	}
   376  	txn.SiafundInputs[0].UnlockConditions.Timelock = 3
   377  }
   378  
   379  // TestTransactionStandaloneValid probes the StandaloneValid method of the
   380  // Transaction type.
   381  func TestTransactionStandaloneValid(t *testing.T) {
   382  	// Build a working transaction.
   383  	var txn Transaction
   384  	err := txn.StandaloneValid(0)
   385  	if err != nil {
   386  		t.Error(err)
   387  	}
   388  
   389  	// Violate fitsInABlock.
   390  	data := make([]byte, BlockSizeLimit)
   391  	txn.ArbitraryData = [][]byte{data}
   392  	err = txn.StandaloneValid(0)
   393  	if err == nil {
   394  		t.Error("failed to trigger fitsInABlock error")
   395  	}
   396  	txn.ArbitraryData = nil
   397  
   398  	// Violate followsStorageProofRules
   399  	txn.StorageProofs = []StorageProof{{}}
   400  	txn.SiacoinOutputs = []SiacoinOutput{{}}
   401  	txn.SiacoinOutputs[0].Value = NewCurrency64(1)
   402  	err = txn.StandaloneValid(0)
   403  	if err == nil {
   404  		t.Error("failed to trigger followsStorageProofRules error")
   405  	}
   406  	txn.StorageProofs = nil
   407  	txn.SiacoinOutputs = nil
   408  
   409  	// Violate noRepeats
   410  	txn.SiacoinInputs = []SiacoinInput{{}, {}}
   411  	err = txn.StandaloneValid(0)
   412  	if err == nil {
   413  		t.Error("failed to trigger noRepeats error")
   414  	}
   415  	txn.SiacoinInputs = nil
   416  
   417  	// Violate followsMinimumValues
   418  	txn.SiacoinOutputs = []SiacoinOutput{{}}
   419  	err = txn.StandaloneValid(0)
   420  	if err == nil {
   421  		t.Error("failed to trigger followsMinimumValues error")
   422  	}
   423  	txn.SiacoinOutputs = nil
   424  
   425  	// Violate correctFileContracts
   426  	txn.FileContracts = []FileContract{
   427  		{
   428  			Payout:      NewCurrency64(1),
   429  			WindowStart: 5,
   430  			WindowEnd:   5,
   431  		},
   432  	}
   433  	err = txn.StandaloneValid(0)
   434  	if err == nil {
   435  		t.Error("failed to trigger correctFileContracts error")
   436  	}
   437  	txn.FileContracts = nil
   438  
   439  	// Violate correctFileContractRevisions
   440  	txn.FileContractRevisions = []FileContractRevision{{}}
   441  	err = txn.StandaloneValid(0)
   442  	if err == nil {
   443  		t.Error("failed to trigger correctFileContractRevisions error")
   444  	}
   445  	txn.FileContractRevisions = nil
   446  
   447  	// Violate validUnlockConditions
   448  	txn.SiacoinInputs = []SiacoinInput{{}}
   449  	txn.SiacoinInputs[0].UnlockConditions.Timelock = 1
   450  	err = txn.StandaloneValid(0)
   451  	if err == nil {
   452  		t.Error("failed to trigger validUnlockConditions error")
   453  	}
   454  	txn.SiacoinInputs = nil
   455  
   456  	// Violate validSignatures
   457  	txn.TransactionSignatures = []TransactionSignature{{}}
   458  	err = txn.StandaloneValid(0)
   459  	if err == nil {
   460  		t.Error("failed to trigger validSignatures error")
   461  	}
   462  	txn.TransactionSignatures = nil
   463  }