github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/types/encoding_test.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math/big"
     8  	"strings"
     9  	"testing"
    10  
    11  	"SiaPrime/crypto"
    12  	"SiaPrime/encoding"
    13  	"gitlab.com/NebulousLabs/fastrand"
    14  )
    15  
    16  func hashStr(v interface{}) string {
    17  	h := crypto.HashObject(v)
    18  	return fmt.Sprintf("%x", h[:])
    19  }
    20  
    21  // heavyBlock is a complex block that fills every possible field with data.
    22  var heavyBlock = func() Block {
    23  	b := Block{
    24  		MinerPayouts: []SiacoinOutput{
    25  			{Value: CalculateCoinbase(0)},
    26  			{Value: CalculateCoinbase(1)},
    27  		},
    28  		Transactions: []Transaction{
    29  			{
    30  				SiacoinInputs: []SiacoinInput{{
    31  					UnlockConditions: UnlockConditions{
    32  						PublicKeys: []SiaPublicKey{{
    33  							Algorithm: SignatureEd25519,
    34  							Key:       fastrand.Bytes(32),
    35  						}},
    36  						SignaturesRequired: 6,
    37  					},
    38  				}},
    39  				SiacoinOutputs: []SiacoinOutput{{
    40  					Value: NewCurrency64(20),
    41  				}},
    42  				FileContracts: []FileContract{{
    43  					FileSize:       12,
    44  					Payout:         NewCurrency64(100),
    45  					RevisionNumber: 8,
    46  					ValidProofOutputs: []SiacoinOutput{{
    47  						Value: NewCurrency64(2),
    48  					}},
    49  					MissedProofOutputs: []SiacoinOutput{{
    50  						Value: NewCurrency64(3),
    51  					}},
    52  				}},
    53  				FileContractRevisions: []FileContractRevision{{
    54  					NewFileSize:       13,
    55  					NewRevisionNumber: 9,
    56  					UnlockConditions: UnlockConditions{
    57  						PublicKeys: []SiaPublicKey{{
    58  							Algorithm: SignatureEd25519,
    59  							Key:       fastrand.Bytes(32),
    60  						}},
    61  					},
    62  					NewValidProofOutputs: []SiacoinOutput{{
    63  						Value: NewCurrency64(4),
    64  					}},
    65  					NewMissedProofOutputs: []SiacoinOutput{{
    66  						Value: NewCurrency64(5),
    67  					}},
    68  				}},
    69  				StorageProofs: []StorageProof{{
    70  					HashSet: []crypto.Hash{{}},
    71  				}},
    72  				SiafundInputs: []SiafundInput{{
    73  					UnlockConditions: UnlockConditions{
    74  						PublicKeys: []SiaPublicKey{{
    75  							Algorithm: SignatureEd25519,
    76  							Key:       fastrand.Bytes(32),
    77  						}},
    78  					},
    79  				}},
    80  				SiafundOutputs: []SiafundOutput{{
    81  					ClaimStart: NewCurrency64(99),
    82  					Value:      NewCurrency64(25),
    83  				}},
    84  				MinerFees:     []Currency{NewCurrency64(215)},
    85  				ArbitraryData: [][]byte{fastrand.Bytes(10)},
    86  				TransactionSignatures: []TransactionSignature{{
    87  					PublicKeyIndex: 5,
    88  					Timelock:       80,
    89  					CoveredFields: CoveredFields{
    90  						WholeTransaction:      true,
    91  						SiacoinInputs:         []uint64{0},
    92  						SiacoinOutputs:        []uint64{1},
    93  						FileContracts:         []uint64{2},
    94  						FileContractRevisions: []uint64{3},
    95  						StorageProofs:         []uint64{4},
    96  						SiafundInputs:         []uint64{5},
    97  						SiafundOutputs:        []uint64{6},
    98  						MinerFees:             []uint64{7},
    99  						ArbitraryData:         []uint64{8},
   100  						TransactionSignatures: []uint64{9},
   101  					},
   102  					Signature: fastrand.Bytes(32),
   103  				}},
   104  			},
   105  		},
   106  	}
   107  	fastrand.Read(b.Transactions[0].SiacoinInputs[0].ParentID[:])
   108  	fastrand.Read(b.Transactions[0].SiacoinOutputs[0].UnlockHash[:])
   109  	fastrand.Read(b.Transactions[0].FileContracts[0].FileMerkleRoot[:])
   110  	fastrand.Read(b.Transactions[0].FileContracts[0].ValidProofOutputs[0].UnlockHash[:])
   111  	fastrand.Read(b.Transactions[0].FileContracts[0].MissedProofOutputs[0].UnlockHash[:])
   112  	fastrand.Read(b.Transactions[0].FileContractRevisions[0].ParentID[:])
   113  	fastrand.Read(b.Transactions[0].FileContractRevisions[0].NewFileMerkleRoot[:])
   114  	fastrand.Read(b.Transactions[0].FileContractRevisions[0].NewValidProofOutputs[0].UnlockHash[:])
   115  	fastrand.Read(b.Transactions[0].FileContractRevisions[0].NewMissedProofOutputs[0].UnlockHash[:])
   116  	fastrand.Read(b.Transactions[0].StorageProofs[0].ParentID[:])
   117  	fastrand.Read(b.Transactions[0].StorageProofs[0].HashSet[0][:])
   118  	fastrand.Read(b.Transactions[0].StorageProofs[0].Segment[:])
   119  	fastrand.Read(b.Transactions[0].SiafundInputs[0].ParentID[:])
   120  	fastrand.Read(b.Transactions[0].SiafundInputs[0].ClaimUnlockHash[:])
   121  	fastrand.Read(b.Transactions[0].SiafundOutputs[0].UnlockHash[:])
   122  	fastrand.Read(b.Transactions[0].TransactionSignatures[0].ParentID[:])
   123  	return b
   124  }()
   125  
   126  // TestBlockEncodes probes the MarshalSia and UnmarshalSia methods of the
   127  // Block type.
   128  func TestBlockEncoding(t *testing.T) {
   129  	var decB Block
   130  	err := encoding.Unmarshal(encoding.Marshal(heavyBlock), &decB)
   131  	if err != nil {
   132  		t.Fatal(err)
   133  	}
   134  	if hashStr(heavyBlock) != hashStr(decB) {
   135  		t.Fatal("block changed after encode/decode:", heavyBlock, decB)
   136  	}
   137  }
   138  
   139  // TestBadBlock tests that a known invalid encoding is not successfully
   140  // decoded.
   141  func TestBadBlock(t *testing.T) {
   142  	badData := "000000000000000000000000000000000000000000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00"
   143  	var block Block
   144  	err := encoding.Unmarshal([]byte(badData), &block)
   145  	if err == nil {
   146  		t.Fatal("invalid block decoded successfully")
   147  	}
   148  }
   149  
   150  // TestTooLargeDecoder tests that the decoder catches allocations that are too
   151  // large.
   152  func TestTooLargeDecoder(t *testing.T) {
   153  	if testing.Short() {
   154  		t.SkipNow()
   155  	}
   156  	enc := encoding.Marshal(Block{})
   157  	// change number of transactions to large number
   158  	copy(enc[len(enc)-8:], encoding.EncUint64(^uint64(0)))
   159  	var block Block
   160  	err := encoding.Unmarshal(enc, &block)
   161  	if err == nil {
   162  		t.Fatal("expected error, got nil")
   163  	}
   164  
   165  	var arb [][]byte
   166  	for i := 0; i < 4; i++ {
   167  		arb = append(arb, make([]byte, encoding.MaxSliceSize-1))
   168  	}
   169  	block.Transactions = []Transaction{{
   170  		ArbitraryData: arb,
   171  	}}
   172  	enc = encoding.Marshal(block)
   173  	err = encoding.Unmarshal(enc, &block)
   174  	if err == nil {
   175  		t.Fatal("expected error, got nil")
   176  	}
   177  }
   178  
   179  // TestCurrencyMarshalJSON probes the MarshalJSON and UnmarshalJSON functions
   180  // of the currency type.
   181  func TestCurrencyMarshalJSON(t *testing.T) {
   182  	b30 := big.NewInt(30)
   183  	c30 := NewCurrency64(30)
   184  
   185  	bMar30, err := b30.MarshalJSON()
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	cMar30, err := c30.MarshalJSON()
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  	if !bytes.Equal(bMar30, bytes.Trim(cMar30, `"`)) {
   194  		t.Error("Currency does not match the marshalling of its math/big equivalent")
   195  	}
   196  
   197  	var cUmar30 Currency
   198  	err = cUmar30.UnmarshalJSON(cMar30)
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	if c30.Cmp(cUmar30) != 0 {
   203  		t.Error("Incorrect unmarshalling of currency type.")
   204  	}
   205  
   206  	cMar30[0] = 0
   207  	err = cUmar30.UnmarshalJSON(cMar30)
   208  	if err == nil {
   209  		t.Error("JSON decoded nonsense input")
   210  	}
   211  }
   212  
   213  // TestCurrencyMarshalSia probes the MarshalSia and UnmarshalSia functions of
   214  // the currency type.
   215  func TestCurrencyMarshalSia(t *testing.T) {
   216  	c := NewCurrency64(1656)
   217  	buf := new(bytes.Buffer)
   218  	err := c.MarshalSia(buf)
   219  	if err != nil {
   220  		t.Fatal(err)
   221  	}
   222  	var cUmar Currency
   223  	cUmar.UnmarshalSia(buf)
   224  	if c.Cmp(cUmar) != 0 {
   225  		t.Error("marshal and unmarshal mismatch for currency type")
   226  	}
   227  }
   228  
   229  // TestCurrencyString probes the String function of the currency type.
   230  func TestCurrencyString(t *testing.T) {
   231  	b := big.NewInt(7135)
   232  	c := NewCurrency64(7135)
   233  	if b.String() != c.String() {
   234  		t.Error("string function not behaving as expected")
   235  	}
   236  }
   237  
   238  // TestCurrencyScan probes the Scan function of the currency type.
   239  func TestCurrencyScan(t *testing.T) {
   240  	var c0 Currency
   241  	c1 := NewCurrency64(81293)
   242  	_, err := fmt.Sscan("81293", &c0)
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  	if c0.Cmp(c1) != 0 {
   247  		t.Error("scanned number does not equal expected value")
   248  	}
   249  	_, err = fmt.Sscan("z", &c0)
   250  	if err == nil {
   251  		t.Fatal("scan is accepting garbage input")
   252  	}
   253  }
   254  
   255  // TestCurrencyEncoding checks that a currency can encode and decode without
   256  // error.
   257  func TestCurrencyEncoding(t *testing.T) {
   258  	c := NewCurrency64(351)
   259  	cMar := encoding.Marshal(c)
   260  	var cUmar Currency
   261  	err := encoding.Unmarshal(cMar, &cUmar)
   262  	if err != nil {
   263  		t.Error("Error unmarshalling a currency:", err)
   264  	}
   265  	if cUmar.Cmp(c) != 0 {
   266  		t.Error("Marshalling and Unmarshalling a currency did not work correctly")
   267  	}
   268  }
   269  
   270  // TestNegativeCurrencyUnmarshalJSON tries to unmarshal a negative number from
   271  // JSON.
   272  func TestNegativeCurrencyUnmarshalJSON(t *testing.T) {
   273  	// Marshal a 2 digit number.
   274  	c := NewCurrency64(35)
   275  	cMar, err := c.MarshalJSON()
   276  	if err != nil {
   277  		t.Fatal(err)
   278  	}
   279  
   280  	// Change the first digit to a negative character.
   281  	cMar[0] = 45
   282  
   283  	// Try unmarshalling the negative currency.
   284  	var cNeg Currency
   285  	err = cNeg.UnmarshalJSON(cMar)
   286  	if err != ErrNegativeCurrency {
   287  		t.Error("expecting ErrNegativeCurrency:", err)
   288  	}
   289  	if cNeg.i.Sign() < 0 {
   290  		t.Error("negative currency returned")
   291  	}
   292  }
   293  
   294  // TestNegativeCurrencyScan tries to scan in a negative number and checks for
   295  // an error.
   296  func TestNegativeCurrencyScan(t *testing.T) {
   297  	var c Currency
   298  	_, err := fmt.Sscan("-23", &c)
   299  	if err != ErrNegativeCurrency {
   300  		t.Error("expecting ErrNegativeCurrency:", err)
   301  	}
   302  }
   303  
   304  // TestCurrencyUnsafeDecode tests that decoding into an existing Currency
   305  // value does not overwrite its contents.
   306  func TestCurrencyUnsafeDecode(t *testing.T) {
   307  	// Scan
   308  	backup := SiacoinPrecision.Mul64(1)
   309  	c := SiacoinPrecision
   310  	_, err := fmt.Sscan("7", &c)
   311  	if err != nil {
   312  		t.Error(err)
   313  	} else if !SiacoinPrecision.Equals(backup) {
   314  		t.Errorf("Scan changed value of SiacoinPrecision: %v -> %v", backup, SiacoinPrecision)
   315  	}
   316  
   317  	// UnmarshalSia
   318  	c = SiacoinPrecision
   319  	err = encoding.Unmarshal(encoding.Marshal(NewCurrency64(7)), &c)
   320  	if err != nil {
   321  		t.Error(err)
   322  	} else if !SiacoinPrecision.Equals(backup) {
   323  		t.Errorf("UnmarshalSia changed value of SiacoinPrecision: %v -> %v", backup, SiacoinPrecision)
   324  	}
   325  }
   326  
   327  // TestTransactionEncoding tests that optimizations applied to the encoding of
   328  // the Transaction type do not change its encoding.
   329  func TestTransactionEncoding(t *testing.T) {
   330  	var txn Transaction
   331  	if h := hashStr(txn); h != "143aa0da2b6a4ca39eee3ee50a6536d75eedff3b5ef0229a6d603afa7854d5b8" {
   332  		t.Error("encoding mismatch:", h)
   333  	}
   334  
   335  	txn = Transaction{
   336  		SiacoinInputs:         []SiacoinInput{{}},
   337  		SiacoinOutputs:        []SiacoinOutput{{}},
   338  		FileContracts:         []FileContract{{}},
   339  		FileContractRevisions: []FileContractRevision{{}},
   340  		StorageProofs:         []StorageProof{{}},
   341  		SiafundInputs:         []SiafundInput{{}},
   342  		SiafundOutputs:        []SiafundOutput{{}},
   343  		MinerFees:             []Currency{{}},
   344  		ArbitraryData:         [][]byte{{}},
   345  		TransactionSignatures: []TransactionSignature{{}},
   346  	}
   347  	if h := hashStr(txn); h != "a6c0f41cb89aaede0682ab06c1e757e12d662a0156ec878f85b935bc219fb3ca" {
   348  		t.Error("encoding mismatch:", h)
   349  	}
   350  }
   351  
   352  // TestSiacoinInputEncoding tests that optimizations applied to the encoding
   353  // of the SiacoinInput type do not change its encoding.
   354  func TestSiacoinInputEncoding(t *testing.T) {
   355  	var sci SiacoinInput
   356  	if h := hashStr(sci); h != "2f806f905436dc7c5079ad8062467266e225d8110a3c58d17628d609cb1c99d0" {
   357  		t.Error("encoding mismatch:", h)
   358  	}
   359  
   360  	sci = SiacoinInput{
   361  		ParentID:         SiacoinOutputID{1, 2, 3},
   362  		UnlockConditions: UnlockConditions{},
   363  	}
   364  	if h := hashStr(sci); h != "f172a8f5892bb2b63eff32de6fd83c132be5ad134d1227d8881632bd809ae075" {
   365  		t.Error("encoding mismatch:", h)
   366  	}
   367  }
   368  
   369  // TestSiacoinOutputEncoding tests that optimizations applied to the encoding
   370  // of the SiacoinOutput type do not change its encoding.
   371  func TestSiacoinOutputEncoding(t *testing.T) {
   372  	var sco SiacoinOutput
   373  	if h := hashStr(sco); h != "4a1931803561f431decab002e7425f0a8531d5e456a1a47fd9998a2530c0f800" {
   374  		t.Error("encoding mismatch:", h)
   375  	}
   376  
   377  	sco = SiacoinOutput{
   378  		Value:      NewCurrency64(0),
   379  		UnlockHash: UnlockHash{1, 2, 3},
   380  	}
   381  	if h := hashStr(sco); h != "32fb94ae64201f3e0a373947382367666bcf205d47a58ece9260c459986ae6fd" {
   382  		t.Error("encoding mismatch:", h)
   383  	}
   384  }
   385  
   386  // TestSiafundInputEncoding tests that optimizations applied to the encoding
   387  // of the SiafundInput type do not change its encoding.
   388  func TestSiafundInputEncoding(t *testing.T) {
   389  	var sci SiafundInput
   390  	if h := hashStr(sci); h != "978a948b1a92bcddcea382bafc7718a25f8cc49b8fb11db5d9159afa960cf70a" {
   391  		t.Error("encoding mismatch:", h)
   392  	}
   393  
   394  	sci = SiafundInput{
   395  		ParentID:         SiafundOutputID{1, 2, 3},
   396  		UnlockConditions: UnlockConditions{1, nil, 3},
   397  		ClaimUnlockHash:  UnlockHash{1, 2, 3},
   398  	}
   399  	if h := hashStr(sci); h != "1a6781ca002262e1def98e294f86dd81f866e2db9029954c64a36d20d0c6b46f" {
   400  		t.Error("encoding mismatch:", h)
   401  	}
   402  }
   403  
   404  // TestSiafundOutputEncoding tests that optimizations applied to the encoding
   405  // of the SiafundOutput type do not change its encoding.
   406  func TestSiafundOutputEncoding(t *testing.T) {
   407  	var sco SiafundOutput
   408  	if h := hashStr(sco); h != "df69a516de12056d0895fdea7a0274c5aba67091543238670513104c1af69c1f" {
   409  		t.Error("encoding mismatch:", h)
   410  	}
   411  
   412  	sco = SiafundOutput{
   413  		Value:      NewCurrency64(0),
   414  		UnlockHash: UnlockHash{1, 2, 3},
   415  		ClaimStart: NewCurrency64(4),
   416  	}
   417  	if h := hashStr(sco); h != "9524d2250b21adc76967e9f86d26a68982727329e5c42a6bf5e62504891a5176" {
   418  		t.Error("encoding mismatch:", h)
   419  	}
   420  }
   421  
   422  // TestCoveredFieldsEncoding tests that optimizations applied to the encoding
   423  // of the CoveredFields type do not change its encoding.
   424  func TestCoveredFieldsEncoding(t *testing.T) {
   425  	var cf CoveredFields
   426  	if h := hashStr(cf); h != "aecfdceb8b630b5b00668d229221f876b3be1630703c4615a642db2c666b4fd7" {
   427  		t.Error("encoding mismatch:", h)
   428  	}
   429  
   430  	cf = CoveredFields{
   431  		WholeTransaction:      true,
   432  		SiacoinInputs:         []uint64{0},
   433  		SiacoinOutputs:        []uint64{1},
   434  		FileContracts:         []uint64{2},
   435  		FileContractRevisions: []uint64{3},
   436  		StorageProofs:         []uint64{4},
   437  		SiafundInputs:         []uint64{5},
   438  		SiafundOutputs:        []uint64{6},
   439  		MinerFees:             []uint64{7},
   440  		ArbitraryData:         []uint64{8},
   441  		TransactionSignatures: []uint64{9, 10},
   442  	}
   443  	if h := hashStr(cf); h != "5b10cd6b50b09447aae02829643e62b513ce99b969a80aeb620f74e77ca9bbba" {
   444  		t.Error("encoding mismatch:", h)
   445  	}
   446  }
   447  
   448  // TestSiaPublicKeyEncoding tests that optimizations applied to the encoding
   449  // of the SiaPublicKey type do not change its encoding.
   450  func TestSiaPublicKeyEncoding(t *testing.T) {
   451  	var spk SiaPublicKey
   452  	if h := hashStr(spk); h != "19ea4a516c66775ea1f648d71f6b8fa227e8b0c1a0c9203f82c33b89c4e759b5" {
   453  		t.Error("encoding mismatch:", h)
   454  	}
   455  
   456  	spk = SiaPublicKey{
   457  		Algorithm: Specifier{1, 2, 3},
   458  		Key:       []byte{4, 5, 6},
   459  	}
   460  	if h := hashStr(spk); h != "9c781bbeebc23a1885d00e778c358f0a4bc81a82b48191449129752a380adc03" {
   461  		t.Error("encoding mismatch:", h)
   462  	}
   463  }
   464  
   465  // TestSiaPublicKeyLoadString checks that the LoadString method is the proper
   466  // inverse of the String() method, also checks that there are no stupid panics
   467  // or severe errors.
   468  func TestSiaPublicKeyLoadString(t *testing.T) {
   469  	spk := SiaPublicKey{
   470  		Algorithm: SignatureEd25519,
   471  		Key:       fastrand.Bytes(32),
   472  	}
   473  
   474  	spkString := spk.String()
   475  	var loadedSPK SiaPublicKey
   476  	loadedSPK.LoadString(spkString)
   477  	if !bytes.Equal(loadedSPK.Algorithm[:], spk.Algorithm[:]) {
   478  		t.Error("SiaPublicKey is not loading correctly")
   479  	}
   480  	if !bytes.Equal(loadedSPK.Key, spk.Key) {
   481  		t.Log(loadedSPK.Key, spk.Key)
   482  		t.Error("SiaPublicKey is not loading correctly")
   483  	}
   484  
   485  	// Try loading crappy strings.
   486  	parts := strings.Split(spkString, ":")
   487  	spk.LoadString(parts[0])
   488  	spk.LoadString(parts[0][1:])
   489  	spk.LoadString(parts[0][:1])
   490  	spk.LoadString(parts[1])
   491  	spk.LoadString(parts[1][1:])
   492  	spk.LoadString(parts[1][:1])
   493  	spk.LoadString(parts[0] + parts[1])
   494  
   495  }
   496  
   497  // TestSiaPublicKeyString does a quick check to verify that the String method
   498  // on the SiaPublicKey is producing the expected output.
   499  func TestSiaPublicKeyString(t *testing.T) {
   500  	spk := SiaPublicKey{
   501  		Algorithm: SignatureEd25519,
   502  		Key:       make([]byte, 32),
   503  	}
   504  
   505  	if spk.String() != "ed25519:0000000000000000000000000000000000000000000000000000000000000000" {
   506  		t.Error("got wrong value for spk.String():", spk.String())
   507  	}
   508  }
   509  
   510  // TestSiaPublicKeyUnmarshalJSON checks that UnmarshalJSON supports both
   511  // encodings of SiaPublicKey.
   512  func TestSiaPublicKeyUnmarshalJSON(t *testing.T) {
   513  	js1 := `{ "algorithm": "ed25519", "key": "5GhilFqVBKtSCedCZc6TIthzxvyBH9gPqqf+Z9hsfBo=" }`
   514  	var spk1 SiaPublicKey
   515  	if err := json.Unmarshal([]byte(js1), &spk1); err != nil {
   516  		t.Error(err)
   517  	}
   518  
   519  	js2 := `"ed25519:e46862945a9504ab5209e74265ce9322d873c6fc811fd80faaa7fe67d86c7c1a"`
   520  	var spk2 SiaPublicKey
   521  	var _ json.Unmarshaler = &spk2
   522  	if err := json.Unmarshal([]byte(js2), &spk2); err != nil {
   523  		t.Fatal(err)
   524  	}
   525  
   526  	if spk1.Algorithm != spk2.Algorithm {
   527  		t.Error("unmarshalled algorithms do not match")
   528  	}
   529  	if !bytes.Equal(spk1.Key, spk2.Key) {
   530  		t.Error("unmarshalled keys do not match")
   531  	}
   532  }
   533  
   534  // TestSpecifierMarshaling tests the marshaling methods of the specifier
   535  // type.
   536  func TestSpecifierMarshaling(t *testing.T) {
   537  	s1 := SpecifierClaimOutput
   538  	b, err := json.Marshal(s1)
   539  	if err != nil {
   540  		t.Fatal(err)
   541  	}
   542  	var s2 Specifier
   543  	err = json.Unmarshal(b, &s2)
   544  	if err != nil {
   545  		t.Fatal(err)
   546  	} else if s2 != s1 {
   547  		t.Fatal("mismatch:", s1, s2)
   548  	}
   549  
   550  	// invalid json
   551  	x := 3
   552  	b, _ = json.Marshal(x)
   553  	err = json.Unmarshal(b, &s2)
   554  	if err == nil {
   555  		t.Fatal("Unmarshal should have failed")
   556  	}
   557  }
   558  
   559  // TestTransactionSignatureEncoding tests that optimizations applied to the
   560  // encoding of the TransactionSignature type do not change its encoding.
   561  func TestTransactionSignatureEncoding(t *testing.T) {
   562  	var ts TransactionSignature
   563  	if h := hashStr(ts); h != "5801097b0ae98fe7cedd4569afc11c0a433f284681ad4d66dd7181293f6d2bba" {
   564  		t.Error("encoding mismatch:", h)
   565  	}
   566  
   567  	ts = TransactionSignature{
   568  		ParentID:       crypto.Hash{1, 2, 3},
   569  		PublicKeyIndex: 4,
   570  		Timelock:       5,
   571  		CoveredFields:  CoveredFields{},
   572  		Signature:      []byte{6, 7, 8},
   573  	}
   574  	if h := hashStr(ts); h != "a3ce36fd8e1d6b7e5b030cdc2630d24a44472072bbd06e94d32d11132d817db0" {
   575  		t.Error("encoding mismatch:", h)
   576  	}
   577  }
   578  
   579  // TestUnlockConditionsEncoding tests that optimizations applied to the
   580  // encoding of the UnlockConditions type do not change its encoding.
   581  func TestUnlockConditionsEncoding(t *testing.T) {
   582  	var uc UnlockConditions
   583  	if h := hashStr(uc); h != "19ea4a516c66775ea1f648d71f6b8fa227e8b0c1a0c9203f82c33b89c4e759b5" {
   584  		t.Error("encoding mismatch:", h)
   585  	}
   586  
   587  	uc = UnlockConditions{
   588  		Timelock:           1,
   589  		PublicKeys:         []SiaPublicKey{{}},
   590  		SignaturesRequired: 3,
   591  	}
   592  	if h := hashStr(uc); h != "164d3741bd274d5333ab1fe8ab641b9d25cb0e0bed8e1d7bc466b5fffc956d96" {
   593  		t.Error("encoding mismatch:", h)
   594  	}
   595  }
   596  
   597  // TestUnlockHashJSONMarshalling checks that when an unlock hash is marshalled
   598  // and unmarshalled using JSON, the result is what is expected.
   599  func TestUnlockHashJSONMarshalling(t *testing.T) {
   600  	// Create an unlock hash.
   601  	uc := UnlockConditions{
   602  		Timelock:           5,
   603  		SignaturesRequired: 3,
   604  	}
   605  	uh := uc.UnlockHash()
   606  
   607  	// Marshal the unlock hash.
   608  	marUH, err := json.Marshal(uh)
   609  	if err != nil {
   610  		t.Fatal(err)
   611  	}
   612  
   613  	// Unmarshal the unlock hash and compare to the original.
   614  	var umarUH UnlockHash
   615  	err = json.Unmarshal(marUH, &umarUH)
   616  	if err != nil {
   617  		t.Fatal(err)
   618  	}
   619  	if umarUH != uh {
   620  		t.Error("Marshalled and unmarshalled unlock hash are not equivalent")
   621  	}
   622  
   623  	// Corrupt the checksum.
   624  	marUH[36]++
   625  	err = umarUH.UnmarshalJSON(marUH)
   626  	if err != ErrInvalidUnlockHashChecksum {
   627  		t.Error("expecting an invalid checksum:", err)
   628  	}
   629  	marUH[36]--
   630  
   631  	// Try an input that's not correct hex.
   632  	marUH[7] += 100
   633  	err = umarUH.UnmarshalJSON(marUH)
   634  	if err == nil {
   635  		t.Error("Expecting error after corrupting input")
   636  	}
   637  	marUH[7] -= 100
   638  
   639  	// Try an input of the wrong length.
   640  	err = (&umarUH).UnmarshalJSON(marUH[2:])
   641  	if err != ErrUnlockHashWrongLen {
   642  		t.Error("Got wrong error:", err)
   643  	}
   644  }
   645  
   646  // TestUnlockHashStringMarshalling checks that when an unlock hash is
   647  // marshalled and unmarshalled using String and LoadString, the result is what
   648  // is expected.
   649  func TestUnlockHashStringMarshalling(t *testing.T) {
   650  	// Create an unlock hash.
   651  	uc := UnlockConditions{
   652  		Timelock:           2,
   653  		SignaturesRequired: 7,
   654  	}
   655  	uh := uc.UnlockHash()
   656  
   657  	// Marshal the unlock hash.
   658  	marUH := uh.String()
   659  
   660  	// Unmarshal the unlock hash and compare to the original.
   661  	var umarUH UnlockHash
   662  	err := umarUH.LoadString(marUH)
   663  	if err != nil {
   664  		t.Fatal(err)
   665  	}
   666  	if umarUH != uh {
   667  		t.Error("Marshalled and unmarshalled unlock hash are not equivalent")
   668  	}
   669  
   670  	// Corrupt the checksum.
   671  	byteMarUH := []byte(marUH)
   672  	byteMarUH[36]++
   673  	err = umarUH.LoadString(string(byteMarUH))
   674  	if err != ErrInvalidUnlockHashChecksum {
   675  		t.Error("expecting an invalid checksum:", err)
   676  	}
   677  	byteMarUH[36]--
   678  
   679  	// Try an input that's not correct hex.
   680  	byteMarUH[7] += 100
   681  	err = umarUH.LoadString(string(byteMarUH))
   682  	if err == nil {
   683  		t.Error("Expecting error after corrupting input")
   684  	}
   685  	byteMarUH[7] -= 100
   686  
   687  	// Try an input of the wrong length.
   688  	err = umarUH.LoadString(string(byteMarUH[2:]))
   689  	if err != ErrUnlockHashWrongLen {
   690  		t.Error("Got wrong error:", err)
   691  	}
   692  }
   693  
   694  // TestCurrencyHumanString checks that the HumanString method of the currency
   695  // type is correctly formatting values.
   696  func TestCurrencyUnits(t *testing.T) {
   697  	tests := []struct {
   698  		in  Currency
   699  		out string
   700  	}{
   701  		{NewCurrency64(1), "1 H"},
   702  		{NewCurrency64(1000), "1000 H"},
   703  		{NewCurrency64(100000000000), "100000000000 H"},
   704  		{NewCurrency64(1000000000000), "1 pS"},
   705  		{NewCurrency64(1234560000000), "1.235 pS"},
   706  		{NewCurrency64(12345600000000), "12.35 pS"},
   707  		{NewCurrency64(123456000000000), "123.5 pS"},
   708  		{NewCurrency64(1000000000000000), "1 nS"},
   709  		{NewCurrency64(1000000000000000000), "1 uS"},
   710  		{NewCurrency64(1000000000).Mul64(1000000000000), "1 mS"},
   711  		{NewCurrency64(1).Mul(SiacoinPrecision), "1 SC"},
   712  		{NewCurrency64(1000).Mul(SiacoinPrecision), "1 KS"},
   713  		{NewCurrency64(1000000).Mul(SiacoinPrecision), "1 MS"},
   714  		{NewCurrency64(1000000000).Mul(SiacoinPrecision), "1 GS"},
   715  		{NewCurrency64(1000000000000).Mul(SiacoinPrecision), "1 TS"},
   716  		{NewCurrency64(1234560000000).Mul(SiacoinPrecision), "1.235 TS"},
   717  		{NewCurrency64(1234560000000000).Mul(SiacoinPrecision), "1235 TS"},
   718  	}
   719  	for _, test := range tests {
   720  		if test.in.HumanString() != test.out {
   721  			t.Errorf("currencyUnits(%v): expected %v, got %v", test.in, test.out, test.in.HumanString())
   722  		}
   723  	}
   724  }
   725  
   726  // TestTransactionMarshalSiaSize tests that the txn.MarshalSiaSize method is
   727  // always consistent with len(encoding.Marshal(txn)).
   728  func TestTransactionMarshalSiaSize(t *testing.T) {
   729  	txn := Transaction{
   730  		SiacoinInputs:         []SiacoinInput{{}},
   731  		SiacoinOutputs:        []SiacoinOutput{{}},
   732  		FileContracts:         []FileContract{{}},
   733  		FileContractRevisions: []FileContractRevision{{}},
   734  		StorageProofs:         []StorageProof{{}},
   735  		SiafundInputs:         []SiafundInput{{}},
   736  		SiafundOutputs:        []SiafundOutput{{}},
   737  		MinerFees:             []Currency{{}},
   738  		ArbitraryData:         [][]byte{{}},
   739  		TransactionSignatures: []TransactionSignature{{}},
   740  	}
   741  	if txn.MarshalSiaSize() != len(encoding.Marshal(txn)) {
   742  		t.Errorf("sizes do not match: expected %v, got %v", len(encoding.Marshal(txn)), txn.MarshalSiaSize())
   743  	}
   744  }
   745  
   746  // TestUnlockHashScan checks if the fmt.Scanner implementation of UnlockHash
   747  // works as expected.
   748  func TestUnlockHashScan(t *testing.T) {
   749  	// Create a random unlock hash.
   750  	var uh UnlockHash
   751  	fastrand.Read(uh[:])
   752  	// Convert it to a string and parse the string using Sscan.
   753  	var scannedHash UnlockHash
   754  	fmt.Sscan(uh.String(), &scannedHash)
   755  	// Check if they are equal.
   756  	if !bytes.Equal(uh[:], scannedHash[:]) {
   757  		t.Fatal("scanned hash is not equal to original hash")
   758  	}
   759  }