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 }