// createServerTester creates a server tester object that is ready for testing, // including money in the wallet and all modules initialized. func createServerTester(name string) (*serverTester, error) { // createServerTester is expensive, and therefore should not be called // during short tests. if testing.Short() { panic("createServerTester called during short tests") } // Create the testing directory. testdir := build.TempDir("api", name) key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } st, err := assembleServerTester(key, testdir) if err != nil { return nil, err } // Mine blocks until the wallet has confirmed money. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { _, err := st.miner.AddBlock() if err != nil { return nil, err } } return st, nil }
// createAuthenticatedServerTester creates an authenticated server tester // object that is ready for testing, including money in the wallet and all // modules initalized. func createAuthenticatedServerTester(name string, password string) (*serverTester, error) { // createAuthenticatedServerTester should not get called during short // tests, as it takes a long time to run. if testing.Short() { panic("assembleServerTester called during short tests") } // Create the testing directory. testdir := build.TempDir("authenticated-api", name) key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } st, err := assembleAuthenticatedServerTester(password, key, testdir) if err != nil { return nil, err } // Mine blocks until the wallet has confirmed money. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { _, err := st.miner.AddBlock() if err != nil { return nil, err } } return st, nil }
// newRenterTester creates a ready-to-use renter tester with money in the // wallet. func newRenterTester(name string) (*renterTester, error) { // Create the modules. testdir := build.TempDir("renter", name) g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir)) if err != nil { return nil, err } cs, err := consensus.New(g, false, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { return nil, err } tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir)) if err != nil { return nil, err } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } _, err = w.Encrypt(key) if err != nil { return nil, err } err = w.Unlock(key) if err != nil { return nil, err } r, err := New(cs, w, tp, filepath.Join(testdir, modules.RenterDir)) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } // Assemble all pieces into a renter tester. rt := &renterTester{ cs: cs, gateway: g, miner: m, tpool: tp, wallet: w, renter: r, } // Mine blocks until there is money in the wallet. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { _, err := rt.miner.AddBlock() if err != nil { return nil, err } } return rt, nil }
// createExplorerTester creates a tester object for the explorer module. func createExplorerTester(name string) (*explorerTester, error) { // Create and assemble the dependencies. testdir := build.TempDir(modules.HostDir, name) g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { return nil, err } cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { return nil, err } tp, err := transactionpool.New(cs, g) if err != nil { return nil, err } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } _, err = w.Encrypt(key) if err != nil { return nil, err } err = w.Unlock(key) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.RenterDir)) if err != nil { return nil, err } e, err := New(cs, filepath.Join(testdir, modules.ExplorerDir)) if err != nil { return nil, err } et := &explorerTester{ cs: cs, gateway: g, miner: m, tpool: tp, wallet: w, walletKey: key, explorer: e, } // Mine until the wallet has money. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := et.miner.FindBlock() err = et.cs.AcceptBlock(b) if err != nil { return nil, err } } return et, nil }
// createConsensusSetTester creates a consensusSetTester that's ready for use. func createConsensusSetTester(name string) (*consensusSetTester, error) { testdir := build.TempDir(modules.ConsensusDir, name) // Create modules. g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { return nil, err } cs, err := New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { return nil, err } tp, err := transactionpool.New(cs, g) if err != nil { return nil, err } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } _, err = w.Encrypt(key) if err != nil { return nil, err } err = w.Unlock(key) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } // Assemble all objects into a consensusSetTester. cst := &consensusSetTester{ gateway: g, miner: m, tpool: tp, wallet: w, walletKey: key, cs: cs, persistDir: testdir, } // Mine until the wallet has money. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := cst.miner.FindBlock() err = cst.cs.AcceptBlock(b) if err != nil { return nil, err } } return cst, nil }
// newHostDBTester creates a ready-to-use hostdb tester with money in the // wallet. func newHostDBTester(name string) (*hostdbTester, error) { // Create the modules. testdir := build.TempDir("hostdb", name) g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { return nil, err } cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { return nil, err } tp, err := transactionpool.New(cs, g) if err != nil { return nil, err } w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } _, err = w.Encrypt(key) if err != nil { return nil, err } err = w.Unlock(key) if err != nil { return nil, err } hdb, err := New(cs, w, tp, filepath.Join(testdir, modules.RenterDir)) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } // Assemble all pieces into a hostdb tester. ht := &hostdbTester{ cs: cs, gateway: g, miner: m, tpool: tp, wallet: w, hostdb: hdb, } // Mine blocks until there is money in the wallet. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { _, err := ht.miner.AddBlock() if err != nil { return nil, err } } return ht, nil }
// newTestingWallet is a helper function that creates a ready-to-use wallet // and mines some coins into it. func newTestingWallet(testdir string, cs modules.ConsensusSet, tp modules.TransactionPool) (modules.Wallet, error) { w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } if !w.Encrypted() { _, err = w.Encrypt(key) if err != nil { return nil, err } } err = w.Unlock(key) if err != nil { return nil, err } // give it some money m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { _, err := m.AddBlock() if err != nil { return nil, err } } return w, nil }
// newFile creates a new file object. func newFile(name string, code modules.ErasureCoder, pieceSize, fileSize uint64) *file { key, _ := crypto.GenerateTwofishKey() return &file{ name: name, size: fileSize, contracts: make(map[modules.NetAddress]fileContract), masterKey: key, erasureCode: code, pieceSize: pieceSize, } }
// createConsensusSetTester creates a consensusSetTester that's ready for use. func createConsensusSetTester(name string) (*consensusSetTester, error) { testdir := build.TempDir(modules.ConsensusDir, name) // Create modules. g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { return nil, err } cs, err := New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { return nil, err } tp, err := transactionpool.New(cs, g) if err != nil { return nil, err } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } _, err = w.Encrypt(key) if err != nil { return nil, err } err = w.Unlock(key) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } // Assemble all objects into a consensusSetTester. cst := &consensusSetTester{ gateway: g, miner: m, tpool: tp, wallet: w, walletKey: key, cs: cs, persistDir: testdir, } cst.addSiafunds() cst.mineSiacoins() return cst, nil }
// reorgToBlank creates a bunch of empty blocks on top of the genesis block // that reorgs the explorer to a state of all blank blocks. func (et *explorerTester) reorgToBlank() error { // Get a unique directory name to house the persistence of the miner // dependencies. dir := et.testdir + " - " + persist.RandomSuffix() // Create a miner and all dependencies to create an alternate chain. g, err := gateway.New("localhost:0", false, filepath.Join(dir, modules.GatewayDir)) if err != nil { return err } cs, err := consensus.New(g, false, filepath.Join(dir, modules.ConsensusDir)) if err != nil { return err } tp, err := transactionpool.New(cs, g, filepath.Join(dir, modules.TransactionPoolDir)) if err != nil { return err } w, err := wallet.New(cs, tp, filepath.Join(dir, modules.WalletDir)) if err != nil { return err } key, err := crypto.GenerateTwofishKey() if err != nil { return err } _, err = w.Encrypt(key) if err != nil { return err } err = w.Unlock(key) if err != nil { return err } m, err := miner.New(cs, tp, w, filepath.Join(dir, modules.RenterDir)) if err != nil { return err } // Mine blocks until the height is higher than the existing consensus, // submitting each block to the explorerTester. currentHeight := cs.Height() for i := types.BlockHeight(0); i <= currentHeight+1; i++ { block, err := m.AddBlock() if err != nil { return err } et.cs.AcceptBlock(block) // error is not checked, will not always be nil } return nil }
// newTestingFile initializes a file object with random parameters. func newTestingFile() *file { key, _ := crypto.GenerateTwofishKey() data, _ := crypto.RandBytes(8) nData, _ := crypto.RandIntn(10) nParity, _ := crypto.RandIntn(10) rsc, _ := NewRSCode(nData+1, nParity+1) return &file{ name: "testfile-" + strconv.Itoa(int(data[0])), size: encoding.DecUint64(data[1:5]), masterKey: key, erasureCode: rsc, pieceSize: encoding.DecUint64(data[6:8]), } }
// initWallet creates a wallet key, initializes the host wallet, unlocks it, // and then stores the key in the host tester. func (ht *hostTester) initWallet() error { // Create the keys for the wallet and unlock it. key, err := crypto.GenerateTwofishKey() if err != nil { return err } ht.walletKey = key _, err = ht.wallet.Encrypt(key) if err != nil { return err } err = ht.wallet.Unlock(key) if err != nil { return err } return nil }
// blankServerTester creates a server tester object that is ready for testing, // without any money in the wallet. func blankServerTester(name string) (*serverTester, error) { // createServerTester is expensive, and therefore should not be called // during short tests. if testing.Short() { panic("blankServerTester called during short tests") } // Create the server tester with key. testdir := build.TempDir("api", name) key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } st, err := assembleServerTester(key, testdir) if err != nil { return nil, err } return st, nil }
// createServerTester creates a server tester object that is ready for testing, // including money in the wallet and all modules initalized. func createServerTester(name string) (*serverTester, error) { // Create the testing directory. testdir := build.TempDir("api", name) key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } st, err := assembleServerTester(key, testdir) if err != nil { return nil, err } // Mine blocks until the wallet has confirmed money. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { _, err := st.miner.AddBlock() if err != nil { return nil, err } } return st, nil }
// TestIntegrationMinimumValidChildTimestamp probes the // MinimumValidChildTimestamp method of the consensus type. func TestIntegrationMinimumValidChildTimestamp(t *testing.T) { if testing.Short() { t.SkipNow() } t.Parallel() // Create a custom consensus set to control the blocks. testdir := build.TempDir(modules.ConsensusDir, "TestIntegrationMinimumValidChildTimestamp") g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir)) if err != nil { t.Fatal(err) } cs, err := New(g, false, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { t.Fatal(err) } tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir)) if err != nil { t.Fatal(err) } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { t.Fatal(err) } key, err := crypto.GenerateTwofishKey() if err != nil { t.Fatal(err) } _, err = w.Encrypt(key) if err != nil { t.Fatal(err) } err = w.Unlock(key) if err != nil { t.Fatal(err) } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { t.Fatal(err) } defer g.Close() // The earliest child timestamp of the genesis block should be the // timestamp of the genesis block. genesisTime := cs.blockRoot.Block.Timestamp earliest, ok := cs.MinimumValidChildTimestamp(cs.blockRoot.Block.ID()) if !ok || genesisTime != earliest { t.Error("genesis block earliest timestamp producing unexpected results") } timestampOffsets := []types.Timestamp{1, 3, 2, 5, 4, 6, 7, 8, 9, 10} blockIDs := []types.BlockID{cs.blockRoot.Block.ID()} for _, offset := range timestampOffsets { bfw, target, err := m.BlockForWork() if err != nil { t.Fatal(err) } bfw.Timestamp = genesisTime + offset solvedBlock, _ := m.SolveBlock(bfw, target) err = cs.AcceptBlock(solvedBlock) if err != nil { t.Fatal(err) } blockIDs = append(blockIDs, solvedBlock.ID()) } // Median should be genesisTime for 6th block. earliest, ok = cs.MinimumValidChildTimestamp(blockIDs[5]) if !ok || earliest != genesisTime { t.Error("incorrect child timestamp") } // Median should be genesisTime+1 for 7th block. earliest, ok = cs.MinimumValidChildTimestamp(blockIDs[6]) if !ok || earliest != genesisTime+1 { t.Error("incorrect child timestamp") } // Median should be genesisTime + 5 for pb11. earliest, ok = cs.MinimumValidChildTimestamp(blockIDs[10]) if !ok || earliest != genesisTime+5 { t.Error("incorrect child timestamp") } }
// createServerTester creates a server tester object that is ready for testing, // including money in the wallet and all modules initalized. func createServerTester(name string) (*serverTester, error) { // Create the testing directory and assign the api port. testdir := build.TempDir("api", name) APIAddr := ":" + strconv.Itoa(APIPort) APIPort++ // Create the modules. g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { return nil, err } cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { return nil, err } tp, err := transactionpool.New(cs, g) if err != nil { return nil, err } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, err } _, err = w.Encrypt(key) if err != nil { return nil, err } err = w.Unlock(key) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } hdb, err := hostdb.New(cs, g) if err != nil { return nil, err } h, err := host.New(cs, hdb, tp, w, ":0", filepath.Join(testdir, modules.HostDir)) if err != nil { return nil, err } r, err := renter.New(cs, hdb, w, filepath.Join(testdir, modules.RenterDir)) if err != nil { return nil, err } exp, err := explorer.New(cs, filepath.Join(testdir, modules.ExplorerDir)) if err != nil { return nil, err } srv, err := NewServer(APIAddr, cs, g, h, hdb, m, r, tp, w, exp) if err != nil { return nil, err } // Assemble the serverTester. st := &serverTester{ cs: cs, gateway: g, host: h, hostdb: hdb, miner: m, renter: r, tpool: tp, exp: exp, wallet: w, walletKey: key, server: srv, } // TODO: A more reasonable way of listening for server errors. go func() { listenErr := srv.Serve() if listenErr != nil { panic(listenErr) } }() // Mine blocks until the wallet has confirmed money. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { _, err := st.miner.AddBlock() if err != nil { return nil, err } } return st, nil }
// CreateHostTester initializes a HostTester. func CreateHostTester(name string, t *testing.T) *hostTester { testdir := build.TempDir(modules.HostDir, name) // Create the modules. g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { t.Fatal(err) } cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { t.Fatal(err) } tp, err := transactionpool.New(cs, g) if err != nil { t.Fatal(err) } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { t.Fatal(err) } key, err := crypto.GenerateTwofishKey() if err != nil { t.Fatal(err) } _, err = w.Encrypt(key) if err != nil { t.Fatal(err) } err = w.Unlock(key) if err != nil { t.Fatal(err) } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { t.Fatal(err) } h, err := New(cs, tp, w, ":0", filepath.Join(testdir, modules.HostDir)) if err != nil { t.Fatal(err) } // Assemble all objects into a hostTester ht := &hostTester{ cs: cs, gateway: g, miner: m, tpool: tp, wallet: w, host: h, t: t, } // Mine blocks until there is money in the wallet. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := m.FindBlock() err := cs.AcceptBlock(b) if err != nil { t.Fatal(err) } } return ht }
// negotiateContract creates a file contract for a host according to the // requests of the host. There is an assumption that only hosts with acceptable // terms will be put into the hostdb. func (r *Renter) negotiateContract(host modules.HostSettings, up modules.FileUploadParams, piece *filePiece) error { lockID := r.mu.RLock() height := r.blockHeight r.mu.RUnlock(lockID) key, err := crypto.GenerateTwofishKey() if err != nil { return err } file, err := os.Open(up.Filename) if err != nil { return err } defer file.Close() info, err := file.Stat() if err != nil { return err } filesize := uint64(info.Size()) // Get the price and payout. sizeCurrency := types.NewCurrency64(filesize) durationCurrency := types.NewCurrency64(uint64(up.Duration)) clientCost := host.Price.Mul(sizeCurrency).Mul(durationCurrency) hostCollateral := host.Collateral.Mul(sizeCurrency).Mul(durationCurrency) payout := clientCost.Add(hostCollateral) validOutputValue := payout.Sub(types.FileContract{Payout: payout}.Tax()) // Create the contract terms. terms := modules.ContractTerms{ FileSize: filesize, Duration: up.Duration, DurationStart: height - 3, WindowSize: defaultWindowSize, Price: host.Price, Collateral: host.Collateral, ValidProofOutputs: []types.SiacoinOutput{ {Value: validOutputValue, UnlockHash: host.UnlockHash}, }, MissedProofOutputs: []types.SiacoinOutput{ {Value: validOutputValue, UnlockHash: types.UnlockHash{}}, }, } // TODO: This is a hackish sleep, we need to be certain that all dependent // transactions have propgated to the host's transaction pool. Instead, // built into the protocol should be a step where any dependent // transactions are automatically provided. if build.Release == "standard" { time.Sleep(time.Minute) } else if build.Release == "testing" { time.Sleep(time.Second * 15) } else { time.Sleep(time.Second) } // Perform the negotiations with the host through a network call. conn, err := net.DialTimeout("tcp", string(host.IPAddress), 10e9) if err != nil { return err } defer conn.Close() err = encoding.WriteObject(conn, [8]byte{'C', 'o', 'n', 't', 'r', 'a', 'c', 't'}) if err != nil { return err } // Send the contract terms and read the response. if err = encoding.WriteObject(conn, terms); err != nil { return err } var response string if err = encoding.ReadObject(conn, &response, 128); err != nil { return err } if response != modules.AcceptTermsResponse { return errors.New(response) } // Encrypt and transmit the file data while calculating its Merkle root. tee := io.TeeReader( // wrap file reader in encryption layer key.NewReader(file), // each byte we read from tee will also be written to conn; // the uploadWriter updates the piece's 'Transferred' field &uploadWriter{piece, conn}, ) merkleRoot, err := crypto.ReaderMerkleRoot(tee) if err != nil { return err } // Create the transaction holding the contract. This is done first so the // transaction is created sooner, which will impact the user's wallet // balance faster vs. waiting for the whole thing to upload before // affecting the user's balance. unsignedTxn, txnBuilder, err := r.createContractTransaction(terms, merkleRoot) if err != nil { return err } // Send the unsigned transaction to the host. err = encoding.WriteObject(conn, unsignedTxn) if err != nil { return err } // The host will respond with a transaction with the collateral added. // Add the collateral inputs from the host to the original wallet // transaction. var collateralTxn types.Transaction err = encoding.ReadObject(conn, &collateralTxn, 16e3) if err != nil { return err } for i := len(unsignedTxn.SiacoinInputs); i < len(collateralTxn.SiacoinInputs); i++ { txnBuilder.AddSiacoinInput(collateralTxn.SiacoinInputs[i]) } signedTxn, err := txnBuilder.Sign(true) if err != nil { return err } // Send the signed transaction back to the host. err = encoding.WriteObject(conn, signedTxn) if err != nil { return err } // Read an ack from the host that all is well. var ack bool err = encoding.ReadObject(conn, &ack, 1) if err != nil { return err } if !ack { return errors.New("host negotiation failed") } // TODO: We don't actually watch the blockchain to make sure that the // file contract made it. // Negotiation was successful; update the filePiece. txIndex := len(signedTxn) - 1 lockID = r.mu.Lock() piece.Active = true piece.Repairing = false piece.Contract = signedTxn[txIndex].FileContracts[0] piece.ContractID = signedTxn[txIndex].FileContractID(0) piece.HostIP = host.IPAddress piece.EncryptionKey = key r.save() r.mu.Unlock(lockID) return nil }
// newTestingTrio creates a Host, Contractor, and TestMiner that can be used // for testing host/renter interactions. func newTestingTrio(name string) (modules.Host, *Contractor, modules.TestMiner, error) { testdir := build.TempDir("contractor", name) // create miner g, err := gateway.New("localhost:0", false, filepath.Join(testdir, modules.GatewayDir)) if err != nil { return nil, nil, nil, err } cs, err := consensus.New(g, false, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { return nil, nil, nil, err } tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir)) if err != nil { return nil, nil, nil, err } w, err := modWallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, nil, nil, err } key, err := crypto.GenerateTwofishKey() if err != nil { return nil, nil, nil, err } if !w.Encrypted() { _, err = w.Encrypt(key) if err != nil { return nil, nil, nil, err } } err = w.Unlock(key) if err != nil { return nil, nil, nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, nil, nil, err } // create host and contractor, using same consensus set and gateway h, err := newTestingHost(filepath.Join(testdir, "Host"), cs, tp) if err != nil { return nil, nil, nil, err } c, err := newTestingContractor(filepath.Join(testdir, "Contractor"), cs, tp) if err != nil { return nil, nil, nil, err } // announce the host err = h.Announce() if err != nil { return nil, nil, nil, err } // mine a block, processing the announcement m.AddBlock() // wait for hostdb to scan host for i := 0; i < 500 && len(c.hdb.RandomHosts(1, nil)) == 0; i++ { time.Sleep(time.Millisecond) } return h, c, m, nil }