github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/node/node.go (about)

     1  // Package node provides tooling for creating a Sia node. Sia nodes consist of a
     2  // collection of modules. The node package gives you tools to easily assemble
     3  // various combinations of modules with varying dependencies and settings,
     4  // including templates for assembling sane no-hassle Sia nodes.
     5  package node
     6  
     7  // TODO: Add support for the explorer.
     8  
     9  // TODO: Add support for custom dependencies and parameters for all of the
    10  // modules.
    11  
    12  import (
    13  	"path/filepath"
    14  
    15  	"github.com/Synthesix/Sia/modules"
    16  	"github.com/Synthesix/Sia/modules/consensus"
    17  	"github.com/Synthesix/Sia/modules/gateway"
    18  	"github.com/Synthesix/Sia/modules/host"
    19  	"github.com/Synthesix/Sia/modules/miner"
    20  	"github.com/Synthesix/Sia/modules/renter"
    21  	"github.com/Synthesix/Sia/modules/renter/contractor"
    22  	"github.com/Synthesix/Sia/modules/renter/hostdb"
    23  	"github.com/Synthesix/Sia/modules/renter/proto"
    24  	"github.com/Synthesix/Sia/modules/transactionpool"
    25  	"github.com/Synthesix/Sia/modules/wallet"
    26  	"github.com/Synthesix/Sia/persist"
    27  
    28  	"github.com/NebulousLabs/errors"
    29  )
    30  
    31  // NodeParams contains a bunch of parameters for creating a new test node. As
    32  // there are many options, templates are provided that you can modify which
    33  // cover the most common use cases.
    34  //
    35  // Each module is created separately. There are several ways to create a module,
    36  // though not all methods are currently available for each module. You should
    37  // only use one method for creating a module, using multiple methods will cause
    38  // an error.
    39  //		+ Indicate with the 'CreateModule' bool that a module should be created
    40  //		  automatically. To create the module with custom dependencies, pass the
    41  //		  custom dependencies in using the 'ModuleDependencies' field.
    42  //		+ Pass an existing module in directly.
    43  //		+ Set 'CreateModule' to false and do not pass in an existing module.
    44  //		  This will result in a 'nil' module, meaning the node will not have
    45  //		  that module.
    46  type NodeParams struct {
    47  	// Flags to indicate which modules should be created automatically by the
    48  	// server. If you are providing a pre-existing module, do not set the flag
    49  	// for that module.
    50  	//
    51  	// NOTE / TODO: The code does not currently enforce this, but you should not
    52  	// provide a custom module unless all of its dependencies are also custom.
    53  	// Example: if the ConsensusSet is custom, the Gateway should also be
    54  	// custom. The TransactionPool however does not need to be custom in this
    55  	// example.
    56  	CreateConsensusSet    bool
    57  	CreateExplorer        bool
    58  	CreateGateway         bool
    59  	CreateHost            bool
    60  	CreateMiner           bool
    61  	CreateRenter          bool
    62  	CreateTransactionPool bool
    63  	CreateWallet          bool
    64  
    65  	// Custom modules - if the modules is provided directly, the provided
    66  	// module will be used instead of creating a new one. If a custom module is
    67  	// provided, the 'omit' flag for that module must be set to false (which is
    68  	// the default setting).
    69  	ConsensusSet    modules.ConsensusSet
    70  	Explorer        modules.Explorer
    71  	Gateway         modules.Gateway
    72  	Host            modules.Host
    73  	Miner           modules.TestMiner
    74  	Renter          modules.Renter
    75  	TransactionPool modules.TransactionPool
    76  	Wallet          modules.Wallet
    77  
    78  	// Dependencies for each module supporting dependency injection.
    79  	ContractorDeps  modules.Dependencies
    80  	ContractSetDeps modules.Dependencies
    81  	HostDBDeps      modules.Dependencies
    82  	RenterDeps      modules.Dependencies
    83  	WalletDeps      modules.Dependencies
    84  
    85  	// The high level directory where all the persistence gets stored for the
    86  	// moudles.
    87  	Dir string
    88  }
    89  
    90  // Node is a collection of Sia modules operating together as a Sia node.
    91  type Node struct {
    92  	// The modules of the node. Modules that are not initialized will be nil.
    93  	ConsensusSet    modules.ConsensusSet
    94  	Explorer        modules.Explorer
    95  	Gateway         modules.Gateway
    96  	Host            modules.Host
    97  	Miner           modules.TestMiner
    98  	Renter          modules.Renter
    99  	TransactionPool modules.TransactionPool
   100  	Wallet          modules.Wallet
   101  
   102  	// The high level directory where all the persistence gets stored for the
   103  	// moudles.
   104  	Dir string
   105  }
   106  
   107  // Close will call close on every module within the node, combining and
   108  // returning the errors.
   109  func (n *Node) Close() (err error) {
   110  	if n.Explorer != nil {
   111  		err = errors.Compose(n.Explorer.Close())
   112  	}
   113  	if n.Miner != nil {
   114  		err = errors.Compose(n.Miner.Close())
   115  	}
   116  	if n.Host != nil {
   117  		err = errors.Compose(n.Host.Close())
   118  	}
   119  	if n.Renter != nil {
   120  		err = errors.Compose(n.Renter.Close())
   121  	}
   122  	if n.Wallet != nil {
   123  		err = errors.Compose(n.Wallet.Close())
   124  	}
   125  	if n.TransactionPool != nil {
   126  		err = errors.Compose(n.TransactionPool.Close())
   127  	}
   128  	if n.ConsensusSet != nil {
   129  		err = errors.Compose(n.ConsensusSet.Close())
   130  	}
   131  	if n.Gateway != nil {
   132  		err = errors.Compose(n.Gateway.Close())
   133  	}
   134  	return err
   135  }
   136  
   137  // New will create a new test node. The inputs to the function are the
   138  // respective 'New' calls for each module. We need to use this awkward method
   139  // of initialization because the siatest package cannot import any of the
   140  // modules directly (so that the modules may use the siatest package to test
   141  // themselves).
   142  func New(params NodeParams) (*Node, error) {
   143  	dir := params.Dir
   144  
   145  	// Gateway.
   146  	g, err := func() (modules.Gateway, error) {
   147  		if params.CreateGateway && params.Gateway != nil {
   148  			return nil, errors.New("cannot both create a gateway and use a passed in gateway")
   149  		}
   150  		/* Template for dealing with optional dependencies:
   151  		if !params.CreateGateway && parames.GatewayDependencies != nil {
   152  			return nil, errors.New("cannot pass in gateway dependencies if you are not creating a gateway")
   153  		}
   154  		*/
   155  		if params.Gateway != nil {
   156  			return params.Gateway, nil
   157  		}
   158  		if !params.CreateGateway {
   159  			return nil, nil
   160  		}
   161  		/* Template for dealing with optional dependencies:
   162  		if params.GatewayDependencies == nil {
   163  			gateway.New(...
   164  		} else {
   165  			gateway.NewDeps(...
   166  		}
   167  		*/
   168  		return gateway.New("localhost:0", false, filepath.Join(dir, modules.GatewayDir))
   169  	}()
   170  	if err != nil {
   171  		return nil, errors.Extend(err, errors.New("unable to create gateway"))
   172  	}
   173  
   174  	// Consensus.
   175  	cs, err := func() (modules.ConsensusSet, error) {
   176  		if params.CreateConsensusSet && params.ConsensusSet != nil {
   177  			return nil, errors.New("cannot both create consensus and use passed in consensus")
   178  		}
   179  		if params.ConsensusSet != nil {
   180  			return params.ConsensusSet, nil
   181  		}
   182  		if !params.CreateConsensusSet {
   183  			return nil, nil
   184  		}
   185  		return consensus.New(g, false, filepath.Join(dir, modules.ConsensusDir))
   186  	}()
   187  	if err != nil {
   188  		return nil, errors.Extend(err, errors.New("unable to create consensus set"))
   189  	}
   190  
   191  	// Transaction Pool.
   192  	tp, err := func() (modules.TransactionPool, error) {
   193  		if params.CreateTransactionPool && params.TransactionPool != nil {
   194  			return nil, errors.New("cannot create transaction pool and also use custom transaction pool")
   195  		}
   196  		if params.TransactionPool != nil {
   197  			return params.TransactionPool, nil
   198  		}
   199  		if !params.CreateTransactionPool {
   200  			return nil, nil
   201  		}
   202  		return transactionpool.New(cs, g, filepath.Join(dir, modules.TransactionPoolDir))
   203  	}()
   204  	if err != nil {
   205  		return nil, errors.Extend(err, errors.New("unable to create transaction pool"))
   206  	}
   207  
   208  	// Wallet.
   209  	w, err := func() (modules.Wallet, error) {
   210  		if params.CreateWallet && params.Wallet != nil {
   211  			return nil, errors.New("cannot create wallet and use custom wallet")
   212  		}
   213  		if params.Wallet != nil {
   214  			return params.Wallet, nil
   215  		}
   216  		if !params.CreateWallet {
   217  			return nil, nil
   218  		}
   219  		walletDeps := params.WalletDeps
   220  		if walletDeps == nil {
   221  			walletDeps = modules.ProdDependencies
   222  		}
   223  		return wallet.NewCustomWallet(cs, tp, filepath.Join(dir, modules.WalletDir), walletDeps)
   224  	}()
   225  	if err != nil {
   226  		return nil, errors.Extend(err, errors.New("unable to create wallet"))
   227  	}
   228  
   229  	// Host.
   230  	h, err := func() (modules.Host, error) {
   231  		if params.CreateHost && params.Host != nil {
   232  			return nil, errors.New("cannot create host and use custom host")
   233  		}
   234  		if params.Host != nil {
   235  			return params.Host, nil
   236  		}
   237  		if !params.CreateHost {
   238  			return nil, nil
   239  		}
   240  		return host.New(cs, tp, w, "localhost:0", filepath.Join(dir, modules.HostDir))
   241  	}()
   242  	if err != nil {
   243  		return nil, errors.Extend(err, errors.New("unable to create host"))
   244  	}
   245  
   246  	// Renter.
   247  	r, err := func() (modules.Renter, error) {
   248  		if params.CreateRenter && params.Renter != nil {
   249  			return nil, errors.New("cannot create renter and also use custom renter")
   250  		}
   251  		if params.Renter != nil {
   252  			return params.Renter, nil
   253  		}
   254  		if !params.CreateRenter {
   255  			return nil, nil
   256  		}
   257  		contractorDeps := params.ContractorDeps
   258  		if contractorDeps == nil {
   259  			contractorDeps = modules.ProdDependencies
   260  		}
   261  		contractSetDeps := params.ContractSetDeps
   262  		if contractSetDeps == nil {
   263  			contractSetDeps = modules.ProdDependencies
   264  		}
   265  		hostDBDeps := params.HostDBDeps
   266  		if hostDBDeps == nil {
   267  			hostDBDeps = modules.ProdDependencies
   268  		}
   269  		renterDeps := params.RenterDeps
   270  		if renterDeps == nil {
   271  			renterDeps = modules.ProdDependencies
   272  		}
   273  		persistDir := filepath.Join(dir, modules.RenterDir)
   274  
   275  		// HostDB
   276  		hdb, err := hostdb.NewCustomHostDB(g, cs, persistDir, hostDBDeps)
   277  		if err != nil {
   278  			return nil, err
   279  		}
   280  		// ContractSet
   281  		contractSet, err := proto.NewContractSet(filepath.Join(persistDir, "contracts"), contractSetDeps)
   282  		if err != nil {
   283  			return nil, err
   284  		}
   285  		// Contractor
   286  		logger, err := persist.NewFileLogger(filepath.Join(persistDir, "contractor.log"))
   287  		if err != nil {
   288  			return nil, err
   289  		}
   290  		hc, err := contractor.NewCustomContractor(cs, &contractor.WalletBridge{W: w}, tp, hdb, contractSet, contractor.NewPersist(persistDir), logger, contractorDeps)
   291  		if err != nil {
   292  			return nil, err
   293  		}
   294  		return renter.NewCustomRenter(g, cs, tp, hdb, hc, persistDir, renterDeps)
   295  	}()
   296  	if err != nil {
   297  		return nil, errors.Extend(err, errors.New("unable to create renter"))
   298  	}
   299  
   300  	// Miner.
   301  	m, err := func() (modules.TestMiner, error) {
   302  		if params.CreateMiner && params.Miner != nil {
   303  			return nil, errors.New("cannot create miner and also use custom miner")
   304  		}
   305  		if params.Miner != nil {
   306  			return params.Miner, nil
   307  		}
   308  		if !params.CreateMiner {
   309  			return nil, nil
   310  		}
   311  		m, err := miner.New(cs, tp, w, filepath.Join(dir, modules.MinerDir))
   312  		if err != nil {
   313  			return nil, err
   314  		}
   315  		return m, nil
   316  	}()
   317  	if err != nil {
   318  		return nil, errors.Extend(err, errors.New("unable to create miner"))
   319  	}
   320  
   321  	// Explorer.
   322  	e, err := func() (modules.Explorer, error) {
   323  		if !params.CreateExplorer && params.Explorer != nil {
   324  			return nil, errors.New("cannot create explorer and also use custom explorer")
   325  		}
   326  		if params.Explorer != nil {
   327  			return params.Explorer, nil
   328  		}
   329  		if !params.CreateExplorer {
   330  			return nil, nil
   331  		}
   332  		// TODO: Implement explorer.
   333  		return nil, errors.New("explorer not implemented")
   334  	}()
   335  	if err != nil {
   336  		return nil, errors.Extend(err, errors.New("unable to create explorer"))
   337  	}
   338  
   339  	return &Node{
   340  		ConsensusSet:    cs,
   341  		Explorer:        e,
   342  		Gateway:         g,
   343  		Host:            h,
   344  		Miner:           m,
   345  		Renter:          r,
   346  		TransactionPool: tp,
   347  		Wallet:          w,
   348  
   349  		Dir: dir,
   350  	}, nil
   351  }