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 }