github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/renter/proto/contract_test.go (about)

     1  package proto
     2  
     3  import (
     4  	"bytes"
     5  	"path/filepath"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/Synthesix/Sia/build"
    10  	"github.com/Synthesix/Sia/crypto"
    11  	"github.com/Synthesix/Sia/encoding"
    12  	"github.com/Synthesix/Sia/modules"
    13  	"github.com/Synthesix/Sia/types"
    14  )
    15  
    16  // TestContractUncommittedTxn tests that if a contract revision is left in an
    17  // uncommitted state, either version of the contract can be recovered.
    18  func TestContractUncommittedTxn(t *testing.T) {
    19  	if testing.Short() {
    20  		t.SkipNow()
    21  	}
    22  	// create contract set with one contract
    23  	dir := build.TempDir(filepath.Join("proto", t.Name()))
    24  	cs, err := NewContractSet(dir, modules.ProdDependencies)
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	initialHeader := contractHeader{
    29  		Transaction: types.Transaction{
    30  			FileContractRevisions: []types.FileContractRevision{{
    31  				NewRevisionNumber:    1,
    32  				NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
    33  				UnlockConditions: types.UnlockConditions{
    34  					PublicKeys: []types.SiaPublicKey{{}, {}},
    35  				},
    36  			}},
    37  		},
    38  	}
    39  	initialRoots := []crypto.Hash{{1}}
    40  	id := initialHeader.ID()
    41  	_, err = cs.managedInsertContract(initialHeader, initialRoots)
    42  	if err != nil {
    43  		t.Fatal(err)
    44  	}
    45  
    46  	// apply an update to the contract, but don't commit it
    47  	sc := cs.mustAcquire(t, id)
    48  	revisedHeader := contractHeader{
    49  		Transaction: types.Transaction{
    50  			FileContractRevisions: []types.FileContractRevision{{
    51  				NewRevisionNumber:    2,
    52  				NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
    53  				UnlockConditions: types.UnlockConditions{
    54  					PublicKeys: []types.SiaPublicKey{{}, {}},
    55  				},
    56  			}},
    57  		},
    58  		StorageSpending: types.NewCurrency64(7),
    59  		UploadSpending:  types.NewCurrency64(17),
    60  	}
    61  	revisedRoots := []crypto.Hash{{1}, {2}}
    62  	fcr := revisedHeader.Transaction.FileContractRevisions[0]
    63  	newRoot := revisedRoots[1]
    64  	storageCost := revisedHeader.StorageSpending.Sub(initialHeader.StorageSpending)
    65  	bandwidthCost := revisedHeader.UploadSpending.Sub(initialHeader.UploadSpending)
    66  	walTxn, err := sc.recordUploadIntent(fcr, newRoot, storageCost, bandwidthCost)
    67  	if err != nil {
    68  		t.Fatal(err)
    69  	}
    70  
    71  	// the state of the contract should match the initial state
    72  	// NOTE: can't use reflect.DeepEqual for the header because it contains
    73  	// types.Currency fields
    74  	if !bytes.Equal(encoding.Marshal(sc.header), encoding.Marshal(initialHeader)) {
    75  		t.Fatal("contractHeader should match initial contractHeader")
    76  	} else if !reflect.DeepEqual(sc.merkleRoots, initialRoots) {
    77  		t.Fatal("Merkle roots should match initial Merkle roots")
    78  	}
    79  
    80  	// close and reopen the contract set
    81  	cs.Close()
    82  	cs, err = NewContractSet(dir, modules.ProdDependencies)
    83  	if err != nil {
    84  		t.Fatal(err)
    85  	}
    86  	// the uncommitted transaction should be stored in the contract
    87  	sc = cs.mustAcquire(t, id)
    88  	if len(sc.unappliedTxns) != 1 {
    89  		t.Fatal("expected 1 unappliedTxn, got", len(sc.unappliedTxns))
    90  	} else if !bytes.Equal(sc.unappliedTxns[0].Updates[0].Instructions, walTxn.Updates[0].Instructions) {
    91  		t.Fatal("WAL transaction changed")
    92  	}
    93  	// the state of the contract should match the initial state
    94  	if !bytes.Equal(encoding.Marshal(sc.header), encoding.Marshal(initialHeader)) {
    95  		t.Fatal("contractHeader should match initial contractHeader", sc.header, initialHeader)
    96  	} else if !reflect.DeepEqual(sc.merkleRoots, initialRoots) {
    97  		t.Fatal("Merkle roots should match initial Merkle roots")
    98  	}
    99  
   100  	// apply the uncommitted transaction
   101  	err = sc.commitTxns()
   102  	if err != nil {
   103  		t.Fatal(err)
   104  	}
   105  	// the uncommitted transaction should be gone now
   106  	if len(sc.unappliedTxns) != 0 {
   107  		t.Fatal("expected 0 unappliedTxns, got", len(sc.unappliedTxns))
   108  	}
   109  	// the state of the contract should now match the revised state
   110  	if !bytes.Equal(encoding.Marshal(sc.header), encoding.Marshal(revisedHeader)) {
   111  		t.Fatal("contractHeader should match revised contractHeader", sc.header, revisedHeader)
   112  	} else if !reflect.DeepEqual(sc.merkleRoots, revisedRoots) {
   113  		t.Fatal("Merkle roots should match revised Merkle roots")
   114  	}
   115  }