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