// New creates the internal data structures, and subscribes to // consensus for changes to the blockchain func New(cs modules.ConsensusSet, persistDir string) (*Explorer, error) { // Check that input modules are non-nil if cs == nil { return nil, errNilCS } // Initialize the explorer. e := &Explorer{ currentBlock: cs.GenesisBlock(), genesisBlockID: cs.GenesisBlock().ID(), seenTimes: make([]time.Time, types.MaturityDelay+1), startTime: time.Now(), cs: cs, persistDir: persistDir, } e.blockchainHeight-- // Set to -1 so the genesis block sets the height to 0. // Intialize the persistent structures, including the database. err := e.initPersist() if err != nil { return nil, err } cs.ConsensusSetSubscribe(e) return e, nil }
// New creates the internal data structures, and subscribes to // consensus for changes to the blockchain func New(cs modules.ConsensusSet, persistDir string) (*Explorer, error) { // Check that input modules are non-nil if cs == nil { return nil, errNilCS } // Initialize the explorer. e := &Explorer{ blocksDifficulty: types.RootDepth, blockHashes: make(map[types.BlockID]types.BlockHeight), blockTargets: make(map[types.BlockID]types.Target), transactionHashes: make(map[types.TransactionID]types.BlockHeight), unlockHashes: make(map[types.UnlockHash]map[types.TransactionID]struct{}), siacoinOutputIDs: make(map[types.SiacoinOutputID]map[types.TransactionID]struct{}), siacoinOutputs: make(map[types.SiacoinOutputID]types.SiacoinOutput), fileContractIDs: make(map[types.FileContractID]map[types.TransactionID]struct{}), fileContractHistories: make(map[types.FileContractID]*fileContractHistory), siafundOutputIDs: make(map[types.SiafundOutputID]map[types.TransactionID]struct{}), siafundOutputs: make(map[types.SiafundOutputID]types.SiafundOutput), cs: cs, persistDir: persistDir, } e.blockchainHeight-- // Set to -1 so the genesis block sets the height to 0. // Intialize the persistent structures, including the database. err := e.initPersist() if err != nil { return nil, err } cs.ConsensusSetPersistentSubscribe(e, modules.ConsensusChangeID{}) return e, nil }
// New creates the internal data structures, and subscribes to // consensus for changes to the blockchain func New(cs modules.ConsensusSet, persistDir string) (*Explorer, error) { // Check that input modules are non-nil if cs == nil { return nil, errNilCS } // Initialize the explorer. e := &Explorer{ cs: cs, persistDir: persistDir, } // Initialize the persistent structures, including the database. err := e.initPersist() if err != nil { return nil, err } // retrieve the current ConsensusChangeID var recentChange modules.ConsensusChangeID err = e.db.View(dbGetInternal(internalRecentChange, &recentChange)) if err != nil { return nil, err } err = cs.ConsensusSetSubscribe(e, recentChange) if err != nil { // TODO: restart from 0 return nil, errors.New("explorer subscription failed: " + err.Error()) } return e, 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 }
// newAnnouncementFinder will create and return an announcement finder. func newAnnouncementFinder(cs modules.ConsensusSet) (*announcementFinder, error) { af := &announcementFinder{ cs: cs, } err := cs.ConsensusSetSubscribe(af, modules.ConsensusChangeBeginning) if err != nil { return nil, err } return af, 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 }
// 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, wallet modules.Wallet, tpool modules.TransactionPool, persistDir string) (*HostDB, error) { if cs == nil { return nil, errNilCS } if wallet == nil { return nil, errNilWallet } if tpool == nil { return nil, errNilTpool } hdb := &HostDB{ cs: cs, wallet: wallet, tpool: tpool, contracts: make(map[types.FileContractID]hostContract), activeHosts: make(map[modules.NetAddress]*hostNode), allHosts: make(map[modules.NetAddress]*hostEntry), scanPool: make(chan *hostEntry, scanPoolSize), persistDir: persistDir, } 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 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 }
// 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 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) (*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, } // Register RPCs // TODO: rename RelayTransactionSet so that the conflicting RPC // RelayTransaction calls v0.4.6 clients and earlier are ignored. g.RegisterRPC("RelayTransactionSet", tp.relayTransactionSet) // Subscribe the transaction pool to the consensus set. err := cs.ConsensusSetPersistentSubscribe(tp, modules.ConsensusChangeID{}) if err != nil { return nil, errors.New("transactionpool subscription failed: " + err.Error()) } return tp, 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") } } 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 }
func dbCalculateBlockFacts(tx *bolt.Tx, cs modules.ConsensusSet, block types.Block) blockFacts { // get the parent block facts var bf blockFacts err := dbGetAndDecode(bucketBlockFacts, block.ParentID, &bf)(tx) assertNil(err) // get target target, exists := cs.ChildTarget(block.ParentID) if !exists { panic(fmt.Sprint("ConsensusSet is missing target of known block", block.ParentID)) } // update fields bf.BlockID = block.ID() bf.Height++ bf.Difficulty = target.Difficulty() bf.Target = target bf.Timestamp = block.Timestamp bf.TotalCoins = types.CalculateNumSiacoins(bf.Height) // calculate maturity timestamp var maturityTimestamp types.Timestamp if bf.Height > types.MaturityDelay { oldBlock, exists := cs.BlockAtHeight(bf.Height - types.MaturityDelay) if !exists { panic(fmt.Sprint("ConsensusSet is missing block at height", bf.Height-types.MaturityDelay)) } maturityTimestamp = oldBlock.Timestamp } bf.MaturityTimestamp = maturityTimestamp // calculate hashrate by averaging last 'hashrateEstimationBlocks' blocks var estimatedHashrate types.Currency if bf.Height > hashrateEstimationBlocks { var totalDifficulty = bf.Target var oldestTimestamp types.Timestamp for i := types.BlockHeight(1); i < hashrateEstimationBlocks; i++ { b, exists := cs.BlockAtHeight(bf.Height - i) if !exists { panic(fmt.Sprint("ConsensusSet is missing block at height", bf.Height-hashrateEstimationBlocks)) } target, exists := cs.ChildTarget(b.ParentID) if !exists { panic(fmt.Sprint("ConsensusSet is missing target of known block", b.ParentID)) } totalDifficulty = totalDifficulty.AddDifficulties(target) oldestTimestamp = b.Timestamp } secondsPassed := bf.Timestamp - oldestTimestamp estimatedHashrate = totalDifficulty.Difficulty().Div64(uint64(secondsPassed)) } bf.EstimatedHashrate = estimatedHashrate bf.MinerPayoutCount += uint64(len(block.MinerPayouts)) bf.TransactionCount += uint64(len(block.Transactions)) for _, txn := range block.Transactions { bf.SiacoinInputCount += uint64(len(txn.SiacoinInputs)) bf.SiacoinOutputCount += uint64(len(txn.SiacoinOutputs)) bf.FileContractCount += uint64(len(txn.FileContracts)) bf.FileContractRevisionCount += uint64(len(txn.FileContractRevisions)) bf.StorageProofCount += uint64(len(txn.StorageProofs)) bf.SiafundInputCount += uint64(len(txn.SiafundInputs)) bf.SiafundOutputCount += uint64(len(txn.SiafundOutputs)) bf.MinerFeeCount += uint64(len(txn.MinerFees)) bf.ArbitraryDataCount += uint64(len(txn.ArbitraryData)) bf.TransactionSignatureCount += uint64(len(txn.TransactionSignatures)) for _, fc := range txn.FileContracts { bf.TotalContractCost = bf.TotalContractCost.Add(fc.Payout) bf.TotalContractSize = bf.TotalContractSize.Add(types.NewCurrency64(fc.FileSize)) } for _, fcr := range txn.FileContractRevisions { bf.TotalContractSize = bf.TotalContractSize.Add(types.NewCurrency64(fcr.NewFileSize)) bf.TotalRevisionVolume = bf.TotalRevisionVolume.Add(types.NewCurrency64(fcr.NewFileSize)) } } return bf }
// startDaemon uses the config parameters to initialize Sia modules and start // siad. func startDaemon(config Config) (err error) { // Prompt user for API password. if config.Siad.AuthenticateAPI { config.APIPassword, err = speakeasy.Ask("Enter API password: "******"" { return errors.New("password cannot be blank") } } // Process the config variables after they are parsed by cobra. config, err = processConfig(config) if err != nil { return err } // Print a startup message. fmt.Println("Loading...") loadStart := time.Now() // Create the server and start serving daemon routes immediately. fmt.Printf("(0/%d) Loading siad...\n", len(config.Siad.Modules)) srv, err := NewServer(config.Siad.APIaddr, config.Siad.RequiredUserAgent, config.APIPassword) if err != nil { return err } servErrs := make(chan error) go func() { servErrs <- srv.Serve() }() // Initialize the Sia 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, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.GatewayDir)) if err != nil { return err } defer g.Close() } 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, !config.Siad.NoBootstrap, filepath.Join(config.Siad.SiaDir, modules.ConsensusDir)) if err != nil { return err } defer cs.Close() } 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 } defer e.Close() } 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 } defer tpool.Close() } 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 } defer w.Close() } 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 } defer m.Close() } 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 } defer h.Close() } 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 } defer r.Close() } // Create the Sia API a := api.New( config.Siad.RequiredUserAgent, config.APIPassword, cs, e, g, h, m, r, tpool, w, ) // connect the API to the server srv.mux.Handle("/", a) // stop the server if a kill signal is caught sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, os.Kill) go func() { <-sigChan fmt.Println("\rCaught stop signal, quitting...") srv.Close() }() // Print a 'startup complete' message. startupTime := time.Since(loadStart) fmt.Println("Finished loading in", startupTime.Seconds(), "seconds") err = <-servErrs if err != nil { build.Critical(err) } return nil }