github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/renter/proto/contractset_test.go (about)

     1  package proto
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	"SiaPrime/build"
     9  	"SiaPrime/crypto"
    10  	"SiaPrime/modules"
    11  	"SiaPrime/types"
    12  
    13  	"gitlab.com/NebulousLabs/fastrand"
    14  )
    15  
    16  // mustAcquire is a convenience function for acquiring contracts that are
    17  // known to be in the set.
    18  func (cs *ContractSet) mustAcquire(t *testing.T, id types.FileContractID) *SafeContract {
    19  	t.Helper()
    20  	c, ok := cs.Acquire(id)
    21  	if !ok {
    22  		t.Fatal("no contract with that id")
    23  	}
    24  	return c
    25  }
    26  
    27  // TestContractSet tests that the ContractSet type is safe for concurrent use.
    28  func TestContractSet(t *testing.T) {
    29  	if testing.Short() {
    30  		t.SkipNow()
    31  	}
    32  	// create contract set
    33  	testDir := build.TempDir(t.Name())
    34  	cs, err := NewContractSet(testDir, modules.ProdDependencies)
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  
    39  	header1 := contractHeader{Transaction: types.Transaction{
    40  		FileContractRevisions: []types.FileContractRevision{{
    41  			ParentID:             types.FileContractID{1},
    42  			NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
    43  			UnlockConditions: types.UnlockConditions{
    44  				PublicKeys: []types.SiaPublicKey{{}, {}},
    45  			},
    46  		}},
    47  	}}
    48  	header2 := contractHeader{Transaction: types.Transaction{
    49  		FileContractRevisions: []types.FileContractRevision{{
    50  			ParentID:             types.FileContractID{2},
    51  			NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
    52  			UnlockConditions: types.UnlockConditions{
    53  				PublicKeys: []types.SiaPublicKey{{}, {}},
    54  			},
    55  		}},
    56  	}}
    57  	id1 := header1.ID()
    58  	id2 := header2.ID()
    59  
    60  	_, err = cs.managedInsertContract(header1, []crypto.Hash{})
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  	_, err = cs.managedInsertContract(header2, []crypto.Hash{})
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  
    69  	// uncontested acquire/release
    70  	c1 := cs.mustAcquire(t, id1)
    71  	cs.Return(c1)
    72  
    73  	// 100 concurrent serialized mutations
    74  	var wg sync.WaitGroup
    75  	for i := 0; i < 100; i++ {
    76  		wg.Add(1)
    77  		go func() {
    78  			defer wg.Done()
    79  			c1 := cs.mustAcquire(t, id1)
    80  			c1.header.Transaction.FileContractRevisions[0].NewRevisionNumber++
    81  			time.Sleep(time.Duration(fastrand.Intn(100)))
    82  			cs.Return(c1)
    83  		}()
    84  	}
    85  	wg.Wait()
    86  	c1 = cs.mustAcquire(t, id1)
    87  	cs.Return(c1)
    88  	if c1.header.LastRevision().NewRevisionNumber != 100 {
    89  		t.Fatal("expected exactly 100 increments, got", c1.header.LastRevision().NewRevisionNumber)
    90  	}
    91  
    92  	// a blocked acquire shouldn't prevent a return
    93  	c1 = cs.mustAcquire(t, id1)
    94  	go func() {
    95  		time.Sleep(time.Millisecond)
    96  		cs.Return(c1)
    97  	}()
    98  	c1 = cs.mustAcquire(t, id1)
    99  	cs.Return(c1)
   100  
   101  	// delete and reinsert id2
   102  	c2 := cs.mustAcquire(t, id2)
   103  	cs.Delete(c2)
   104  	roots, err := c2.merkleRoots.merkleRoots()
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	cs.managedInsertContract(c2.header, roots)
   109  
   110  	// call all the methods in parallel haphazardly
   111  	funcs := []func(){
   112  		func() { cs.Len() },
   113  		func() { cs.IDs() },
   114  		func() { cs.View(id1); cs.View(id2) },
   115  		func() { cs.ViewAll() },
   116  		func() { cs.Return(cs.mustAcquire(t, id1)) },
   117  		func() { cs.Return(cs.mustAcquire(t, id2)) },
   118  		func() {
   119  			header3 := contractHeader{
   120  				Transaction: types.Transaction{
   121  					FileContractRevisions: []types.FileContractRevision{{
   122  						ParentID:             types.FileContractID{3},
   123  						NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
   124  						UnlockConditions: types.UnlockConditions{
   125  							PublicKeys: []types.SiaPublicKey{{}, {}},
   126  						},
   127  					}},
   128  				},
   129  			}
   130  			id3 := header3.ID()
   131  			_, err := cs.managedInsertContract(header3, []crypto.Hash{})
   132  			if err != nil {
   133  				t.Fatal(err)
   134  			}
   135  			c3 := cs.mustAcquire(t, id3)
   136  			cs.Delete(c3)
   137  		},
   138  	}
   139  	wg = sync.WaitGroup{}
   140  	for _, fn := range funcs {
   141  		wg.Add(1)
   142  		go func(fn func()) {
   143  			defer wg.Done()
   144  			for i := 0; i < 100; i++ {
   145  				time.Sleep(time.Duration(fastrand.Intn(100)))
   146  				fn()
   147  			}
   148  		}(fn)
   149  	}
   150  	wg.Wait()
   151  }