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