github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/types/signatures_test.go (about) 1 package types 2 3 import ( 4 "testing" 5 6 "github.com/NebulousLabs/Sia/crypto" 7 ) 8 9 // TestUnlockHash runs the UnlockHash code. 10 func TestUnlockHash(t *testing.T) { 11 uc := UnlockConditions{ 12 Timelock: 1, 13 PublicKeys: []SiaPublicKey{ 14 { 15 Algorithm: SignatureEntropy, 16 Key: []byte{'f', 'a', 'k', 'e'}, 17 }, 18 }, 19 SignaturesRequired: 3, 20 } 21 22 _ = uc.UnlockHash() 23 } 24 25 // TestSigHash runs the SigHash function of the transaction type. 26 func TestSigHash(t *testing.T) { 27 txn := Transaction{ 28 SiacoinInputs: []SiacoinInput{{}}, 29 SiacoinOutputs: []SiacoinOutput{{}}, 30 FileContracts: []FileContract{{}}, 31 FileContractRevisions: []FileContractRevision{{}}, 32 StorageProofs: []StorageProof{{}}, 33 SiafundInputs: []SiafundInput{{}}, 34 SiafundOutputs: []SiafundOutput{{}}, 35 MinerFees: []Currency{{}}, 36 ArbitraryData: [][]byte{{'o'}, {'t'}}, 37 TransactionSignatures: []TransactionSignature{ 38 { 39 CoveredFields: CoveredFields{ 40 WholeTransaction: true, 41 }, 42 }, 43 { 44 CoveredFields: CoveredFields{ 45 SiacoinInputs: []uint64{0}, 46 SiacoinOutputs: []uint64{0}, 47 FileContracts: []uint64{0}, 48 FileContractRevisions: []uint64{0}, 49 StorageProofs: []uint64{0}, 50 SiafundInputs: []uint64{0}, 51 SiafundOutputs: []uint64{0}, 52 MinerFees: []uint64{0}, 53 ArbitraryData: []uint64{0}, 54 TransactionSignatures: []uint64{0}, 55 }, 56 }, 57 }, 58 } 59 60 _ = txn.SigHash(0) 61 _ = txn.SigHash(1) 62 63 } 64 65 // TestSortedUnique probes the sortedUnique function. 66 func TestSortedUnique(t *testing.T) { 67 su := []uint64{3, 5, 6, 8, 12} 68 if !sortedUnique(su, 13) { 69 t.Error("sortedUnique rejected a valid array") 70 } 71 if sortedUnique(su, 12) { 72 t.Error("sortedUnique accepted an invalid max") 73 } 74 if sortedUnique(su, 11) { 75 t.Error("sortedUnique accepted an invalid max") 76 } 77 78 unsorted := []uint64{3, 5, 3} 79 if sortedUnique(unsorted, 6) { 80 t.Error("sortedUnique accepted an unsorted array") 81 } 82 83 repeats := []uint64{2, 4, 4, 7} 84 if sortedUnique(repeats, 8) { 85 t.Error("sortedUnique accepted an array with repeats") 86 } 87 88 bothFlaws := []uint64{2, 3, 4, 5, 6, 6, 4} 89 if sortedUnique(bothFlaws, 7) { 90 t.Error("Sorted unique accetped array with multiple flaws") 91 } 92 } 93 94 // TestTransactionValidCoveredFields probes the validCoveredFields menthod of 95 // the transaction type. 96 func TestTransactionValidCoveredFields(t *testing.T) { 97 if testing.Short() { 98 t.SkipNow() 99 } 100 101 // Create a transaction with all fields filled in minimally. The first 102 // check has a legal CoveredFields object with 'WholeTransaction' set. 103 txn := Transaction{ 104 SiacoinInputs: []SiacoinInput{{}}, 105 SiacoinOutputs: []SiacoinOutput{{}}, 106 FileContracts: []FileContract{{}}, 107 FileContractRevisions: []FileContractRevision{{}}, 108 StorageProofs: []StorageProof{{}}, 109 SiafundInputs: []SiafundInput{{}}, 110 SiafundOutputs: []SiafundOutput{{}}, 111 MinerFees: []Currency{{}}, 112 ArbitraryData: [][]byte{{'o'}, {'t'}}, 113 TransactionSignatures: []TransactionSignature{ 114 { 115 CoveredFields: CoveredFields{ 116 WholeTransaction: true, 117 }, 118 }, 119 }, 120 } 121 err := txn.validCoveredFields() 122 if err != nil { 123 t.Error(err) 124 } 125 126 // Second check has CoveredFields object where 'WholeTransaction' is not 127 // set. 128 txn.TransactionSignatures = append(txn.TransactionSignatures, TransactionSignature{ 129 CoveredFields: CoveredFields{ 130 SiacoinOutputs: []uint64{0}, 131 MinerFees: []uint64{0}, 132 ArbitraryData: []uint64{0}, 133 FileContractRevisions: []uint64{0}, 134 }, 135 }) 136 err = txn.validCoveredFields() 137 if err != nil { 138 t.Error(err) 139 } 140 141 // Add signature coverage to the first signature. This should not violate 142 // any rules. 143 txn.TransactionSignatures[0].CoveredFields.TransactionSignatures = []uint64{1} 144 err = txn.validCoveredFields() 145 if err != nil { 146 t.Error(err) 147 } 148 149 // Add siacoin output coverage to the first signature. This should violate 150 // rules, as the fields are not allowed to be set when 'WholeTransaction' 151 // is set. 152 txn.TransactionSignatures[0].CoveredFields.SiacoinOutputs = []uint64{0} 153 err = txn.validCoveredFields() 154 if err != ErrWholeTransactionViolation { 155 t.Error("Expecting ErrWholeTransactionViolation, got", err) 156 } 157 158 // Create a SortedUnique violation instead of a WholeTransactionViolation. 159 txn.TransactionSignatures[0].CoveredFields.SiacoinOutputs = nil 160 txn.TransactionSignatures[0].CoveredFields.TransactionSignatures = []uint64{1, 2} 161 err = txn.validCoveredFields() 162 if err != ErrSortedUniqueViolation { 163 t.Error("Expecting ErrSortedUniqueViolation, got", err) 164 } 165 } 166 167 // TestTransactionValidSignatures probes the validSignatures method of the 168 // Transaction type. 169 func TestTransactionValidSignatures(t *testing.T) { 170 // Create keys for use in signing and verifying. 171 sk, pk, err := crypto.GenerateKeyPair() 172 if err != nil { 173 t.Fatal(err) 174 } 175 176 // Create UnlockConditions with 3 keys, 2 of which are required. The first 177 // possible key is a standard signature. The second key is an unknown 178 // signature type, which should always be accepted. The final type is an 179 // entropy type, which should never be accepted. 180 uc := UnlockConditions{ 181 PublicKeys: []SiaPublicKey{ 182 {Algorithm: SignatureEd25519, Key: pk[:]}, 183 {}, 184 {Algorithm: SignatureEntropy}, 185 }, 186 SignaturesRequired: 2, 187 } 188 189 // Create a transaction with each type of unlock condition. 190 txn := Transaction{ 191 SiacoinInputs: []SiacoinInput{ 192 {UnlockConditions: uc}, 193 }, 194 FileContractRevisions: []FileContractRevision{ 195 {UnlockConditions: uc}, 196 }, 197 SiafundInputs: []SiafundInput{ 198 {UnlockConditions: uc}, 199 }, 200 } 201 txn.FileContractRevisions[0].ParentID[0] = 1 // can't overlap with other objects 202 txn.SiafundInputs[0].ParentID[0] = 2 // can't overlap with other objects 203 204 // Create the signatures that spend the output. 205 txn.TransactionSignatures = []TransactionSignature{ 206 // First signatures use cryptography. 207 { 208 Timelock: 5, 209 CoveredFields: CoveredFields{WholeTransaction: true}, 210 }, 211 { 212 CoveredFields: CoveredFields{WholeTransaction: true}, 213 }, 214 { 215 CoveredFields: CoveredFields{WholeTransaction: true}, 216 }, 217 218 // The second signatures should always work for being unrecognized 219 // types. 220 {PublicKeyIndex: 1}, 221 {PublicKeyIndex: 1}, 222 {PublicKeyIndex: 1}, 223 } 224 txn.TransactionSignatures[1].ParentID[0] = 1 225 txn.TransactionSignatures[2].ParentID[0] = 2 226 txn.TransactionSignatures[4].ParentID[0] = 1 227 txn.TransactionSignatures[5].ParentID[0] = 2 228 sigHash0 := txn.SigHash(0) 229 sigHash1 := txn.SigHash(1) 230 sigHash2 := txn.SigHash(2) 231 sig0, err := crypto.SignHash(sigHash0, sk) 232 if err != nil { 233 t.Fatal(err) 234 } 235 sig1, err := crypto.SignHash(sigHash1, sk) 236 if err != nil { 237 t.Fatal(err) 238 } 239 sig2, err := crypto.SignHash(sigHash2, sk) 240 if err != nil { 241 t.Fatal(err) 242 } 243 txn.TransactionSignatures[0].Signature = sig0[:] 244 txn.TransactionSignatures[1].Signature = sig1[:] 245 txn.TransactionSignatures[2].Signature = sig2[:] 246 247 // Check that the signing was successful. 248 err = txn.validSignatures(10) 249 if err != nil { 250 t.Error(err) 251 } 252 253 // Corrupt one of the sigantures. 254 sig0[0]++ 255 txn.TransactionSignatures[0].Signature = sig0[:] 256 err = txn.validSignatures(10) 257 if err == nil { 258 t.Error("Corrupted a signature but the txn was still accepted as valid!") 259 } 260 sig0[0]-- 261 txn.TransactionSignatures[0].Signature = sig0[:] 262 263 // Fail the validCoveredFields check. 264 txn.TransactionSignatures[0].CoveredFields.SiacoinInputs = []uint64{33} 265 err = txn.validSignatures(10) 266 if err == nil { 267 t.Error("failed to flunk the validCoveredFields check") 268 } 269 txn.TransactionSignatures[0].CoveredFields.SiacoinInputs = nil 270 271 // Double spend a SiacoinInput, FileContractTermination, and SiafundInput. 272 txn.SiacoinInputs = append(txn.SiacoinInputs, SiacoinInput{UnlockConditions: UnlockConditions{}}) 273 err = txn.validSignatures(10) 274 if err == nil { 275 t.Error("failed to double spend a siacoin input") 276 } 277 txn.SiacoinInputs = txn.SiacoinInputs[:len(txn.SiacoinInputs)-1] 278 txn.FileContractRevisions = append(txn.FileContractRevisions, FileContractRevision{UnlockConditions: UnlockConditions{}}) 279 err = txn.validSignatures(10) 280 if err == nil { 281 t.Error("failed to double spend a file contract termination") 282 } 283 txn.FileContractRevisions = txn.FileContractRevisions[:len(txn.FileContractRevisions)-1] 284 txn.SiafundInputs = append(txn.SiafundInputs, SiafundInput{UnlockConditions: UnlockConditions{}}) 285 err = txn.validSignatures(10) 286 if err == nil { 287 t.Error("failed to double spend a siafund input") 288 } 289 txn.SiafundInputs = txn.SiafundInputs[:len(txn.SiafundInputs)-1] 290 291 // Add a frivilous signature 292 txn.TransactionSignatures = append(txn.TransactionSignatures, TransactionSignature{}) 293 err = txn.validSignatures(10) 294 if err != ErrFrivilousSignature { 295 t.Error(err) 296 } 297 txn.TransactionSignatures = txn.TransactionSignatures[:len(txn.TransactionSignatures)-1] 298 299 // Replace one of the cryptography signatures with an always-accepted 300 // signature. This should get rejected because the always-accepted 301 // signature has already been used. 302 tmpTxn0 := txn.TransactionSignatures[0] 303 txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 1} 304 err = txn.validSignatures(10) 305 if err != ErrPublicKeyOveruse { 306 t.Error(err) 307 } 308 txn.TransactionSignatures[0] = tmpTxn0 309 310 // Fail the timelock check for signatures. 311 err = txn.validSignatures(4) 312 if err != ErrPrematureSignature { 313 t.Error(err) 314 } 315 316 // Try to spend an entropy signature. 317 txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 2} 318 err = txn.validSignatures(10) 319 if err != ErrEntropyKey { 320 t.Error(err) 321 } 322 txn.TransactionSignatures[0] = tmpTxn0 323 324 // Try to point to a nonexistent public key. 325 txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 5} 326 err = txn.validSignatures(10) 327 if err != ErrInvalidPubKeyIndex { 328 t.Error(err) 329 } 330 txn.TransactionSignatures[0] = tmpTxn0 331 332 // Insert a malformed public key into the transaction. 333 txn.SiacoinInputs[0].UnlockConditions.PublicKeys[0].Key = []byte{'b', 'a', 'd'} 334 err = txn.validSignatures(10) 335 if err == nil { 336 t.Error(err) 337 } 338 txn.SiacoinInputs[0].UnlockConditions.PublicKeys[0].Key = pk[:] 339 340 // Insert a malformed signature into the transaction. 341 txn.TransactionSignatures[0].Signature = []byte{'m', 'a', 'l'} 342 err = txn.validSignatures(10) 343 if err == nil { 344 t.Error(err) 345 } 346 txn.TransactionSignatures[0] = tmpTxn0 347 348 // Try to spend a transaction when not every required signature is 349 // available. 350 txn.TransactionSignatures = txn.TransactionSignatures[1:] 351 err = txn.validSignatures(10) 352 if err != ErrMissingSignatures { 353 t.Error(err) 354 } 355 }