github.com/nebulouslabs/sia@v1.3.7/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/NebulousLabs/Sia/modules" 16 "github.com/NebulousLabs/Sia/modules/consensus" 17 "github.com/NebulousLabs/Sia/modules/gateway" 18 "github.com/NebulousLabs/Sia/modules/host" 19 "github.com/NebulousLabs/Sia/modules/miner" 20 "github.com/NebulousLabs/Sia/modules/renter" 21 "github.com/NebulousLabs/Sia/modules/renter/contractor" 22 "github.com/NebulousLabs/Sia/modules/renter/hostdb" 23 "github.com/NebulousLabs/Sia/modules/renter/proto" 24 "github.com/NebulousLabs/Sia/modules/transactionpool" 25 "github.com/NebulousLabs/Sia/modules/wallet" 26 "github.com/NebulousLabs/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 // Custom settings for modules 86 Allowance modules.Allowance 87 88 // The following fields are used to skip parts of the node set up 89 SkipSetAllowance bool 90 SkipHostDiscovery bool 91 92 // The high level directory where all the persistence gets stored for the 93 // modules. 94 Dir string 95 } 96 97 // Node is a collection of Sia modules operating together as a Sia node. 98 type Node struct { 99 // The modules of the node. Modules that are not initialized will be nil. 100 ConsensusSet modules.ConsensusSet 101 Explorer modules.Explorer 102 Gateway modules.Gateway 103 Host modules.Host 104 Miner modules.TestMiner 105 Renter modules.Renter 106 TransactionPool modules.TransactionPool 107 Wallet modules.Wallet 108 109 // The high level directory where all the persistence gets stored for the 110 // modules. 111 Dir string 112 } 113 114 // Close will call close on every module within the node, combining and 115 // returning the errors. 116 func (n *Node) Close() (err error) { 117 if n.Explorer != nil { 118 err = errors.Compose(n.Explorer.Close()) 119 } 120 if n.Miner != nil { 121 err = errors.Compose(n.Miner.Close()) 122 } 123 if n.Host != nil { 124 err = errors.Compose(n.Host.Close()) 125 } 126 if n.Renter != nil { 127 err = errors.Compose(n.Renter.Close()) 128 } 129 if n.Wallet != nil { 130 err = errors.Compose(n.Wallet.Close()) 131 } 132 if n.TransactionPool != nil { 133 err = errors.Compose(n.TransactionPool.Close()) 134 } 135 if n.ConsensusSet != nil { 136 err = errors.Compose(n.ConsensusSet.Close()) 137 } 138 if n.Gateway != nil { 139 err = errors.Compose(n.Gateway.Close()) 140 } 141 return err 142 } 143 144 // New will create a new test node. The inputs to the function are the 145 // respective 'New' calls for each module. We need to use this awkward method 146 // of initialization because the siatest package cannot import any of the 147 // modules directly (so that the modules may use the siatest package to test 148 // themselves). 149 func New(params NodeParams) (*Node, error) { 150 dir := params.Dir 151 152 // Gateway. 153 g, err := func() (modules.Gateway, error) { 154 if params.CreateGateway && params.Gateway != nil { 155 return nil, errors.New("cannot both create a gateway and use a passed in gateway") 156 } 157 /* Template for dealing with optional dependencies: 158 if !params.CreateGateway && parames.GatewayDependencies != nil { 159 return nil, errors.New("cannot pass in gateway dependencies if you are not creating a gateway") 160 } 161 */ 162 if params.Gateway != nil { 163 return params.Gateway, nil 164 } 165 if !params.CreateGateway { 166 return nil, nil 167 } 168 /* Template for dealing with optional dependencies: 169 if params.GatewayDependencies == nil { 170 gateway.New(... 171 } else { 172 gateway.NewDeps(... 173 } 174 */ 175 return gateway.New("localhost:0", false, filepath.Join(dir, modules.GatewayDir)) 176 }() 177 if err != nil { 178 return nil, errors.Extend(err, errors.New("unable to create gateway")) 179 } 180 181 // Consensus. 182 cs, err := func() (modules.ConsensusSet, error) { 183 if params.CreateConsensusSet && params.ConsensusSet != nil { 184 return nil, errors.New("cannot both create consensus and use passed in consensus") 185 } 186 if params.ConsensusSet != nil { 187 return params.ConsensusSet, nil 188 } 189 if !params.CreateConsensusSet { 190 return nil, nil 191 } 192 return consensus.New(g, false, filepath.Join(dir, modules.ConsensusDir)) 193 }() 194 if err != nil { 195 return nil, errors.Extend(err, errors.New("unable to create consensus set")) 196 } 197 198 // Transaction Pool. 199 tp, err := func() (modules.TransactionPool, error) { 200 if params.CreateTransactionPool && params.TransactionPool != nil { 201 return nil, errors.New("cannot create transaction pool and also use custom transaction pool") 202 } 203 if params.TransactionPool != nil { 204 return params.TransactionPool, nil 205 } 206 if !params.CreateTransactionPool { 207 return nil, nil 208 } 209 return transactionpool.New(cs, g, filepath.Join(dir, modules.TransactionPoolDir)) 210 }() 211 if err != nil { 212 return nil, errors.Extend(err, errors.New("unable to create transaction pool")) 213 } 214 215 // Wallet. 216 w, err := func() (modules.Wallet, error) { 217 if params.CreateWallet && params.Wallet != nil { 218 return nil, errors.New("cannot create wallet and use custom wallet") 219 } 220 if params.Wallet != nil { 221 return params.Wallet, nil 222 } 223 if !params.CreateWallet { 224 return nil, nil 225 } 226 walletDeps := params.WalletDeps 227 if walletDeps == nil { 228 walletDeps = modules.ProdDependencies 229 } 230 return wallet.NewCustomWallet(cs, tp, filepath.Join(dir, modules.WalletDir), walletDeps) 231 }() 232 if err != nil { 233 return nil, errors.Extend(err, errors.New("unable to create wallet")) 234 } 235 236 // Host. 237 h, err := func() (modules.Host, error) { 238 if params.CreateHost && params.Host != nil { 239 return nil, errors.New("cannot create host and use custom host") 240 } 241 if params.Host != nil { 242 return params.Host, nil 243 } 244 if !params.CreateHost { 245 return nil, nil 246 } 247 return host.New(cs, tp, w, "localhost:0", filepath.Join(dir, modules.HostDir)) 248 }() 249 if err != nil { 250 return nil, errors.Extend(err, errors.New("unable to create host")) 251 } 252 253 // Renter. 254 r, err := func() (modules.Renter, error) { 255 if params.CreateRenter && params.Renter != nil { 256 return nil, errors.New("cannot create renter and also use custom renter") 257 } 258 if params.Renter != nil { 259 return params.Renter, nil 260 } 261 if !params.CreateRenter { 262 return nil, nil 263 } 264 contractorDeps := params.ContractorDeps 265 if contractorDeps == nil { 266 contractorDeps = modules.ProdDependencies 267 } 268 contractSetDeps := params.ContractSetDeps 269 if contractSetDeps == nil { 270 contractSetDeps = modules.ProdDependencies 271 } 272 hostDBDeps := params.HostDBDeps 273 if hostDBDeps == nil { 274 hostDBDeps = modules.ProdDependencies 275 } 276 renterDeps := params.RenterDeps 277 if renterDeps == nil { 278 renterDeps = modules.ProdDependencies 279 } 280 persistDir := filepath.Join(dir, modules.RenterDir) 281 282 // HostDB 283 hdb, err := hostdb.NewCustomHostDB(g, cs, persistDir, hostDBDeps) 284 if err != nil { 285 return nil, err 286 } 287 // ContractSet 288 contractSet, err := proto.NewContractSet(filepath.Join(persistDir, "contracts"), contractSetDeps) 289 if err != nil { 290 return nil, err 291 } 292 // Contractor 293 logger, err := persist.NewFileLogger(filepath.Join(persistDir, "contractor.log")) 294 if err != nil { 295 return nil, err 296 } 297 hc, err := contractor.NewCustomContractor(cs, &contractor.WalletBridge{W: w}, tp, hdb, contractSet, contractor.NewPersist(persistDir), logger, contractorDeps) 298 if err != nil { 299 return nil, err 300 } 301 return renter.NewCustomRenter(g, cs, tp, hdb, hc, persistDir, renterDeps) 302 }() 303 if err != nil { 304 return nil, errors.Extend(err, errors.New("unable to create renter")) 305 } 306 307 // Miner. 308 m, err := func() (modules.TestMiner, error) { 309 if params.CreateMiner && params.Miner != nil { 310 return nil, errors.New("cannot create miner and also use custom miner") 311 } 312 if params.Miner != nil { 313 return params.Miner, nil 314 } 315 if !params.CreateMiner { 316 return nil, nil 317 } 318 m, err := miner.New(cs, tp, w, filepath.Join(dir, modules.MinerDir)) 319 if err != nil { 320 return nil, err 321 } 322 return m, nil 323 }() 324 if err != nil { 325 return nil, errors.Extend(err, errors.New("unable to create miner")) 326 } 327 328 // Explorer. 329 e, err := func() (modules.Explorer, error) { 330 if !params.CreateExplorer && params.Explorer != nil { 331 return nil, errors.New("cannot create explorer and also use custom explorer") 332 } 333 if params.Explorer != nil { 334 return params.Explorer, nil 335 } 336 if !params.CreateExplorer { 337 return nil, nil 338 } 339 // TODO: Implement explorer. 340 return nil, errors.New("explorer not implemented") 341 }() 342 if err != nil { 343 return nil, errors.Extend(err, errors.New("unable to create explorer")) 344 } 345 346 return &Node{ 347 ConsensusSet: cs, 348 Explorer: e, 349 Gateway: g, 350 Host: h, 351 Miner: m, 352 Renter: r, 353 TransactionPool: tp, 354 Wallet: w, 355 356 Dir: dir, 357 }, nil 358 }