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 }