// New returns a host database that will still crawling the hosts it finds on // the blockchain. func New(cs *consensus.ConsensusSet, g modules.Gateway) (hdb *HostDB, err error) { // Check for nil dependencies. if cs == nil { err = ErrNilConsensusSet return } if g == nil { err = ErrNilGateway return } // Build an empty hostdb. hdb = &HostDB{ consensusSet: cs, gateway: g, activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), mu: sync.New(modules.SafeMutexDelay, 1), } // Begin listening to consensus and looking for hosts. for i := 0; i < scanningThreads; i++ { go hdb.threadedProbeHosts() } go hdb.threadedScan() cs.ConsensusSetSubscribe(hdb) return }
// NewServer creates a new API server from the provided modules. func NewServer(APIaddr string, s *consensus.ConsensusSet, g modules.Gateway, h modules.Host, m modules.Miner, r modules.Renter, tp modules.TransactionPool, w modules.Wallet, explorer modules.Explorer) (*Server, error) { l, err := net.Listen("tcp", APIaddr) if err != nil { return nil, err } srv := &Server{ cs: s, gateway: g, host: h, miner: m, renter: r, tpool: tp, wallet: w, explorer: explorer, listener: l, mu: sync.New(modules.SafeMutexDelay, 1), } // Register API handlers srv.initAPI() return srv, nil }
// New creates a new wallet, loading any known addresses from the input file // name and then using the file to save in the future. Keys and addresses are // not loaded into the wallet during the call to 'new', but rather during the // call to 'Unlock'. func New(cs modules.ConsensusSet, tpool modules.TransactionPool, persistDir string) (*Wallet, error) { // Check for nil dependencies. if cs == nil { return nil, errNilConsensusSet } if tpool == nil { return nil, errNilTpool } // Initialize the data structure. w := &Wallet{ cs: cs, tpool: tpool, keys: make(map[types.UnlockHash]spendableKey), siacoinOutputs: make(map[types.SiacoinOutputID]types.SiacoinOutput), siafundOutputs: make(map[types.SiafundOutputID]types.SiafundOutput), spentOutputs: make(map[types.OutputID]types.BlockHeight), processedTransactionMap: make(map[types.TransactionID]*modules.ProcessedTransaction), historicOutputs: make(map[types.OutputID]types.Currency), historicClaimStarts: make(map[types.SiafundOutputID]types.Currency), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 1), } err := w.initPersist() if err != nil { return nil, err } return w, nil }
// TestRepeatInsert inserts 2 hosts with the same address. func TestRepeatInsert(t *testing.T) { if testing.Short() { t.SkipNow() } hdb := &HostDB{ activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), mu: sync.New(modules.SafeMutexDelay, 1), } entry1 := hostEntry{ HostSettings: modules.HostSettings{IPAddress: fakeAddr(0)}, weight: types.NewCurrency64(1), } entry2 := entry1 hdb.insertNode(&entry1) entry2.weight = types.NewCurrency64(100) hdb.insertNode(&entry2) if len(hdb.activeHosts) != 1 { t.Error("insterting the same entry twice should result in only 1 entry in the hostdb") } }
// NewServer creates a new API server from the provided modules. func NewServer(APIaddr string, s *consensus.ConsensusSet, g modules.Gateway, h modules.Host, hdb modules.HostDB, m modules.Miner, r modules.Renter, tp modules.TransactionPool, w modules.Wallet, exp modules.Explorer) (*Server, error) { l, err := net.Listen("tcp", APIaddr) if err != nil { return nil, err } srv := &Server{ cs: s, gateway: g, host: h, hostdb: hdb, miner: m, renter: r, tpool: tp, wallet: w, exp: exp, blockchainHeight: -1, listener: l, mu: sync.New(modules.SafeMutexDelay, 1), } // Set the genesis block and start listening to the consensus package. srv.currentBlock = srv.cs.GenesisBlock() srv.cs.ConsensusSetSubscribe(srv) // Register API handlers srv.initAPI() return srv, nil }
// New creates a transaction pool that is ready to receive transactions. func New(cs modules.ConsensusSet, g modules.Gateway) (*TransactionPool, error) { // Check that the input modules are non-nil. if cs == nil { return nil, errNilCS } if g == nil { return nil, errNilGateway } // Initialize a transaction pool. tp := &TransactionPool{ consensusSet: cs, gateway: g, knownObjects: make(map[ObjectID]TransactionSetID), transactionSets: make(map[TransactionSetID][]types.Transaction), transactionSetDiffs: make(map[TransactionSetID]modules.ConsensusChange), // The consensus change index is intialized to '-1', which indicates // that no consensus changes have been sent yet. The first consensus // change will then have an index of '0'. consensusChangeIndex: -1, mu: sync.New(modules.SafeMutexDelay, 5), } // Register RPCs g.RegisterRPC("RelayTransactionSet", tp.RelayTransactionSet) g.RegisterRPC("RelayTransaction", tp.RelayTransaction) // COMPAT v0.3.3.3 // Subscribe the transaction pool to the consensus set. cs.ConsensusSetSubscribe(tp) return tp, nil }
// New returns an empty renter. func New(cs modules.ConsensusSet, wallet modules.Wallet, tpool modules.TransactionPool, persistDir string) (*Renter, error) { hdb, err := hostdb.New(cs, wallet, tpool, persistDir) if err != nil { return nil, err } r := &Renter{ cs: cs, wallet: wallet, hostDB: hdb, files: make(map[string]*file), tracking: make(map[string]trackedFile), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 1), } err = r.initPersist() if err != nil { return nil, err } go r.threadedRepairLoop() return r, nil }
// New returns an initialized Gateway. func New(addr string, persistDir string) (g *Gateway, err error) { // Create the directory if it doesn't exist. err = os.MkdirAll(persistDir, 0700) if err != nil { return } // Create the logger. logger, err := makeLogger(persistDir) if err != nil { return } g = &Gateway{ handlers: make(map[rpcID]modules.RPCFunc), initRPCs: make(map[string]modules.RPCFunc), peers: make(map[modules.NetAddress]*peer), nodes: make(map[modules.NetAddress]struct{}), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 2), log: logger, } // Load the old peer list. If it doesn't exist, no problem, but if it does, // we want to know about any errors preventing us from loading it. if loadErr := g.load(); loadErr != nil && !os.IsNotExist(loadErr) { return nil, loadErr } // Create listener and set address. g.listener, err = net.Listen("tcp", addr) if err != nil { return } _, port, _ := net.SplitHostPort(g.listener.Addr().String()) g.myAddr = modules.NetAddress(net.JoinHostPort("::1", port)) // Register RPCs. g.RegisterRPC("ShareNodes", g.shareNodes) g.RegisterRPC("RelayNode", g.relayNode) g.RegisterConnectCall("ShareNodes", g.requestNodes) g.log.Println("INFO: gateway created, started logging") // Learn our external IP. go g.learnHostname() // Automatically forward the RPC port, if possible. go g.forwardPort(port) // Spawn the peer and node managers. These will attempt to keep the peer // and node lists healthy. go g.threadedPeerManager() go g.threadedNodeManager() // Spawn the primary listener. go g.listen() return }
// TestVariedWeights runs broad statistical tests on selecting hosts with // multiple different weights. func TestVariedWeights(t *testing.T) { if testing.Short() { t.SkipNow() } hdb := &HostDB{ activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), mu: sync.New(modules.SafeMutexDelay, 1), } // insert i hosts with the weights 0, 1, ..., i-1. 100e3 selections will be made // per weight added to the tree, the total number of selections necessary // will be tallied up as hosts are created. hostCount := 5 expectedPerWeight := int(10e3) selections := 0 for i := 0; i < hostCount; i++ { entry := hostEntry{ HostSettings: modules.HostSettings{IPAddress: fakeAddr(uint8(i))}, weight: types.NewCurrency64(uint64(i)), } hdb.insertNode(&entry) selections += i * expectedPerWeight } // Perform many random selections, noting which host was selected each // time. selectionMap := make(map[string]int) for i := 0; i < selections; i++ { randEntry := hdb.RandomHosts(1) if len(randEntry) == 0 { t.Fatal("no hosts!") } node, exists := hdb.activeHosts[randEntry[0].IPAddress] if !exists { t.Fatal("can't find randomly selected node in tree") } selectionMap[node.hostEntry.weight.String()] += 1 } // Check that each host was selected an expected number of times. An error // will be reported if the host of 0 weight is ever selected. acceptableError := 0.2 for weight, timesSelected := range selectionMap { intWeight, err := strconv.Atoi(weight) if err != nil { t.Fatal(err) } expectedSelected := float64(intWeight * expectedPerWeight) if float64(expectedSelected)*acceptableError > float64(timesSelected) || float64(expectedSelected)/acceptableError < float64(timesSelected) { t.Error("weighted list not selecting in a uniform distribution based on weight") t.Error(expectedSelected) t.Error(timesSelected) } } }
// New returns an initialized Gateway. func New(addr string, persistDir string) (g *Gateway, err error) { // Create the directory if it doesn't exist. err = os.MkdirAll(persistDir, 0700) if err != nil { return } // Create the logger. logger, err := makeLogger(persistDir) if err != nil { return } g = &Gateway{ handlers: make(map[rpcID]modules.RPCFunc), initRPCs: make(map[string]modules.RPCFunc), peers: make(map[modules.NetAddress]*peer), nodes: make(map[modules.NetAddress]struct{}), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 2), log: logger, } // Register RPCs. g.RegisterRPC("ShareNodes", g.shareNodes) g.RegisterRPC("RelayNode", g.relayNode) g.RegisterConnectCall("ShareNodes", g.requestNodes) g.RegisterConnectCall("RelayNode", g.sendAddress) g.log.Println("INFO: gateway created, started logging") // Create listener and set address. g.listener, err = net.Listen("tcp", addr) if err != nil { return } _, port, _ := net.SplitHostPort(g.listener.Addr().String()) g.myAddr = modules.NetAddress(net.JoinHostPort(modules.ExternalIP, port)) g.log.Println("INFO: our address is", g.myAddr) // Spawn the primary listener. go g.listen() // Load the old peer list. If it doesn't exist, no problem, but if it does, // we want to know about any errors preventing us from loading it. if loadErr := g.load(); loadErr != nil && !os.IsNotExist(loadErr) { return nil, loadErr } // Spawn the connector loop. This will continually attempt to add nodes as // peers to ensure we stay well-connected. go g.makeOutboundConnections() return }
// New creates a new wallet, loading any known addresses from the input file // name and then using the file to save in the future. func New(cs modules.ConsensusSet, tpool modules.TransactionPool, saveDir string) (w *Wallet, err error) { if cs == nil { err = errors.New("wallet cannot use a nil state") return } if tpool == nil { err = errors.New("wallet cannot use a nil transaction pool") return } w = &Wallet{ state: cs, tpool: tpool, saveDir: saveDir, age: AgeDelay + 100, keys: make(map[types.UnlockHash]*key), timelockedKeys: make(map[types.BlockHeight][]types.UnlockHash), visibleAddresses: make(map[types.UnlockHash]struct{}), siafundAddresses: make(map[types.UnlockHash]struct{}), siafundOutputs: make(map[types.SiafundOutputID]types.SiafundOutput), transactions: make(map[string]*openTransaction), mu: sync.New(modules.SafeMutexDelay, 1), } // Create the wallet folder. err = os.MkdirAll(saveDir, 0700) if err != nil { return } // Try to load a previously saved wallet file. If it doesn't exist, assume // that we're creating a new wallet file. // TODO: log warning if no file found? err = w.load() if os.IsNotExist(err) { err = nil // No wallet file exists... make a visible address for the user. _, _, err = w.coinAddress(true) if err != nil { return nil, err } } if err != nil { err = fmt.Errorf("couldn't load wallet file %s: %v", saveDir, err) return } w.tpool.TransactionPoolSubscribe(w) return }
// New returns a ready-to-go miner that is not mining. func New(cs modules.ConsensusSet, tpool modules.TransactionPool, w modules.Wallet, persistDir string) (*Miner, error) { // Create the miner and its dependencies. if cs == nil { return nil, errors.New("miner cannot use a nil state") } if tpool == nil { return nil, errors.New("miner cannot use a nil transaction pool") } if w == nil { return nil, errors.New("miner cannot use a nil wallet") } // Grab some starting block variables. currentBlock := cs.GenesisBlock().ID() currentTarget, exists1 := cs.ChildTarget(currentBlock) earliestTimestamp, exists2 := cs.EarliestChildTimestamp(currentBlock) if build.DEBUG { if !exists1 { panic("could not get child target") } if !exists2 { panic("could not get child earliest timestamp") } } addr, _, err := w.CoinAddress(false) // false indicates that the address should not be visible to the user. if err != nil { return nil, err } // Assemble the miner. m := &Miner{ cs: cs, tpool: tpool, wallet: w, parent: currentBlock, target: currentTarget, earliestTimestamp: earliestTimestamp, address: addr, blockMem: make(map[types.BlockHeader]types.Block), headerMem: make([]types.BlockHeader, headerForWorkMemory), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 1), } err = m.initPersist() if err != nil { return nil, err } m.tpool.TransactionPoolSubscribe(m) return m, nil }
// New returns a ready-to-go miner that is not mining. func New(cs modules.ConsensusSet, tpool modules.TransactionPool, w modules.Wallet, persistDir string) (*Miner, error) { // Create the miner and its dependencies. if cs == nil { return nil, errors.New("miner cannot use a nil state") } if tpool == nil { return nil, errors.New("miner cannot use a nil transaction pool") } if w == nil { return nil, errors.New("miner cannot use a nil wallet") } // Grab some starting block variables. currentBlock := cs.GenesisBlock().ID() currentTarget, exists1 := cs.ChildTarget(currentBlock) earliestTimestamp, exists2 := cs.EarliestChildTimestamp(currentBlock) if build.DEBUG { if !exists1 { panic("could not get child target") } if !exists2 { panic("could not get child earliest timestamp") } } // Assemble the miner. The miner is assembled without an address because // the wallet is likely not unlocked yet. The miner will grab an address // after the miner is unlocked (this must be coded manually for each // function that potentially requires the miner to have an address. m := &Miner{ cs: cs, tpool: tpool, wallet: w, parent: currentBlock, target: currentTarget, earliestTimestamp: earliestTimestamp, blockMem: make(map[types.BlockHeader]*types.Block), arbDataMem: make(map[types.BlockHeader][]byte), headerMem: make([]types.BlockHeader, headerForWorkMemory), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 1), } err := m.initPersist() if err != nil { return nil, err } m.tpool.TransactionPoolSubscribe(m) return m, nil }
// New creates and starts up a hostdb. The hostdb that gets returned will not // have finished scanning the network or blockchain. func New() *HostDB { hdb := &HostDB{ activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), mu: sync.New(modules.SafeMutexDelay, 1), } // Begin listening to consensus and looking for hosts. for i := 0; i < scanningThreads; i++ { go hdb.threadedProbeHosts() } go hdb.threadedScan() return hdb }
// New returns an empty renter. func New(cs modules.ConsensusSet, hdb modules.HostDB, wallet modules.Wallet, tpool modules.TransactionPool, saveDir string) (*Renter, error) { if cs == nil { return nil, ErrNilCS } if hdb == nil { return nil, ErrNilHostDB } if wallet == nil { return nil, ErrNilWallet } if tpool == nil { return nil, ErrNilTpool } r := &Renter{ cs: cs, hostDB: hdb, wallet: wallet, tpool: tpool, files: make(map[string]*file), contracts: make(map[types.FileContractID]types.FileContract), saveDir: saveDir, mu: sync.New(modules.SafeMutexDelay, 1), } _, err := rand.Read(r.entropy[:]) if err != nil { return nil, err } err = os.MkdirAll(saveDir, 0700) if err != nil { return nil, err } err = r.load() if err != nil && !os.IsNotExist(err) { return nil, err } r.cs.ConsensusSetSubscribe(r) return r, nil }
// New returns an empty renter. func New(cs *consensus.State, hdb modules.HostDB, wallet modules.Wallet, saveDir string) (*Renter, error) { if cs == nil { return nil, ErrNilCS } if hdb == nil { return nil, ErrNilHostDB } if wallet == nil { return nil, ErrNilWallet } r := &Renter{ cs: cs, hostDB: hdb, wallet: wallet, files: make(map[string]*file), saveDir: saveDir, mu: sync.New(modules.SafeMutexDelay, 1), } err := os.MkdirAll(saveDir, 0700) if err != nil { return nil, err } err = r.load() if err != nil && !os.IsNotExist(err) { return nil, err } // TODO: I'm worried about balances here. Because of the way that the // re-try algorithm works, it won't be a problem, but without that we would // need to make sure that scanAllFiles() didn't get called until the entire // balance had loaded, which would require loading the entire blockchain. // This also won't be a problem once we're also saving the addresses. // // TODO: bring back this functionality when we have resumable uploads. //r.scanAllFiles() r.cs.ConsensusSetSubscribe(r) return r, nil }
// New returns an empty renter. func New(cs modules.ConsensusSet, hdb modules.HostDB, wallet modules.Wallet, tpool modules.TransactionPool, persistDir string) (*Renter, error) { if cs == nil { return nil, ErrNilCS } if hdb == nil { return nil, ErrNilHostDB } if wallet == nil { return nil, ErrNilWallet } if tpool == nil { return nil, ErrNilTpool } r := &Renter{ cs: cs, hostDB: hdb, wallet: wallet, tpool: tpool, files: make(map[string]*file), contracts: make(map[types.FileContractID]types.FileContract), repairSet: make(map[string]string), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 1), } _, err := rand.Read(r.entropy[:]) if err != nil { return nil, err } err = r.initPersist() if err != nil { return nil, err } cs.ConsensusSetSubscribe(r) go r.threadedRepairUploads() return r, nil }
func newRenter(cs modules.ConsensusSet, tpool modules.TransactionPool, hdb hostDB, hc hostContractor, persistDir string) (*Renter, error) { r := &Renter{ cs: cs, hostDB: hdb, hostContractor: hc, files: make(map[string]*file), tracking: make(map[string]trackedFile), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 1), } if err := r.initPersist(); err != nil { return nil, err } go r.threadedRepairLoop() return r, nil }
// New creates the internal data structures, and subscribes to // consensus for changes to the blockchain func New(cs modules.ConsensusSet) (be *BlockExplorer, err error) { // Check that input modules are non-nil if cs == nil { err = errors.New("Blockchain explorer cannot use a nil ConsensusSet") return } // Initilize the module state be = &BlockExplorer{ currentBlock: cs.GenesisBlock(), genesisBlockID: cs.GenesisBlock().ID(), blockchainHeight: 0, currencySent: types.NewCurrency64(0), activeContractCost: types.NewCurrency64(0), totalContractCost: types.NewCurrency64(0), cs: cs, mu: sync.New(modules.SafeMutexDelay, 1), } cs.ConsensusSetSubscribe(be) return }
// NewServer creates a new API server from the provided modules. func NewServer(APIaddr string, s *consensus.State, g modules.Gateway, h modules.Host, hdb modules.HostDB, m modules.Miner, r modules.Renter, tp modules.TransactionPool, w modules.Wallet, be modules.BlockExplorer) (*Server, error) { srv := &Server{ cs: s, gateway: g, host: h, hostdb: hdb, miner: m, renter: r, tpool: tp, wallet: w, blocke: be, mu: sync.New(modules.SafeMutexDelay, 1), } // Set the genesis block and start listening to the consensus package. srv.currentBlock = srv.cs.GenesisBlock() srv.cs.ConsensusSetSubscribe(srv) // Register API handlers srv.initAPI(APIaddr) return srv, nil }
// New creates and starts up a hostdb. The hostdb that gets returned will not // have finished scanning the network or blockchain. func New(cs modules.ConsensusSet, g modules.Gateway, persistDir string) (*HostDB, error) { // Check for nil dependencies. if cs == nil { return nil, errNilConsensusSet } if g == nil { return nil, errNilGateway } // Build an empty hostdb. hdb := &HostDB{ consensusSet: cs, gateway: g, activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), persistDir: persistDir, mu: sync.New(modules.SafeMutexDelay, 1), } err := hdb.initPersist() if err != nil { return nil, err } // Begin listening to consensus and looking for hosts. for i := 0; i < scanningThreads; i++ { go hdb.threadedProbeHosts() } go hdb.threadedScan() cs.ConsensusSetSubscribe(hdb) return hdb, nil }
// New creates a transaction pool that is ready to receive transactions. func New(cs modules.ConsensusSet, g modules.Gateway) (tp *TransactionPool, err error) { // Check that the input modules are non-nil. if cs == nil { err = errors.New("transaction pool cannot use a nil state") return } if g == nil { err = errors.New("transaction pool cannot use a nil gateway") return } // Initialize a transaction pool. tp = &TransactionPool{ consensusSet: cs, gateway: g, transactions: make(map[crypto.Hash]struct{}), siacoinOutputs: make(map[types.SiacoinOutputID]types.SiacoinOutput), fileContracts: make(map[types.FileContractID]types.FileContract), siafundOutputs: make(map[types.SiafundOutputID]types.SiafundOutput), referenceSiacoinOutputs: make(map[types.SiacoinOutputID]types.SiacoinOutput), referenceFileContracts: make(map[types.FileContractID]types.FileContract), referenceSiafundOutputs: make(map[types.SiafundOutputID]types.SiafundOutput), mu: sync.New(modules.SafeMutexDelay, 1), } // Register RPCs g.RegisterRPC("RelayTransaction", tp.RelayTransaction) // Subscribe the transaction pool to the consensus set. cs.ConsensusSetSubscribe(tp) return }
// New returns a new ConsensusSet, containing at least the genesis block. If // there is an existing block database present in saveDir, it will be loaded. // Otherwise, a new database will be created. func New(gateway modules.Gateway, saveDir string) (*ConsensusSet, error) { if gateway == nil { return nil, ErrNilGateway } // Create the genesis block. genesisBlock := types.Block{ Timestamp: types.GenesisTimestamp, Transactions: []types.Transaction{ {SiafundOutputs: types.GenesisSiafundAllocation}, }, } // Create the ConsensusSet object. cs := &ConsensusSet{ gateway: gateway, blockRoot: processedBlock{ Block: genesisBlock, ChildTarget: types.RootTarget, Depth: types.RootDepth, DiffsGenerated: true, }, dosBlocks: make(map[types.BlockID]struct{}), mu: sync.New(modules.SafeMutexDelay, 1), } // Allocate the Siafund addresses by putting them all in a big transaction inside the genesis block for i, siafundOutput := range genesisBlock.Transactions[0].SiafundOutputs { sfid := genesisBlock.Transactions[0].SiafundOutputID(i) sfod := modules.SiafundOutputDiff{ Direction: modules.DiffApply, ID: sfid, SiafundOutput: siafundOutput, } cs.blockRoot.SiafundOutputDiffs = append(cs.blockRoot.SiafundOutputDiffs, sfod) } // Create the consensus directory. err := os.MkdirAll(saveDir, 0700) if err != nil { return nil, err } // Try to load an existing database from disk. err = cs.load(saveDir) if err != nil { return nil, err } // Send the genesis block to subscribers. cs.updateSubscribers(nil, []*processedBlock{&cs.blockRoot}) // Send any blocks that were loaded from disk to subscribers. cs.loadDiffs() // Register RPCs gateway.RegisterRPC("SendBlocks", cs.sendBlocks) gateway.RegisterRPC("RelayBlock", cs.RelayBlock) gateway.RegisterConnectCall("SendBlocks", cs.receiveBlocks) return cs, nil }
// New returns an initialized Host. func New(cs *consensus.ConsensusSet, hdb modules.HostDB, tpool modules.TransactionPool, wallet modules.Wallet, addr string, saveDir string) (*Host, error) { if cs == nil { return nil, errors.New("host cannot use a nil state") } if hdb == nil { return nil, errors.New("host cannot use a nil hostdb") } if tpool == nil { return nil, errors.New("host cannot use a nil tpool") } if wallet == nil { return nil, errors.New("host cannot use a nil wallet") } // Create host directory if it does not exist. err := os.MkdirAll(saveDir, 0700) if err != nil { return nil, err } h := &Host{ cs: cs, hostdb: hdb, tpool: tpool, wallet: wallet, // default host settings HostSettings: modules.HostSettings{ TotalStorage: 10e9, // 10 GB MaxFilesize: 5 * 1024 * 1024 * 1024, // 5 GiB MaxDuration: 144 * 60, // 60 days WindowSize: 288, // 48 hours Price: types.NewCurrency64(100e12), // 0.1 siacoin / mb / week Collateral: types.NewCurrency64(0), }, saveDir: saveDir, obligationsByID: make(map[types.FileContractID]contractObligation), obligationsByHeight: make(map[types.BlockHeight][]contractObligation), mu: safesync.New(modules.SafeMutexDelay, 1), } h.spaceRemaining = h.TotalStorage // Generate signing key, for revising contracts. sk, pk, err := crypto.StdKeyGen.Generate() if err != nil { return nil, err } h.secretKey = sk h.publicKey = types.SiaPublicKey{ Algorithm: types.SignatureEd25519, Key: pk[:], } // Load the old host data. err = h.load() if err != nil && !os.IsNotExist(err) { return nil, err } // Create listener and set address. h.listener, err = net.Listen("tcp", addr) if err != nil { return nil, err } _, port, _ := net.SplitHostPort(h.listener.Addr().String()) h.myAddr = modules.NetAddress(net.JoinHostPort("::1", port)) // Learn our external IP. go h.learnHostname() // Forward the hosting port, if possible. go h.forwardPort(port) // spawn listener go h.listen() h.cs.ConsensusSetSubscribe(h) return h, nil }
// TestWeightedList inserts and removes nodes in a semi-random manner and // verifies that the tree stays consistent through the adjustments. func TestWeightedList(t *testing.T) { if testing.Short() { t.SkipNow() } // Create a hostdb and 3 equal entries to insert. hdb := &HostDB{ activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), mu: sync.New(modules.SafeMutexDelay, 1), } // Create a bunch of host entries of equal weight. firstInsertions := 64 for i := 0; i < firstInsertions; i++ { entry := hostEntry{ HostSettings: modules.HostSettings{IPAddress: fakeAddr(uint8(i))}, weight: types.NewCurrency64(10), } hdb.insertNode(&entry) } err := uniformTreeVerification(hdb, firstInsertions) if err != nil { t.Error(err) } // Remove a few hosts and check that the tree is still in order. removals := 12 // Keep a map of what we've removed so far. removedMap := make(map[uint8]struct{}) for i := 0; i < removals; i++ { // Try numbers until we roll a number that's not been removed yet. var randInt uint8 for { randBig, err := rand.Int(rand.Reader, big.NewInt(int64(firstInsertions))) if err != nil { t.Fatal(err) } randInt = uint8(randBig.Int64()) _, exists := removedMap[randInt] if !exists { break } } // Remove the entry and add it to the list of removed entries err := hdb.RemoveHost(fakeAddr(randInt)) if err != nil { t.Fatal(err) } removedMap[randInt] = struct{}{} } err = uniformTreeVerification(hdb, firstInsertions-removals) if err != nil { t.Error(err) } // Do some more insertions. secondInsertions := 64 for i := firstInsertions; i < firstInsertions+secondInsertions; i++ { entry := hostEntry{ HostSettings: modules.HostSettings{IPAddress: fakeAddr(uint8(i))}, weight: types.NewCurrency64(10), } hdb.insertNode(&entry) } err = uniformTreeVerification(hdb, firstInsertions-removals+secondInsertions) if err != nil { t.Error(err) } }
// TestRandomHosts probles the RandomHosts function. func TestRandomHosts(t *testing.T) { if testing.Short() { t.SkipNow() } hdb := &HostDB{ activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), mu: sync.New(modules.SafeMutexDelay, 1), } // Insert 3 hosts to be selected. entry1 := hostEntry{ HostSettings: modules.HostSettings{IPAddress: fakeAddr(1)}, weight: types.NewCurrency64(1), } entry2 := hostEntry{ HostSettings: modules.HostSettings{IPAddress: fakeAddr(2)}, weight: types.NewCurrency64(2), } entry3 := hostEntry{ HostSettings: modules.HostSettings{IPAddress: fakeAddr(3)}, weight: types.NewCurrency64(3), } hdb.insertNode(&entry1) hdb.insertNode(&entry2) hdb.insertNode(&entry3) if len(hdb.activeHosts) != 3 { t.Error("wrong number of hosts") } if hdb.hostTree.weight.Cmp(types.NewCurrency64(6)) != 0 { t.Error("unexpected weight at initialization") t.Error(hdb.hostTree.weight) } // Grab 1 random host. randHosts := hdb.RandomHosts(1) if len(randHosts) != 1 { t.Error("didn't get 1 hosts") } if len(hdb.activeHosts) != 3 { t.Error("wrong number of hosts") } if hdb.hostTree.weight.Cmp(types.NewCurrency64(6)) != 0 { t.Error("unexpected weight at initialization") } // Grab 2 random hosts. randHosts = hdb.RandomHosts(2) if len(randHosts) != 2 { t.Error("didn't get 2 hosts") } if len(hdb.activeHosts) != 3 { t.Error("wrong number of hosts") } if hdb.hostTree.weight.Cmp(types.NewCurrency64(6)) != 0 { t.Error("unexpected weight at initialization") } if randHosts[0].IPAddress == randHosts[1].IPAddress { t.Error("doubled up") } // Grab 3 random hosts. randHosts = hdb.RandomHosts(3) if len(randHosts) != 3 { t.Error("didn't get 3 hosts") } if len(hdb.activeHosts) != 3 { t.Error("wrong number of hosts") } if hdb.hostTree.weight.Cmp(types.NewCurrency64(6)) != 0 { t.Error("unexpected weight at initialization") } if randHosts[0].IPAddress == randHosts[1].IPAddress || randHosts[0].IPAddress == randHosts[2].IPAddress || randHosts[1].IPAddress == randHosts[2].IPAddress { t.Error("doubled up") } // Grab 4 random hosts. 3 should be returned. randHosts = hdb.RandomHosts(4) if len(randHosts) != 3 { t.Error("didn't get 3 hosts") } if len(hdb.activeHosts) != 3 { t.Error("wrong number of hosts") } if hdb.hostTree.weight.Cmp(types.NewCurrency64(6)) != 0 { t.Error("unexpected weight at initialization") } if randHosts[0].IPAddress == randHosts[1].IPAddress || randHosts[0].IPAddress == randHosts[2].IPAddress || randHosts[1].IPAddress == randHosts[2].IPAddress { t.Error("doubled up") } }
// New returns a new State, containing at least the genesis block. If there is // an existing block database present in saveDir, it will be loaded. Otherwise, // a new database will be created. func New(gateway modules.Gateway, saveDir string) (*State, error) { if gateway == nil { return nil, ErrNilGateway } // Create the State object. cs := &State{ blockMap: make(map[types.BlockID]*blockNode), dosBlocks: make(map[types.BlockID]struct{}), currentPath: make([]types.BlockID, 1), siacoinOutputs: make(map[types.SiacoinOutputID]types.SiacoinOutput), fileContracts: make(map[types.FileContractID]types.FileContract), siafundOutputs: make(map[types.SiafundOutputID]types.SiafundOutput), delayedSiacoinOutputs: make(map[types.BlockHeight]map[types.SiacoinOutputID]types.SiacoinOutput), gateway: gateway, mu: sync.New(modules.SafeMutexDelay, 1), } // Create the genesis block and add it as the BlockRoot. genesisBlock := types.Block{ Timestamp: types.GenesisTimestamp, Transactions: []types.Transaction{ {SiafundOutputs: types.GenesisSiafundAllocation}, }, } cs.blockRoot = &blockNode{ block: genesisBlock, childTarget: types.RootTarget, depth: types.RootDepth, diffsGenerated: true, } cs.blockMap[genesisBlock.ID()] = cs.blockRoot // Fill out the consensus information for the genesis block. cs.currentPath[0] = genesisBlock.ID() cs.siacoinOutputs[genesisBlock.MinerPayoutID(0)] = types.SiacoinOutput{ Value: types.CalculateCoinbase(0), UnlockHash: types.ZeroUnlockHash, } // Allocate the Siafund addresses by putting them all in a big transaction // and applying the diffs. for i, siafundOutput := range genesisBlock.Transactions[0].SiafundOutputs { sfid := genesisBlock.Transactions[0].SiafundOutputID(i) sfod := modules.SiafundOutputDiff{ Direction: modules.DiffApply, ID: sfid, SiafundOutput: siafundOutput, } cs.commitSiafundOutputDiff(sfod, modules.DiffApply) cs.blockRoot.siafundOutputDiffs = append(cs.blockRoot.siafundOutputDiffs, sfod) } // Send out genesis block update. cs.updateSubscribers(nil, []*blockNode{cs.blockRoot}) // Create the consensus directory. err := os.MkdirAll(saveDir, 0700) if err != nil { return nil, err } // During short tests, use an in-memory database. if build.Release == "testing" && testing.Short() { cs.db = persist.NilDB } else { // Otherwise, try to load an existing database from disk. err = cs.load(saveDir) if err != nil { return nil, err } } // Register RPCs gateway.RegisterRPC("SendBlocks", cs.sendBlocks) gateway.RegisterRPC("RelayBlock", cs.RelayBlock) gateway.RegisterConnectCall("SendBlocks", cs.receiveBlocks) // Spawn resynchronize loop. go cs.threadedResynchronize() return cs, nil }
// New returns an initialized Host. func New(cs *consensus.State, hdb modules.HostDB, tpool modules.TransactionPool, wallet modules.Wallet, addr string, saveDir string) (*Host, error) { if cs == nil { return nil, errors.New("host cannot use a nil state") } if hdb == nil { return nil, errors.New("host cannot use a nil hostdb") } if tpool == nil { return nil, errors.New("host cannot use a nil tpool") } if wallet == nil { return nil, errors.New("host cannot use a nil wallet") } coinAddr, _, err := wallet.CoinAddress(false) // false indicates that the address should not be visible to the user. if err != nil { return nil, err } h := &Host{ cs: cs, hostdb: hdb, tpool: tpool, wallet: wallet, // default host settings HostSettings: modules.HostSettings{ TotalStorage: 10e9, // 10 GB MaxFilesize: 1e9, // 1 GB MaxDuration: 144 * 60, // 60 days WindowSize: 288, // 48 hours Price: types.NewCurrency64(100e12), // 0.1 siacoin / mb / week Collateral: types.NewCurrency64(0), UnlockHash: coinAddr, }, saveDir: saveDir, obligationsByID: make(map[types.FileContractID]contractObligation), obligationsByHeight: make(map[types.BlockHeight][]contractObligation), mu: sync.New(modules.SafeMutexDelay, 1), } h.spaceRemaining = h.TotalStorage // Create listener and set address. h.listener, err = net.Listen("tcp", addr) if err != nil { return nil, err } _, port, _ := net.SplitHostPort(h.listener.Addr().String()) h.myAddr = modules.NetAddress(net.JoinHostPort(modules.ExternalIP, port)) err = os.MkdirAll(saveDir, 0700) if err != nil { return nil, err } err = h.load() if err != nil && !os.IsNotExist(err) { return nil, err } // spawn listener go h.listen() h.cs.ConsensusSetSubscribe(h) return h, nil }