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 }