gitlab.com/jokerrs1/Sia@v1.3.2/types/signatures_test.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "testing" 6 7 "github.com/NebulousLabs/Sia/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) 74 _ = txn.SigHash(1) 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 menthod 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 { 128 CoveredFields: CoveredFields{ 129 WholeTransaction: true, 130 }, 131 }, 132 }, 133 } 134 err := txn.validCoveredFields() 135 if err != nil { 136 t.Error(err) 137 } 138 139 // Second check has CoveredFields object where 'WholeTransaction' is not 140 // set. 141 txn.TransactionSignatures = append(txn.TransactionSignatures, TransactionSignature{ 142 CoveredFields: CoveredFields{ 143 SiacoinOutputs: []uint64{0}, 144 MinerFees: []uint64{0}, 145 ArbitraryData: []uint64{0}, 146 FileContractRevisions: []uint64{0}, 147 }, 148 }) 149 err = txn.validCoveredFields() 150 if err != nil { 151 t.Error(err) 152 } 153 154 // Add signature coverage to the first signature. This should not violate 155 // any rules. 156 txn.TransactionSignatures[0].CoveredFields.TransactionSignatures = []uint64{1} 157 err = txn.validCoveredFields() 158 if err != nil { 159 t.Error(err) 160 } 161 162 // Add siacoin output coverage to the first signature. This should violate 163 // rules, as the fields are not allowed to be set when 'WholeTransaction' 164 // is set. 165 txn.TransactionSignatures[0].CoveredFields.SiacoinOutputs = []uint64{0} 166 err = txn.validCoveredFields() 167 if err != ErrWholeTransactionViolation { 168 t.Error("Expecting ErrWholeTransactionViolation, got", err) 169 } 170 171 // Create a SortedUnique violation instead of a WholeTransactionViolation. 172 txn.TransactionSignatures[0].CoveredFields.SiacoinOutputs = nil 173 txn.TransactionSignatures[0].CoveredFields.TransactionSignatures = []uint64{1, 2} 174 err = txn.validCoveredFields() 175 if err != ErrSortedUniqueViolation { 176 t.Error("Expecting ErrSortedUniqueViolation, got", err) 177 } 178 } 179 180 // TestTransactionValidSignatures probes the validSignatures method of the 181 // Transaction type. 182 func TestTransactionValidSignatures(t *testing.T) { 183 // Create keys for use in signing and verifying. 184 sk, pk := crypto.GenerateKeyPair() 185 186 // Create UnlockConditions with 3 keys, 2 of which are required. The first 187 // possible key is a standard signature. The second key is an unknown 188 // signature type, which should always be accepted. The final type is an 189 // entropy type, which should never be accepted. 190 uc := UnlockConditions{ 191 PublicKeys: []SiaPublicKey{ 192 {Algorithm: SignatureEd25519, Key: pk[:]}, 193 {}, 194 {Algorithm: SignatureEntropy}, 195 }, 196 SignaturesRequired: 2, 197 } 198 199 // Create a transaction with each type of unlock condition. 200 txn := Transaction{ 201 SiacoinInputs: []SiacoinInput{ 202 {UnlockConditions: uc}, 203 }, 204 FileContractRevisions: []FileContractRevision{ 205 {UnlockConditions: uc}, 206 }, 207 SiafundInputs: []SiafundInput{ 208 {UnlockConditions: uc}, 209 }, 210 } 211 txn.FileContractRevisions[0].ParentID[0] = 1 // can't overlap with other objects 212 txn.SiafundInputs[0].ParentID[0] = 2 // can't overlap with other objects 213 214 // Create the signatures that spend the output. 215 txn.TransactionSignatures = []TransactionSignature{ 216 // First signatures use cryptography. 217 { 218 Timelock: 5, 219 CoveredFields: CoveredFields{WholeTransaction: true}, 220 }, 221 { 222 CoveredFields: CoveredFields{WholeTransaction: true}, 223 }, 224 { 225 CoveredFields: CoveredFields{WholeTransaction: true}, 226 }, 227 228 // The second signatures should always work for being unrecognized 229 // types. 230 {PublicKeyIndex: 1}, 231 {PublicKeyIndex: 1}, 232 {PublicKeyIndex: 1}, 233 } 234 txn.TransactionSignatures[1].ParentID[0] = 1 235 txn.TransactionSignatures[2].ParentID[0] = 2 236 txn.TransactionSignatures[4].ParentID[0] = 1 237 txn.TransactionSignatures[5].ParentID[0] = 2 238 sigHash0 := txn.SigHash(0) 239 sigHash1 := txn.SigHash(1) 240 sigHash2 := txn.SigHash(2) 241 sig0 := crypto.SignHash(sigHash0, sk) 242 sig1 := crypto.SignHash(sigHash1, sk) 243 sig2 := crypto.SignHash(sigHash2, sk) 244 txn.TransactionSignatures[0].Signature = sig0[:] 245 txn.TransactionSignatures[1].Signature = sig1[:] 246 txn.TransactionSignatures[2].Signature = sig2[:] 247 248 // Check that the signing was successful. 249 err := txn.validSignatures(10) 250 if err != nil { 251 t.Error(err) 252 } 253 254 // Corrupt one of the signatures. 255 sig0[0]++ 256 txn.TransactionSignatures[0].Signature = sig0[:] 257 err = txn.validSignatures(10) 258 if err == nil { 259 t.Error("Corrupted a signature but the txn was still accepted as valid!") 260 } 261 sig0[0]-- 262 txn.TransactionSignatures[0].Signature = sig0[:] 263 264 // Fail the validCoveredFields check. 265 txn.TransactionSignatures[0].CoveredFields.SiacoinInputs = []uint64{33} 266 err = txn.validSignatures(10) 267 if err == nil { 268 t.Error("failed to flunk the validCoveredFields check") 269 } 270 txn.TransactionSignatures[0].CoveredFields.SiacoinInputs = nil 271 272 // Double spend a SiacoinInput, FileContractTermination, and SiafundInput. 273 txn.SiacoinInputs = append(txn.SiacoinInputs, SiacoinInput{UnlockConditions: UnlockConditions{}}) 274 err = txn.validSignatures(10) 275 if err == nil { 276 t.Error("failed to double spend a siacoin input") 277 } 278 txn.SiacoinInputs = txn.SiacoinInputs[:len(txn.SiacoinInputs)-1] 279 txn.FileContractRevisions = append(txn.FileContractRevisions, FileContractRevision{UnlockConditions: UnlockConditions{}}) 280 err = txn.validSignatures(10) 281 if err == nil { 282 t.Error("failed to double spend a file contract termination") 283 } 284 txn.FileContractRevisions = txn.FileContractRevisions[:len(txn.FileContractRevisions)-1] 285 txn.SiafundInputs = append(txn.SiafundInputs, SiafundInput{UnlockConditions: UnlockConditions{}}) 286 err = txn.validSignatures(10) 287 if err == nil { 288 t.Error("failed to double spend a siafund input") 289 } 290 txn.SiafundInputs = txn.SiafundInputs[:len(txn.SiafundInputs)-1] 291 292 // Add a frivolous signature 293 txn.TransactionSignatures = append(txn.TransactionSignatures, TransactionSignature{}) 294 err = txn.validSignatures(10) 295 if err != ErrFrivolousSignature { 296 t.Error(err) 297 } 298 txn.TransactionSignatures = txn.TransactionSignatures[:len(txn.TransactionSignatures)-1] 299 300 // Replace one of the cryptography signatures with an always-accepted 301 // signature. This should get rejected because the always-accepted 302 // signature has already been used. 303 tmpTxn0 := txn.TransactionSignatures[0] 304 txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 1} 305 err = txn.validSignatures(10) 306 if err != ErrPublicKeyOveruse { 307 t.Error(err) 308 } 309 txn.TransactionSignatures[0] = tmpTxn0 310 311 // Fail the timelock check for signatures. 312 err = txn.validSignatures(4) 313 if err != ErrPrematureSignature { 314 t.Error(err) 315 } 316 317 // Try to spend an entropy signature. 318 txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 2} 319 err = txn.validSignatures(10) 320 if err != ErrEntropyKey { 321 t.Error(err) 322 } 323 txn.TransactionSignatures[0] = tmpTxn0 324 325 // Try to point to a nonexistent public key. 326 txn.TransactionSignatures[0] = TransactionSignature{PublicKeyIndex: 5} 327 err = txn.validSignatures(10) 328 if err != ErrInvalidPubKeyIndex { 329 t.Error(err) 330 } 331 txn.TransactionSignatures[0] = tmpTxn0 332 333 // Insert a malformed public key into the transaction. 334 txn.SiacoinInputs[0].UnlockConditions.PublicKeys[0].Key = []byte{'b', 'a', 'd'} 335 err = txn.validSignatures(10) 336 if err == nil { 337 t.Error(err) 338 } 339 txn.SiacoinInputs[0].UnlockConditions.PublicKeys[0].Key = pk[:] 340 341 // Insert a malformed signature into the transaction. 342 txn.TransactionSignatures[0].Signature = []byte{'m', 'a', 'l'} 343 err = txn.validSignatures(10) 344 if err == nil { 345 t.Error(err) 346 } 347 txn.TransactionSignatures[0] = tmpTxn0 348 349 // Try to spend a transaction when not every required signature is 350 // available. 351 txn.TransactionSignatures = txn.TransactionSignatures[1:] 352 err = txn.validSignatures(10) 353 if err != ErrMissingSignatures { 354 t.Error(err) 355 } 356 }