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  }