// createMinerTester creates a minerTester that's ready for use. func createMinerTester(name string) (*minerTester, error) { testdir := build.TempDir(modules.MinerDir, name) // 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 } var key crypto.TwofishKey _, err = rand.Read(key[:]) 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 := New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } // Assemble the minerTester. mt := &minerTester{ gateway: g, cs: cs, tpool: tp, wallet: w, walletKey: key, miner: m, } // Mine until the wallet has money. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := m.FindBlock() err = cs.AcceptBlock(b) if err != nil { return nil, err } } return mt, 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 }
// createWalletTester takes a testing.T and creates a WalletTester. func createWalletTester(name string) (*walletTester, error) { // Create the modules testdir := build.TempDir(modules.WalletDir, 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 := New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } var masterKey crypto.TwofishKey _, err = rand.Read(masterKey[:]) if err != nil { return nil, err } _, err = w.Encrypt(masterKey) if err != nil { return nil, err } err = w.Unlock(masterKey) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } // Assemble all componenets into a wallet tester. wt := &walletTester{ cs: cs, gateway: g, tpool: tp, miner: m, wallet: w, walletMasterKey: masterKey, persistDir: testdir, } // Mine blocks until there is money in the wallet. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := wt.miner.FindBlock() err := wt.cs.AcceptBlock(b) if err != nil { return nil, err } } return wt, 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 }
// TestNilInputs tries starting the wallet using nil inputs. func TestNilInputs(t *testing.T) { testdir := build.TempDir(modules.WalletDir, "TestNilInputs") 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) } wdir := filepath.Join(testdir, modules.WalletDir) _, err = New(cs, nil, wdir) if err != errNilTpool { t.Error(err) } _, err = New(nil, tp, wdir) if err != errNilConsensusSet { t.Error(err) } _, err = New(nil, nil, wdir) if err != errNilConsensusSet { t.Error(err) } }
// 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 }
// TestCloseWallet tries to close the wallet. func TestCloseWallet(t *testing.T) { if testing.Short() { t.Skip() } testdir := build.TempDir(modules.WalletDir, "TestCloseWallet") g, err := gateway.New("localhost: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, filepath.Join(testdir, modules.TransactionPoolDir)) if err != nil { t.Fatal(err) } wdir := filepath.Join(testdir, modules.WalletDir) w, err := New(cs, tp, wdir) if err != nil { t.Fatal(err) } if err := w.Close(); err != nil { t.Fatal(err) } }
// TestIntegrationWalletBlankEncrypt tries to encrypt and unlock the wallet // through the api using a blank encryption key - meaning that the wallet seed // returned by the encryption call can be used as the encryption key. func TestIntegrationWalletBlankEncrypt(t *testing.T) { if testing.Short() { t.SkipNow() } // Create a server object without encrypting or unlocking the wallet. testdir := build.TempDir("api", "TestIntegrationWalletBlankEncrypt") 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) } srv, err := NewServer(":0", cs, g, nil, nil, nil, nil, tp, w, nil) if err != nil { t.Fatal(err) } // Assemble the serverTester. st := &serverTester{ cs: cs, gateway: g, tpool: tp, wallet: w, server: srv, } go func() { listenErr := srv.Serve() if listenErr != nil { panic(listenErr) } }() // Make a call to /wallet/encrypt and get the seed. Provide no encryption // key so that the encryption key is the seed that gets returned. var wep WalletEncryptPOST err = st.postAPI("/wallet/encrypt", url.Values{}, &wep) if err != nil { t.Fatal(err) } // Use the seed to call /wallet/unlock. unlockValues := url.Values{} unlockValues.Set("encryptionpassword", wep.PrimarySeed) err = st.stdPostAPI("/wallet/unlock", unlockValues) if err != nil { t.Fatal(err) } // Check that the wallet actually unlocked. if !w.Unlocked() { t.Error("wallet is not unlocked") } }
// 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 }
// TestIntegrationWalletGETEncrypted probes the GET call to /wallet when the // wallet has never been encrypted. func TestIntegrationWalletGETEncrypted(t *testing.T) { if testing.Short() { t.SkipNow() } // Check a wallet that has never been encrypted. testdir := build.TempDir("api", "TestIntegrationWalletGETEncrypted") g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { t.Fatal("Failed to create gateway:", err) } cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { t.Fatal("Failed to create consensus set:", err) } tp, err := transactionpool.New(cs, g) if err != nil { t.Fatal("Failed to create tpool:", err) } w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { t.Fatal("Failed to create wallet:", err) } srv, err := NewServer(":0", "Sia-Agent", cs, nil, g, nil, nil, nil, tp, w) if err != nil { t.Fatal(err) } // Assemble the serverTester and start listening for api requests. st := &serverTester{ cs: cs, gateway: g, tpool: tp, wallet: w, server: srv, } go func() { listenErr := srv.Serve() if listenErr != nil { t.Fatal("API server quit:", listenErr) } }() defer st.server.Close() var wg WalletGET err = st.getAPI("/wallet", &wg) if err != nil { t.Fatal(err) } if wg.Encrypted { t.Error("Wallet has never been unlocked") } if wg.Unlocked { t.Error("Wallet has never been unlocked") } }
// 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 } 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, cs: cs, persistDir: testdir, csUpdateChan: cs.ConsensusSetNotify(), minerUpdateChan: m.MinerNotify(), tpoolUpdateChan: tp.TransactionPoolNotify(), walletUpdateChan: w.WalletNotify(), } cst.csUpdateWait() // 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 } cst.csUpdateWait() } return cst, 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, } 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 }
// blankHostTester creates a host tester where the modules are created but no // extra initialization has been done, for example no blocks have been mined // and the wallet keys have not been created. func blankHostTester(name string) (*hostTester, error) { testdir := build.TempDir(modules.HostDir, name) // Create the modules. g, err := gateway.New("localhost: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, 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 } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } h, err := New(cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir)) if err != nil { return nil, err } /* r, err := renter.New(cs, w, tp, filepath.Join(testdir, modules.RenterDir)) if err != nil { return nil, err } */ // Assemble all objects into a hostTester ht := &hostTester{ cs: cs, gateway: g, miner: m, // renter: r, tpool: tp, wallet: w, host: h, persistDir: testdir, } return ht, nil }
func createExplorerTester(name string, t *testing.T) *explorerTester { 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) } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.RenterDir)) if err != nil { t.Fatal(err) } be, err := New(cs) if err != nil { t.Fatal(err) } et := &explorerTester{ cs: cs, gateway: g, miner: m, tpool: tp, wallet: w, explorer: be, csUpdateChan: cs.ConsensusSetNotify(), beUpdateChan: be.BlockExplorerNotify(), tpoolUpdateChan: tp.TransactionPoolNotify(), minerUpdateChan: m.MinerNotify(), walletUpdateChan: w.WalletNotify(), t: t, } et.csUpdateWait() return et }
// createBlankWalletTester creates a wallet tester that has not mined any // blocks or encrypted the wallet. func createBlankWalletTester(name string) (*walletTester, error) { // Create the modules testdir := build.TempDir(modules.WalletDir, 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 := New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.WalletDir)) if err != nil { return nil, err } // Assemble all componenets into a wallet tester. wt := &walletTester{ gateway: g, cs: cs, tpool: tp, miner: m, wallet: w, persistDir: testdir, } return wt, nil }
// TestPrimarySeed checks that the correct seed is returned when calling // PrimarySeed. func TestPrimarySeed(t *testing.T) { if testing.Short() { t.SkipNow() } // Create a wallet and fetch the seed at startup. dir := build.TempDir(modules.WalletDir, "TestPrimarySeed") g, err := gateway.New(":0", filepath.Join(dir, modules.GatewayDir)) if err != nil { t.Fatal(err) } cs, err := consensus.New(g, filepath.Join(dir, modules.ConsensusDir)) if err != nil { t.Fatal(err) } tp, err := transactionpool.New(cs, g) if err != nil { t.Fatal(err) } w, err := New(cs, tp, filepath.Join(dir, modules.WalletDir)) if err != nil { t.Fatal(err) } seed, err := w.Encrypt(crypto.TwofishKey{}) if err != nil { t.Fatal(err) } err = w.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) if err != nil { t.Fatal(err) } primarySeed, progress, err := w.PrimarySeed() if err != nil { t.Fatal(err) } if !bytes.Equal(primarySeed[:], seed[:]) { t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") } if progress != 0 { t.Error("primary seed is returning the wrong progress") } _, err = w.NextAddress() if err != nil { t.Fatal(err) } _, progress, err = w.PrimarySeed() if err != nil { t.Fatal(err) } if progress != 1 { t.Error("primary seed is returning the wrong progress") } // Lock then unlock the wallet and check the responses. err = w.Lock() if err != nil { t.Fatal(err) } _, _, err = w.PrimarySeed() if err != modules.ErrLockedWallet { t.Error("unexpected err:", err) } err = w.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) if err != nil { t.Fatal(err) } primarySeed, progress, err = w.PrimarySeed() if err != nil { t.Fatal(err) } if !bytes.Equal(primarySeed[:], seed[:]) { t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") } if progress != 1 { t.Error("progress reporting an unexpected value") } }
// 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 }
// 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 }
// assembleAuthenticatedServerTester creates a bunch of modules and assembles // them into a server tester that requires authentication with the given // requiredPassword. No directories are created and no blocks are mined. func assembleAuthenticatedServerTester(requiredPassword string, key crypto.TwofishKey, testdir string) (*serverTester, error) { // assembleAuthenticatedServerTester 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 modules. 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 } if !w.Encrypted() { _, 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 } h, err := host.New(cs, tp, w, "localhost:0", filepath.Join(testdir, modules.HostDir)) if err != nil { return nil, err } r, err := renter.New(cs, w, tp, filepath.Join(testdir, modules.RenterDir)) if err != nil { return nil, err } srv, err := NewServer("localhost:0", "Sia-Agent", requiredPassword, cs, nil, g, h, m, r, tp, w) if err != nil { return nil, err } // Assemble the serverTester. st := &serverTester{ cs: cs, gateway: g, host: h, miner: m, renter: r, tpool: tp, wallet: w, walletKey: key, server: srv, dir: testdir, } // TODO: A more reasonable way of listening for server errors. go func() { listenErr := srv.Serve() if listenErr != nil { panic(listenErr) } }() 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") } }
// newHDBTester returns a ready-to-use hdb tester, with all modules // initialized. func newHDBTester(name string) (*hdbTester, 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 } hdb, err := New(cs, g, filepath.Join(testdir, modules.HostDBDir)) 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 } var masterKey crypto.TwofishKey _, err = rand.Read(masterKey[:]) if err != nil { return nil, err } _, err = w.Encrypt(masterKey) if err != nil { return nil, err } err = w.Unlock(masterKey) 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 } m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { return nil, err } // Assemble all objects into an hdbTester. hdbt := &hdbTester{ cs: cs, gateway: g, host: h, miner: m, tpool: tp, wallet: w, walletMasterKey: masterKey, hostdb: hdb, } // Mine blocks until there is money in the wallet. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := hdbt.miner.FindBlock() err = hdbt.cs.AcceptBlock(b) if err != nil { return nil, err } } return hdbt, err }
// startDaemonCmd uses the config parameters to start siad. func startDaemon() error { // Establish multithreading. runtime.GOMAXPROCS(runtime.NumCPU()) // Print a startup message. // // TODO: This message can be removed once the api starts up in under 1/2 // second. fmt.Println("siad is loading, may take a minute or two") loadStart := time.Now().UnixNano() // Create all of the modules. gateway, err := gateway.New(config.Siad.RPCaddr, filepath.Join(config.Siad.SiaDir, modules.GatewayDir)) if err != nil { return err } state, err := consensus.New(gateway, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir)) if err != nil { return err } tpool, err := transactionpool.New(state, gateway) if err != nil { return err } wallet, err := wallet.New(state, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir)) if err != nil { return err } miner, err := miner.New(state, tpool, wallet, filepath.Join(config.Siad.SiaDir, modules.MinerDir)) if err != nil { return err } hostdb, err := hostdb.New(state, gateway) if err != nil { return err } host, err := host.New(state, hostdb, tpool, wallet, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir)) if err != nil { return err } renter, err := renter.New(state, hostdb, wallet, filepath.Join(config.Siad.SiaDir, modules.RenterDir)) if err != nil { return err } srv, err := api.NewServer(config.Siad.APIaddr, state, gateway, host, hostdb, miner, renter, tpool, wallet, nil) if err != nil { return err } // Bootstrap to the network. if !config.Siad.NoBootstrap { for i := range modules.BootstrapPeers { go gateway.Connect(modules.BootstrapPeers[i]) } } // Print a 'startup complete' message. // // TODO: This message can be removed once the api starts up in under 1/2 // second. startupTime := time.Now().UnixNano() - loadStart fmt.Println("siad has finished loading after", float64(startupTime)/1e9, "seconds") // Start serving api requests. err = srv.Serve() if err != nil { return err } return nil }
// newHDBTester returns a ready-to-use hdb tester, with all modules // initialized. func newHDBTester(name string, t *testing.T) *hdbTester { testdir := build.TempDir("hostdb", name) // Create the gateway. g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { t.Fatal(err) } // Create the consensus set. cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { t.Fatal(err) } // Create the hostdb. hdb, err := New(cs, g) if err != nil { t.Fatal(err) } // Create the tpool. tp, err := transactionpool.New(cs, g) if err != nil { t.Fatal(err) } // Create the wallet. w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { t.Fatal(err) } // Create the host. h, err := host.New(cs, hdb, tp, w, ":0", filepath.Join(testdir, modules.HostDir)) if err != nil { t.Fatal(err) } // Create the miner. m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.MinerDir)) if err != nil { t.Fatal(err) } // Assemble all objects into an hdbTester. hdbt := &hdbTester{ cs: cs, gateway: g, host: h, miner: m, tpool: tp, wallet: w, hostdb: hdb, csUpdateChan: cs.ConsensusSetNotify(), hostUpdateChan: h.HostNotify(), hostdbUpdateChan: hdb.HostDBNotify(), tpoolUpdateChan: tp.TransactionPoolNotify(), minerUpdateChan: m.MinerNotify(), walletUpdateChan: w.WalletNotify(), t: t, } hdbt.csUpdateWait() // Mine blocks until there is money in the wallet. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := hdbt.miner.FindBlock() err = hdbt.cs.AcceptBlock(b) if err != nil { t.Fatal(err) } hdbt.csUpdateWait() } // TODO: Reconsider the way that the RPC's happen. //g.RegisterRPC("HostSettings", h.Settings) return hdbt }
// 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 }
// startDaemonCmd uses the config parameters to start siad. func startDaemon(config Config) (err error) { // Prompt user for API password. var password string if config.Siad.AuthenticateAPI { password, err = speakeasy.Ask("Enter API password: "******"" { return errors.New("password cannot be blank") } passwordConfirm, err := speakeasy.Ask("Confirm API password: "******"passwords don't match") } } // Print a startup message. fmt.Println("Loading...") loadStart := time.Now() // Create all of the modules. i := 0 var g modules.Gateway if strings.Contains(config.Siad.Modules, "g") { i++ fmt.Printf("(%d/%d) Loading gateway...\n", i, len(config.Siad.Modules)) g, err = gateway.New(config.Siad.RPCaddr, filepath.Join(config.Siad.SiaDir, modules.GatewayDir)) if err != nil { return err } } var cs modules.ConsensusSet if strings.Contains(config.Siad.Modules, "c") { i++ fmt.Printf("(%d/%d) Loading consensus...\n", i, len(config.Siad.Modules)) cs, err = consensus.New(g, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir)) if err != nil { return err } } var e modules.Explorer if strings.Contains(config.Siad.Modules, "e") { i++ fmt.Printf("(%d/%d) Loading explorer...\n", i, len(config.Siad.Modules)) e, err = explorer.New(cs, filepath.Join(config.Siad.SiaDir, modules.ExplorerDir)) if err != nil { return err } } var tpool modules.TransactionPool if strings.Contains(config.Siad.Modules, "t") { i++ fmt.Printf("(%d/%d) Loading transaction pool...\n", i, len(config.Siad.Modules)) tpool, err = transactionpool.New(cs, g, filepath.Join(config.Siad.SiaDir, modules.TransactionPoolDir)) if err != nil { return err } } var w modules.Wallet if strings.Contains(config.Siad.Modules, "w") { i++ fmt.Printf("(%d/%d) Loading wallet...\n", i, len(config.Siad.Modules)) w, err = wallet.New(cs, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir)) if err != nil { return err } } var m modules.Miner if strings.Contains(config.Siad.Modules, "m") { i++ fmt.Printf("(%d/%d) Loading miner...\n", i, len(config.Siad.Modules)) m, err = miner.New(cs, tpool, w, filepath.Join(config.Siad.SiaDir, modules.MinerDir)) if err != nil { return err } } var h modules.Host if strings.Contains(config.Siad.Modules, "h") { i++ fmt.Printf("(%d/%d) Loading host...\n", i, len(config.Siad.Modules)) h, err = host.New(cs, tpool, w, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir)) if err != nil { return err } } var r modules.Renter if strings.Contains(config.Siad.Modules, "r") { i++ fmt.Printf("(%d/%d) Loading renter...\n", i, len(config.Siad.Modules)) r, err = renter.New(cs, w, tpool, filepath.Join(config.Siad.SiaDir, modules.RenterDir)) if err != nil { return err } } srv, err := api.NewServer( config.Siad.APIaddr, config.Siad.RequiredUserAgent, password, cs, e, g, h, m, r, tpool, w, ) if err != nil { return err } // Bootstrap to the network. if !config.Siad.NoBootstrap && g != nil { // connect to 3 random bootstrap nodes perm, err := crypto.Perm(len(modules.BootstrapPeers)) if err != nil { return err } for _, i := range perm[:3] { go g.Connect(modules.BootstrapPeers[i]) } } // Print a 'startup complete' message. startupTime := time.Since(loadStart) fmt.Println("Finished loading in", startupTime.Seconds(), "seconds") // Start serving api requests. err = srv.Serve() if err != nil { return err } return nil }
// startDaemonCmd uses the config parameters to start siad. func startDaemon() error { // Establish multithreading. runtime.GOMAXPROCS(runtime.NumCPU()) // Create all of the modules. gateway, err := gateway.New(config.Siad.RPCaddr, filepath.Join(config.Siad.SiaDir, modules.GatewayDir)) if err != nil { return err } state, err := consensus.New(gateway, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir)) if err != nil { return err } tpool, err := transactionpool.New(state, gateway) if err != nil { return err } wallet, err := wallet.New(state, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir)) if err != nil { return err } miner, err := miner.New(state, tpool, wallet, filepath.Join(config.Siad.SiaDir, modules.MinerDir)) if err != nil { return err } hostdb, err := hostdb.New(state, gateway) if err != nil { return err } host, err := host.New(state, hostdb, tpool, wallet, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir)) if err != nil { return err } renter, err := renter.New(state, hostdb, wallet, filepath.Join(config.Siad.SiaDir, modules.RenterDir)) if err != nil { return err } srv, err := api.NewServer(config.Siad.APIaddr, state, gateway, host, hostdb, miner, renter, tpool, wallet, nil) if err != nil { return err } // Bootstrap to the network. if !config.Siad.NoBootstrap { for i := range modules.BootstrapPeers { go gateway.Connect(modules.BootstrapPeers[i]) } } // Send a struct down the started channel, so the testing package knows // that daemon startup has completed. A gofunc is used with the hope that // srv.Serve() will start running before the value is sent down the // channel. go func() { started <- struct{}{} }() // Start serving api requests. err = srv.Serve() if err != nil { return err } return nil }
// newRenterTester creates a ready-to-use renter tester with money in the // wallet. func newRenterTester(name string, t *testing.T) *renterTester { testdir := build.TempDir("renter", name) // Create the gateway. g, err := gateway.New(":0", filepath.Join(testdir, modules.GatewayDir)) if err != nil { t.Fatal(err) } // Create the consensus set. cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir)) if err != nil { t.Fatal(err) } // Create the hostdb. hdb, err := hostdb.New(cs, g) if err != nil { t.Fatal(err) } // Create the tpool. tp, err := transactionpool.New(cs, g) if err != nil { t.Fatal(err) } // Create the wallet. w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir)) if err != nil { t.Fatal(err) } // Create the renter. r, err := New(cs, hdb, w, filepath.Join(testdir, modules.RenterDir)) if err != nil { t.Fatal(err) } // Create the miner. m, err := miner.New(cs, tp, w, filepath.Join(testdir, modules.RenterDir)) if err != nil { t.Fatal(err) } // Assemble all pieces into a renter tester. rt := &renterTester{ cs: cs, hostdb: hdb, miner: m, tpool: tp, wallet: w, renter: r, csUpdateChan: cs.ConsensusSetNotify(), hostdbUpdateChan: hdb.HostDBNotify(), renterUpdateChan: r.RenterNotify(), tpoolUpdateChan: tp.TransactionPoolNotify(), minerUpdateChan: m.MinerNotify(), walletUpdateChan: w.WalletNotify(), t: t, } rt.csUpdateWait() // Mine blocks until there is money in the wallet. for i := types.BlockHeight(0); i <= types.MaturityDelay; i++ { b, _ := rt.miner.FindBlock() err := rt.cs.AcceptBlock(b) if err != nil { t.Fatal(err) } rt.csUpdateWait() } return rt }
// assembleServerTester creates a bunch of modules and assembles them into a // server tester, without creating any directories or mining any blocks. func assembleServerTester(key crypto.TwofishKey, testdir string) (*serverTester, error) { // 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 } if !w.Encrypted() { _, 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 } h, err := host.New(cs, tp, w, ":0", filepath.Join(testdir, modules.HostDir)) if err != nil { return nil, err } r, err := renter.New(cs, w, tp, filepath.Join(testdir, modules.RenterDir)) if err != nil { return nil, err } e, err := explorer.New(cs, filepath.Join(testdir, modules.ExplorerDir)) if err != nil { return nil, err } srv, err := NewServer(":0", "Sia-Agent", cs, e, g, h, m, r, tp, w) if err != nil { return nil, err } // Assemble the serverTester. st := &serverTester{ cs: cs, gateway: g, host: h, miner: m, renter: r, tpool: tp, explorer: e, wallet: w, walletKey: key, server: srv, dir: testdir, } // TODO: A more reasonable way of listening for server errors. go func() { listenErr := srv.Serve() if listenErr != nil { panic(listenErr) } }() return st, nil }
// startDaemonCmd uses the config parameters to start siad. func startDaemon(config Config) error { // Print a startup message. fmt.Println("Loading...") loadStart := time.Now() // Create all of the modules. gateway, err := gateway.New(config.Siad.RPCaddr, filepath.Join(config.Siad.SiaDir, modules.GatewayDir)) if err != nil { return err } cs, err := consensus.New(gateway, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir)) if err != nil { return err } var e *explorer.Explorer if config.Siad.Explorer { e, err = explorer.New(cs, filepath.Join(config.Siad.SiaDir, modules.ExplorerDir)) if err != nil { return err } } tpool, err := transactionpool.New(cs, gateway) if err != nil { return err } wallet, err := wallet.New(cs, tpool, filepath.Join(config.Siad.SiaDir, modules.WalletDir)) if err != nil { return err } miner, err := miner.New(cs, tpool, wallet, filepath.Join(config.Siad.SiaDir, modules.MinerDir)) if err != nil { return err } host, err := host.New(cs, tpool, wallet, config.Siad.HostAddr, filepath.Join(config.Siad.SiaDir, modules.HostDir)) if err != nil { return err } renter, err := renter.New(cs, wallet, tpool, filepath.Join(config.Siad.SiaDir, modules.RenterDir)) if err != nil { return err } srv, err := api.NewServer( config.Siad.APIaddr, config.Siad.RequiredUserAgent, cs, e, gateway, host, miner, renter, tpool, wallet, ) if err != nil { return err } // Bootstrap to the network. if !config.Siad.NoBootstrap { // connect to 3 random bootstrap nodes perm, err := crypto.Perm(len(modules.BootstrapPeers)) if err != nil { return err } for _, i := range perm[:3] { go gateway.Connect(modules.BootstrapPeers[i]) } } // Print a 'startup complete' message. startupTime := time.Since(loadStart) fmt.Println("Finished loading in", startupTime.Seconds(), "seconds") // Start serving api requests. err = srv.Serve() if err != nil { return err } return nil }