github.com/NebulousLabs/Sia@v1.3.7/modules/renter/contractor/negotiate_test.go (about) 1 package contractor 2 3 import ( 4 "path/filepath" 5 "testing" 6 7 "github.com/NebulousLabs/Sia/build" 8 "github.com/NebulousLabs/Sia/crypto" 9 "github.com/NebulousLabs/Sia/modules" 10 "github.com/NebulousLabs/Sia/modules/consensus" 11 "github.com/NebulousLabs/Sia/modules/gateway" 12 "github.com/NebulousLabs/Sia/modules/miner" 13 "github.com/NebulousLabs/Sia/modules/renter/hostdb" 14 "github.com/NebulousLabs/Sia/modules/transactionpool" 15 modWallet "github.com/NebulousLabs/Sia/modules/wallet" // name conflicts with type 16 "github.com/NebulousLabs/Sia/types" 17 ) 18 19 // contractorTester contains all of the modules that are used while testing the contractor. 20 type contractorTester struct { 21 cs modules.ConsensusSet 22 gateway modules.Gateway 23 miner modules.TestMiner 24 tpool modules.TransactionPool 25 wallet modules.Wallet 26 walletKey crypto.TwofishKey 27 hdb hostDB 28 29 contractor *Contractor 30 } 31 32 // Close shuts down the contractor tester. 33 func (rt *contractorTester) Close() error { 34 errs := []error{ 35 rt.gateway.Close(), 36 rt.cs.Close(), 37 rt.tpool.Close(), 38 rt.miner.Close(), 39 rt.wallet.Close(), 40 } 41 return build.JoinErrors(errs, ": ") 42 } 43 44 // newContractorTester creates a ready-to-use contractor tester with money in the 45 // wallet. 46 func newContractorTester(name string) (*contractorTester, error) { 47 // Create the modules. 48 testdir := build.TempDir("contractor", name) 49 g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir)) 50 if err != nil { 51 return nil, err 52 } 53 cs, err := consensus.New(g, false, filepath.Join(testdir, modules.ConsensusDir)) 54 if err != nil { 55 return nil, err 56 } 57 tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir)) 58 if err != nil { 59 return nil, err 60 } 61 w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) 62 if err != nil { 63 return nil, err 64 } 65 key := crypto.GenerateTwofishKey() 66 _, err = w.Encrypt(key) 67 if err != nil { 68 return nil, err 69 } 70 err = w.Unlock(key) 71 if err != nil { 72 return nil, err 73 } 74 hdb, err := hostdb.New(g, cs, filepath.Join(testdir, modules.RenterDir)) 75 if err != nil { 76 return nil, err 77 } 78 m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) 79 if err != nil { 80 return nil, err 81 } 82 c, err := New(cs, w, tp, hdb, filepath.Join(testdir, modules.RenterDir)) 83 if err != nil { 84 return nil, err 85 } 86 87 // Assemble all pieces into a contractor tester. 88 ct := &contractorTester{ 89 cs: cs, 90 gateway: g, 91 miner: m, 92 tpool: tp, 93 wallet: w, 94 hdb: hdb, 95 96 contractor: c, 97 } 98 99 // Mine blocks until there is money in the wallet. 100 for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { 101 _, err := ct.miner.AddBlock() 102 if err != nil { 103 return nil, err 104 } 105 } 106 return ct, nil 107 } 108 109 func TestNegotiateContract(t *testing.T) { 110 if testing.Short() { 111 t.SkipNow() 112 } 113 t.Parallel() 114 ct, err := newContractorTester(t.Name()) 115 if err != nil { 116 t.Fatal(err) 117 } 118 defer ct.Close() 119 120 payout := types.NewCurrency64(1e16) 121 122 fc := types.FileContract{ 123 FileSize: 0, 124 FileMerkleRoot: crypto.Hash{}, // no proof possible without data 125 WindowStart: 100, 126 WindowEnd: 1000, 127 Payout: payout, 128 ValidProofOutputs: []types.SiacoinOutput{ 129 {Value: types.PostTax(ct.contractor.blockHeight, payout), UnlockHash: types.UnlockHash{}}, 130 {Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, 131 }, 132 MissedProofOutputs: []types.SiacoinOutput{ 133 // same as above 134 {Value: types.PostTax(ct.contractor.blockHeight, payout), UnlockHash: types.UnlockHash{}}, 135 // goes to the void, not the hostdb 136 {Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, 137 }, 138 UnlockHash: types.UnlockHash{}, 139 RevisionNumber: 0, 140 } 141 142 txnBuilder, err := ct.wallet.StartTransaction() 143 if err != nil { 144 t.Fatal(err) 145 } 146 err = txnBuilder.FundSiacoins(fc.Payout) 147 if err != nil { 148 t.Fatal(err) 149 } 150 txnBuilder.AddFileContract(fc) 151 signedTxnSet, err := txnBuilder.Sign(true) 152 if err != nil { 153 t.Fatal(err) 154 } 155 156 err = ct.tpool.AcceptTransactionSet(signedTxnSet) 157 if err != nil { 158 t.Fatal(err) 159 } 160 161 } 162 163 func TestReviseContract(t *testing.T) { 164 if testing.Short() { 165 t.SkipNow() 166 } 167 t.Parallel() 168 ct, err := newContractorTester(t.Name()) 169 if err != nil { 170 t.Fatal(err) 171 } 172 defer ct.Close() 173 174 // get an address 175 ourAddr, err := ct.wallet.NextAddress() 176 if err != nil { 177 t.Fatal(err) 178 } 179 180 // generate keys 181 sk, pk := crypto.GenerateKeyPair() 182 renterPubKey := types.SiaPublicKey{ 183 Algorithm: types.SignatureEd25519, 184 Key: pk[:], 185 } 186 187 uc := types.UnlockConditions{ 188 PublicKeys: []types.SiaPublicKey{renterPubKey, renterPubKey}, 189 SignaturesRequired: 1, 190 } 191 192 // create file contract 193 payout := types.NewCurrency64(1e16) 194 195 fc := types.FileContract{ 196 FileSize: 0, 197 FileMerkleRoot: crypto.Hash{}, // no proof possible without data 198 WindowStart: 100, 199 WindowEnd: 1000, 200 Payout: payout, 201 UnlockHash: uc.UnlockHash(), 202 RevisionNumber: 0, 203 } 204 // outputs need account for tax 205 fc.ValidProofOutputs = []types.SiacoinOutput{ 206 {Value: types.PostTax(ct.contractor.blockHeight, payout), UnlockHash: ourAddr.UnlockHash()}, 207 {Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, // no collateral 208 } 209 fc.MissedProofOutputs = []types.SiacoinOutput{ 210 // same as above 211 fc.ValidProofOutputs[0], 212 // goes to the void, not the hostdb 213 {Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, 214 } 215 216 txnBuilder, err := ct.wallet.StartTransaction() 217 if err != nil { 218 t.Fatal(err) 219 } 220 err = txnBuilder.FundSiacoins(fc.Payout) 221 if err != nil { 222 t.Fatal(err) 223 } 224 txnBuilder.AddFileContract(fc) 225 signedTxnSet, err := txnBuilder.Sign(true) 226 if err != nil { 227 t.Fatal(err) 228 } 229 230 // submit contract 231 err = ct.tpool.AcceptTransactionSet(signedTxnSet) 232 if err != nil { 233 t.Fatal(err) 234 } 235 236 // create revision 237 fcid := signedTxnSet[len(signedTxnSet)-1].FileContractID(0) 238 rev := types.FileContractRevision{ 239 ParentID: fcid, 240 UnlockConditions: uc, 241 NewFileSize: 10, 242 NewWindowStart: 100, 243 NewWindowEnd: 1000, 244 NewRevisionNumber: 1, 245 NewValidProofOutputs: fc.ValidProofOutputs, 246 NewMissedProofOutputs: fc.MissedProofOutputs, 247 } 248 249 // create transaction containing the revision 250 signedTxn := types.Transaction{ 251 FileContractRevisions: []types.FileContractRevision{rev}, 252 TransactionSignatures: []types.TransactionSignature{{ 253 ParentID: crypto.Hash(fcid), 254 CoveredFields: types.CoveredFields{FileContractRevisions: []uint64{0}}, 255 PublicKeyIndex: 0, // hostdb key is always first -- see negotiateContract 256 }}, 257 } 258 259 // sign the transaction 260 encodedSig := crypto.SignHash(signedTxn.SigHash(0), sk) 261 signedTxn.TransactionSignatures[0].Signature = encodedSig[:] 262 263 err = signedTxn.StandaloneValid(ct.contractor.blockHeight) 264 if err != nil { 265 t.Fatal(err) 266 } 267 268 // submit revision 269 err = ct.tpool.AcceptTransactionSet([]types.Transaction{signedTxn}) 270 if err != nil { 271 t.Fatal(err) 272 } 273 }