gitlab.com/SiaPrime/SiaPrime@v1.4.1/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(0)
   146  	if err != nil {
   147  		t.Error(err)
   148  	}
   149  	data = make([]byte, BlockSizeLimit)
   150  	txn.ArbitraryData[0] = data
   151  	err = txn.fitsInABlock(0)
   152  	if err != ErrTransactionTooLarge {
   153  		t.Error(err)
   154  	}
   155  
   156  	// Try a too-large transaction before and after the hardfork height.
   157  	data = make([]byte, OakHardforkTxnSizeLimit+1)
   158  	txn.ArbitraryData[0] = data
   159  	err = txn.fitsInABlock(0)
   160  	if err != nil {
   161  		t.Error(err)
   162  	}
   163  	err = txn.fitsInABlock(OakHardforkBlock)
   164  	if err != ErrTransactionTooLarge {
   165  		t.Error(err)
   166  	}
   167  }
   168  
   169  // TestTransactionFollowsMinimumValues probes the followsMinimumValues method
   170  // of the Transaction type.
   171  func TestTransactionFollowsMinimumValues(t *testing.T) {
   172  	// Start with a transaction that follows all of minimum-values rules.
   173  	txn := Transaction{
   174  		SiacoinOutputs: []SiacoinOutput{{Value: NewCurrency64(1)}},
   175  		FileContracts:  []FileContract{{Payout: NewCurrency64(1)}},
   176  		SiafundOutputs: []SiafundOutput{{Value: NewCurrency64(1)}},
   177  		MinerFees:      []Currency{NewCurrency64(1)},
   178  	}
   179  	err := txn.followsMinimumValues()
   180  	if err != nil {
   181  		t.Error(err)
   182  	}
   183  
   184  	// Try a zero value for each type.
   185  	txn.SiacoinOutputs[0].Value = ZeroCurrency
   186  	err = txn.followsMinimumValues()
   187  	if err != ErrZeroOutput {
   188  		t.Error(err)
   189  	}
   190  	txn.SiacoinOutputs[0].Value = NewCurrency64(1)
   191  	txn.FileContracts[0].Payout = ZeroCurrency
   192  	err = txn.followsMinimumValues()
   193  	if err != ErrZeroOutput {
   194  		t.Error(err)
   195  	}
   196  	txn.FileContracts[0].Payout = NewCurrency64(1)
   197  	txn.SiafundOutputs[0].Value = ZeroCurrency
   198  	err = txn.followsMinimumValues()
   199  	if err != ErrZeroOutput {
   200  		t.Error(err)
   201  	}
   202  	txn.SiafundOutputs[0].Value = NewCurrency64(1)
   203  	txn.MinerFees[0] = ZeroCurrency
   204  	err = txn.followsMinimumValues()
   205  	if err != ErrZeroMinerFee {
   206  		t.Error(err)
   207  	}
   208  	txn.MinerFees[0] = NewCurrency64(1)
   209  
   210  	// Try a non-zero value for the ClaimStart field of a siafund output.
   211  	txn.SiafundOutputs[0].ClaimStart = NewCurrency64(1)
   212  	err = txn.followsMinimumValues()
   213  	if err != ErrNonZeroClaimStart {
   214  		t.Error(err)
   215  	}
   216  	txn.SiafundOutputs[0].ClaimStart = ZeroCurrency
   217  }
   218  
   219  // TestTransactionFollowsStorageProofRules probes the followsStorageProofRules
   220  // method of the Transaction type.
   221  func TestTransactionFollowsStorageProofRules(t *testing.T) {
   222  	// Try a transaction with no storage proofs.
   223  	txn := Transaction{}
   224  	err := txn.followsStorageProofRules()
   225  	if err != nil {
   226  		t.Error(err)
   227  	}
   228  
   229  	// Try a transaction with a legal storage proof.
   230  	txn.StorageProofs = append(txn.StorageProofs, StorageProof{})
   231  	err = txn.followsStorageProofRules()
   232  	if err != nil {
   233  		t.Error(err)
   234  	}
   235  
   236  	// Try a transaction with a storage proof and a SiacoinOutput.
   237  	txn.SiacoinOutputs = append(txn.SiacoinOutputs, SiacoinOutput{})
   238  	err = txn.followsStorageProofRules()
   239  	if err != ErrStorageProofWithOutputs {
   240  		t.Error(err)
   241  	}
   242  	txn.SiacoinOutputs = nil
   243  
   244  	// Try a transaction with a storage proof and a FileContract.
   245  	txn.FileContracts = append(txn.FileContracts, FileContract{})
   246  	err = txn.followsStorageProofRules()
   247  	if err != ErrStorageProofWithOutputs {
   248  		t.Error(err)
   249  	}
   250  	txn.FileContracts = nil
   251  
   252  	// Try a transaction with a storage proof and a FileContractRevision.
   253  	txn.FileContractRevisions = append(txn.FileContractRevisions, FileContractRevision{})
   254  	err = txn.followsStorageProofRules()
   255  	if err != ErrStorageProofWithOutputs {
   256  		t.Error(err)
   257  	}
   258  	txn.FileContractRevisions = nil
   259  
   260  	// Try a transaction with a storage proof and a FileContractRevision.
   261  	txn.SiafundOutputs = append(txn.SiafundOutputs, SiafundOutput{})
   262  	err = txn.followsStorageProofRules()
   263  	if err != ErrStorageProofWithOutputs {
   264  		t.Error(err)
   265  	}
   266  	txn.SiafundOutputs = nil
   267  }
   268  
   269  // TestTransactionNoRepeats probes the noRepeats method of the Transaction
   270  // type.
   271  func TestTransactionNoRepeats(t *testing.T) {
   272  	// Try a transaction all the repeatable types but no conflicts.
   273  	txn := Transaction{
   274  		SiacoinInputs:         []SiacoinInput{{}},
   275  		StorageProofs:         []StorageProof{{}},
   276  		FileContractRevisions: []FileContractRevision{{}},
   277  		SiafundInputs:         []SiafundInput{{}},
   278  	}
   279  	txn.FileContractRevisions[0].ParentID[0] = 1 // Otherwise it will conflict with the storage proof.
   280  	err := txn.noRepeats()
   281  	if err != nil {
   282  		t.Error(err)
   283  	}
   284  
   285  	// Try a transaction double spending a siacoin output.
   286  	txn.SiacoinInputs = append(txn.SiacoinInputs, SiacoinInput{})
   287  	err = txn.noRepeats()
   288  	if err != ErrDoubleSpend {
   289  		t.Error(err)
   290  	}
   291  	txn.SiacoinInputs = txn.SiacoinInputs[:1]
   292  
   293  	// Try double spending a file contract, checking that both storage proofs
   294  	// and terminations can conflict with each other.
   295  	txn.StorageProofs = append(txn.StorageProofs, StorageProof{})
   296  	err = txn.noRepeats()
   297  	if err != ErrDoubleSpend {
   298  		t.Error(err)
   299  	}
   300  	txn.StorageProofs = txn.StorageProofs[:1]
   301  
   302  	// Have the storage proof conflict with the file contract termination.
   303  	txn.StorageProofs[0].ParentID[0] = 1
   304  	err = txn.noRepeats()
   305  	if err != ErrDoubleSpend {
   306  		t.Error(err)
   307  	}
   308  	txn.StorageProofs[0].ParentID[0] = 0
   309  
   310  	// Have the file contract termination conflict with itself.
   311  	txn.FileContractRevisions = append(txn.FileContractRevisions, FileContractRevision{})
   312  	txn.FileContractRevisions[1].ParentID[0] = 1
   313  	err = txn.noRepeats()
   314  	if err != ErrDoubleSpend {
   315  		t.Error(err)
   316  	}
   317  	txn.FileContractRevisions = txn.FileContractRevisions[:1]
   318  
   319  	// Try a transaction double spending a siafund output.
   320  	txn.SiafundInputs = append(txn.SiafundInputs, SiafundInput{})
   321  	err = txn.noRepeats()
   322  	if err != ErrDoubleSpend {
   323  		t.Error(err)
   324  	}
   325  	txn.SiafundInputs = txn.SiafundInputs[:1]
   326  }
   327  
   328  // TestValudUnlockConditions probes the validUnlockConditions function.
   329  func TestValidUnlockConditions(t *testing.T) {
   330  	// The only thing to check is the timelock.
   331  	uc := UnlockConditions{Timelock: 3}
   332  	err := validUnlockConditions(uc, 2)
   333  	if err != ErrTimelockNotSatisfied {
   334  		t.Error(err)
   335  	}
   336  	err = validUnlockConditions(uc, 3)
   337  	if err != nil {
   338  		t.Error(err)
   339  	}
   340  	err = validUnlockConditions(uc, 4)
   341  	if err != nil {
   342  		t.Error(err)
   343  	}
   344  }
   345  
   346  // TestTransactionValidUnlockConditions probes the validUnlockConditions method
   347  // of the transaction type.
   348  func TestTransactionValidUnlockConditions(t *testing.T) {
   349  	// Create a transaction with each type of valid unlock condition.
   350  	txn := Transaction{
   351  		SiacoinInputs: []SiacoinInput{
   352  			{UnlockConditions: UnlockConditions{Timelock: 3}},
   353  		},
   354  		FileContractRevisions: []FileContractRevision{
   355  			{UnlockConditions: UnlockConditions{Timelock: 3}},
   356  		},
   357  		SiafundInputs: []SiafundInput{
   358  			{UnlockConditions: UnlockConditions{Timelock: 3}},
   359  		},
   360  	}
   361  	err := txn.validUnlockConditions(4)
   362  	if err != nil {
   363  		t.Error(err)
   364  	}
   365  
   366  	// Try with illegal conditions in the siacoin inputs.
   367  	txn.SiacoinInputs[0].UnlockConditions.Timelock = 5
   368  	err = txn.validUnlockConditions(4)
   369  	if err == nil {
   370  		t.Error(err)
   371  	}
   372  	txn.SiacoinInputs[0].UnlockConditions.Timelock = 3
   373  
   374  	// Try with illegal conditions in the siafund inputs.
   375  	txn.FileContractRevisions[0].UnlockConditions.Timelock = 5
   376  	err = txn.validUnlockConditions(4)
   377  	if err == nil {
   378  		t.Error(err)
   379  	}
   380  	txn.FileContractRevisions[0].UnlockConditions.Timelock = 3
   381  
   382  	// Try with illegal conditions in the siafund inputs.
   383  	txn.SiafundInputs[0].UnlockConditions.Timelock = 5
   384  	err = txn.validUnlockConditions(4)
   385  	if err == nil {
   386  		t.Error(err)
   387  	}
   388  	txn.SiafundInputs[0].UnlockConditions.Timelock = 3
   389  }
   390  
   391  // TestTransactionStandaloneValid probes the StandaloneValid method of the
   392  // Transaction type.
   393  func TestTransactionStandaloneValid(t *testing.T) {
   394  	// Build a working transaction.
   395  	var txn Transaction
   396  	err := txn.StandaloneValid(0)
   397  	if err != nil {
   398  		t.Error(err)
   399  	}
   400  
   401  	// Violate fitsInABlock.
   402  	data := make([]byte, BlockSizeLimit)
   403  	txn.ArbitraryData = [][]byte{data}
   404  	err = txn.StandaloneValid(0)
   405  	if err == nil {
   406  		t.Error("failed to trigger fitsInABlock error")
   407  	}
   408  	txn.ArbitraryData = nil
   409  
   410  	// Violate followsStorageProofRules
   411  	txn.StorageProofs = []StorageProof{{}}
   412  	txn.SiacoinOutputs = []SiacoinOutput{{}}
   413  	txn.SiacoinOutputs[0].Value = NewCurrency64(1)
   414  	err = txn.StandaloneValid(0)
   415  	if err == nil {
   416  		t.Error("failed to trigger followsStorageProofRules error")
   417  	}
   418  	txn.StorageProofs = nil
   419  	txn.SiacoinOutputs = nil
   420  
   421  	// Violate noRepeats
   422  	txn.SiacoinInputs = []SiacoinInput{{}, {}}
   423  	err = txn.StandaloneValid(0)
   424  	if err == nil {
   425  		t.Error("failed to trigger noRepeats error")
   426  	}
   427  	txn.SiacoinInputs = nil
   428  
   429  	// Violate followsMinimumValues
   430  	txn.SiacoinOutputs = []SiacoinOutput{{}}
   431  	err = txn.StandaloneValid(0)
   432  	if err == nil {
   433  		t.Error("failed to trigger followsMinimumValues error")
   434  	}
   435  	txn.SiacoinOutputs = nil
   436  
   437  	// Violate correctFileContracts
   438  	txn.FileContracts = []FileContract{
   439  		{
   440  			Payout:      NewCurrency64(1),
   441  			WindowStart: 5,
   442  			WindowEnd:   5,
   443  		},
   444  	}
   445  	err = txn.StandaloneValid(0)
   446  	if err == nil {
   447  		t.Error("failed to trigger correctFileContracts error")
   448  	}
   449  	txn.FileContracts = nil
   450  
   451  	// Violate correctFileContractRevisions
   452  	txn.FileContractRevisions = []FileContractRevision{{}}
   453  	err = txn.StandaloneValid(0)
   454  	if err == nil {
   455  		t.Error("failed to trigger correctFileContractRevisions error")
   456  	}
   457  	txn.FileContractRevisions = nil
   458  
   459  	// Violate validUnlockConditions
   460  	txn.SiacoinInputs = []SiacoinInput{{}}
   461  	txn.SiacoinInputs[0].UnlockConditions.Timelock = 1
   462  	err = txn.StandaloneValid(0)
   463  	if err == nil {
   464  		t.Error("failed to trigger validUnlockConditions error")
   465  	}
   466  	txn.SiacoinInputs = nil
   467  
   468  	// Violate validSignatures
   469  	txn.TransactionSignatures = []TransactionSignature{{}}
   470  	err = txn.StandaloneValid(0)
   471  	if err == nil {
   472  		t.Error("failed to trigger validSignatures error")
   473  	}
   474  	txn.TransactionSignatures = nil
   475  }