github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/renter/contractor/negotiate_test.go (about) 1 package contractor 2 3 import ( 4 "path/filepath" 5 "testing" 6 7 "github.com/Synthesix/Sia/build" 8 "github.com/Synthesix/Sia/crypto" 9 "github.com/Synthesix/Sia/modules" 10 "github.com/Synthesix/Sia/modules/consensus" 11 "github.com/Synthesix/Sia/modules/gateway" 12 "github.com/Synthesix/Sia/modules/miner" 13 "github.com/Synthesix/Sia/modules/renter/hostdb" 14 "github.com/Synthesix/Sia/modules/transactionpool" 15 modWallet "github.com/Synthesix/Sia/modules/wallet" // name conflicts with type 16 "github.com/Synthesix/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 := ct.wallet.StartTransaction() 143 err = txnBuilder.FundSiacoins(fc.Payout) 144 if err != nil { 145 t.Fatal(err) 146 } 147 txnBuilder.AddFileContract(fc) 148 signedTxnSet, err := txnBuilder.Sign(true) 149 if err != nil { 150 t.Fatal(err) 151 } 152 153 err = ct.tpool.AcceptTransactionSet(signedTxnSet) 154 if err != nil { 155 t.Fatal(err) 156 } 157 158 } 159 160 func TestReviseContract(t *testing.T) { 161 if testing.Short() { 162 t.SkipNow() 163 } 164 t.Parallel() 165 ct, err := newContractorTester(t.Name()) 166 if err != nil { 167 t.Fatal(err) 168 } 169 defer ct.Close() 170 171 // get an address 172 ourAddr, err := ct.wallet.NextAddress() 173 if err != nil { 174 t.Fatal(err) 175 } 176 177 // generate keys 178 sk, pk := crypto.GenerateKeyPair() 179 renterPubKey := types.SiaPublicKey{ 180 Algorithm: types.SignatureEd25519, 181 Key: pk[:], 182 } 183 184 uc := types.UnlockConditions{ 185 PublicKeys: []types.SiaPublicKey{renterPubKey, renterPubKey}, 186 SignaturesRequired: 1, 187 } 188 189 // create file contract 190 payout := types.NewCurrency64(1e16) 191 192 fc := types.FileContract{ 193 FileSize: 0, 194 FileMerkleRoot: crypto.Hash{}, // no proof possible without data 195 WindowStart: 100, 196 WindowEnd: 1000, 197 Payout: payout, 198 UnlockHash: uc.UnlockHash(), 199 RevisionNumber: 0, 200 } 201 // outputs need account for tax 202 fc.ValidProofOutputs = []types.SiacoinOutput{ 203 {Value: types.PostTax(ct.contractor.blockHeight, payout), UnlockHash: ourAddr.UnlockHash()}, 204 {Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, // no collateral 205 } 206 fc.MissedProofOutputs = []types.SiacoinOutput{ 207 // same as above 208 fc.ValidProofOutputs[0], 209 // goes to the void, not the hostdb 210 {Value: types.ZeroCurrency, UnlockHash: types.UnlockHash{}}, 211 } 212 213 txnBuilder := ct.wallet.StartTransaction() 214 err = txnBuilder.FundSiacoins(fc.Payout) 215 if err != nil { 216 t.Fatal(err) 217 } 218 txnBuilder.AddFileContract(fc) 219 signedTxnSet, err := txnBuilder.Sign(true) 220 if err != nil { 221 t.Fatal(err) 222 } 223 224 // submit contract 225 err = ct.tpool.AcceptTransactionSet(signedTxnSet) 226 if err != nil { 227 t.Fatal(err) 228 } 229 230 // create revision 231 fcid := signedTxnSet[len(signedTxnSet)-1].FileContractID(0) 232 rev := types.FileContractRevision{ 233 ParentID: fcid, 234 UnlockConditions: uc, 235 NewFileSize: 10, 236 NewWindowStart: 100, 237 NewWindowEnd: 1000, 238 NewRevisionNumber: 1, 239 NewValidProofOutputs: fc.ValidProofOutputs, 240 NewMissedProofOutputs: fc.MissedProofOutputs, 241 } 242 243 // create transaction containing the revision 244 signedTxn := types.Transaction{ 245 FileContractRevisions: []types.FileContractRevision{rev}, 246 TransactionSignatures: []types.TransactionSignature{{ 247 ParentID: crypto.Hash(fcid), 248 CoveredFields: types.CoveredFields{FileContractRevisions: []uint64{0}}, 249 PublicKeyIndex: 0, // hostdb key is always first -- see negotiateContract 250 }}, 251 } 252 253 // sign the transaction 254 encodedSig := crypto.SignHash(signedTxn.SigHash(0), sk) 255 signedTxn.TransactionSignatures[0].Signature = encodedSig[:] 256 257 err = signedTxn.StandaloneValid(ct.contractor.blockHeight) 258 if err != nil { 259 t.Fatal(err) 260 } 261 262 // submit revision 263 err = ct.tpool.AcceptTransactionSet([]types.Transaction{signedTxn}) 264 if err != nil { 265 t.Fatal(err) 266 } 267 }