node: ensure datadir can be co-inhabited by different instances
This change ensures that nodes started with different Name but same DataDir values don't use the same nodekey and IPC socket.
This commit is contained in:
		@@ -79,7 +79,8 @@ func importChain(ctx *cli.Context) error {
 | 
				
			|||||||
	if ctx.GlobalBool(utils.TestNetFlag.Name) {
 | 
						if ctx.GlobalBool(utils.TestNetFlag.Name) {
 | 
				
			||||||
		state.StartingNonce = 1048576 // (2**20)
 | 
							state.StartingNonce = 1048576 // (2**20)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	chain, chainDb := utils.MakeChain(ctx)
 | 
						stack := makeFullNode(ctx)
 | 
				
			||||||
 | 
						chain, chainDb := utils.MakeChain(ctx, stack)
 | 
				
			||||||
	start := time.Now()
 | 
						start := time.Now()
 | 
				
			||||||
	err := utils.ImportChain(chain, ctx.Args().First())
 | 
						err := utils.ImportChain(chain, ctx.Args().First())
 | 
				
			||||||
	chainDb.Close()
 | 
						chainDb.Close()
 | 
				
			||||||
@@ -94,7 +95,8 @@ func exportChain(ctx *cli.Context) error {
 | 
				
			|||||||
	if len(ctx.Args()) < 1 {
 | 
						if len(ctx.Args()) < 1 {
 | 
				
			||||||
		utils.Fatalf("This command requires an argument.")
 | 
							utils.Fatalf("This command requires an argument.")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	chain, _ := utils.MakeChain(ctx)
 | 
						stack := makeFullNode(ctx)
 | 
				
			||||||
 | 
						chain, _ := utils.MakeChain(ctx, stack)
 | 
				
			||||||
	start := time.Now()
 | 
						start := time.Now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
@@ -122,20 +124,25 @@ func exportChain(ctx *cli.Context) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func removeDB(ctx *cli.Context) error {
 | 
					func removeDB(ctx *cli.Context) error {
 | 
				
			||||||
	confirm, err := console.Stdin.PromptConfirm("Remove local database?")
 | 
						stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
 | 
				
			||||||
	if err != nil {
 | 
						dbdir := stack.ResolvePath("chaindata")
 | 
				
			||||||
		utils.Fatalf("%v", err)
 | 
						if !common.FileExist(dbdir) {
 | 
				
			||||||
 | 
							fmt.Println(dbdir, "does not exist")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if confirm {
 | 
						fmt.Println(dbdir)
 | 
				
			||||||
		fmt.Println("Removing chaindata...")
 | 
						confirm, err := console.Stdin.PromptConfirm("Remove this database?")
 | 
				
			||||||
		start := time.Now()
 | 
						switch {
 | 
				
			||||||
 | 
						case err != nil:
 | 
				
			||||||
		os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata"))
 | 
							utils.Fatalf("%v", err)
 | 
				
			||||||
 | 
						case !confirm:
 | 
				
			||||||
		fmt.Printf("Removed in %v\n", time.Since(start))
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		fmt.Println("Operation aborted")
 | 
							fmt.Println("Operation aborted")
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							fmt.Println("Removing...")
 | 
				
			||||||
 | 
							start := time.Now()
 | 
				
			||||||
 | 
							os.RemoveAll(dbdir)
 | 
				
			||||||
 | 
							fmt.Printf("Removed in %v\n", time.Since(start))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -143,7 +150,8 @@ func removeDB(ctx *cli.Context) error {
 | 
				
			|||||||
func upgradeDB(ctx *cli.Context) error {
 | 
					func upgradeDB(ctx *cli.Context) error {
 | 
				
			||||||
	glog.Infoln("Upgrading blockchain database")
 | 
						glog.Infoln("Upgrading blockchain database")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chain, chainDb := utils.MakeChain(ctx)
 | 
						stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
 | 
				
			||||||
 | 
						chain, chainDb := utils.MakeChain(ctx, stack)
 | 
				
			||||||
	bcVersion := core.GetBlockChainVersion(chainDb)
 | 
						bcVersion := core.GetBlockChainVersion(chainDb)
 | 
				
			||||||
	if bcVersion == 0 {
 | 
						if bcVersion == 0 {
 | 
				
			||||||
		bcVersion = core.BlockChainVersion
 | 
							bcVersion = core.BlockChainVersion
 | 
				
			||||||
@@ -156,10 +164,12 @@ func upgradeDB(ctx *cli.Context) error {
 | 
				
			|||||||
		utils.Fatalf("Unable to export chain for reimport %s", err)
 | 
							utils.Fatalf("Unable to export chain for reimport %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	chainDb.Close()
 | 
						chainDb.Close()
 | 
				
			||||||
	os.RemoveAll(filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), "chaindata"))
 | 
						if dir := dbDirectory(chainDb); dir != "" {
 | 
				
			||||||
 | 
							os.RemoveAll(dir)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Import the chain file.
 | 
						// Import the chain file.
 | 
				
			||||||
	chain, chainDb = utils.MakeChain(ctx)
 | 
						chain, chainDb = utils.MakeChain(ctx, stack)
 | 
				
			||||||
	core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
 | 
						core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
 | 
				
			||||||
	err := utils.ImportChain(chain, exportFile)
 | 
						err := utils.ImportChain(chain, exportFile)
 | 
				
			||||||
	chainDb.Close()
 | 
						chainDb.Close()
 | 
				
			||||||
@@ -172,8 +182,17 @@ func upgradeDB(ctx *cli.Context) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func dbDirectory(db ethdb.Database) string {
 | 
				
			||||||
 | 
						ldb, ok := db.(*ethdb.LDBDatabase)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ldb.Path()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func dump(ctx *cli.Context) error {
 | 
					func dump(ctx *cli.Context) error {
 | 
				
			||||||
	chain, chainDb := utils.MakeChain(ctx)
 | 
						stack := makeFullNode(ctx)
 | 
				
			||||||
 | 
						chain, chainDb := utils.MakeChain(ctx, stack)
 | 
				
			||||||
	for _, arg := range ctx.Args() {
 | 
						for _, arg := range ctx.Args() {
 | 
				
			||||||
		var block *types.Block
 | 
							var block *types.Block
 | 
				
			||||||
		if hashish(arg) {
 | 
							if hashish(arg) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -107,7 +107,7 @@ func remoteConsole(ctx *cli.Context) error {
 | 
				
			|||||||
		utils.Fatalf("Unable to attach to remote geth: %v", err)
 | 
							utils.Fatalf("Unable to attach to remote geth: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	config := console.Config{
 | 
						config := console.Config{
 | 
				
			||||||
		DataDir: utils.MustMakeDataDir(ctx),
 | 
							DataDir: utils.MakeDataDir(ctx),
 | 
				
			||||||
		DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
 | 
							DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
 | 
				
			||||||
		Client:  client,
 | 
							Client:  client,
 | 
				
			||||||
		Preload: utils.MakeConsolePreloads(ctx),
 | 
							Preload: utils.MakeConsolePreloads(ctx),
 | 
				
			||||||
@@ -135,7 +135,7 @@ func remoteConsole(ctx *cli.Context) error {
 | 
				
			|||||||
// for "geth attach" and "geth monitor" with no argument.
 | 
					// for "geth attach" and "geth monitor" with no argument.
 | 
				
			||||||
func dialRPC(endpoint string) (*rpc.Client, error) {
 | 
					func dialRPC(endpoint string) (*rpc.Client, error) {
 | 
				
			||||||
	if endpoint == "" {
 | 
						if endpoint == "" {
 | 
				
			||||||
		endpoint = node.DefaultIPCEndpoint()
 | 
							endpoint = node.DefaultIPCEndpoint(clientIdentifier)
 | 
				
			||||||
	} else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") {
 | 
						} else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") {
 | 
				
			||||||
		// Backwards compatibility with geth < 1.5 which required
 | 
							// Backwards compatibility with geth < 1.5 which required
 | 
				
			||||||
		// these prefixes.
 | 
							// these prefixes.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,9 +195,9 @@ func testDAOForkBlockNewChain(t *testing.T, testnet bool, genesis string, votes
 | 
				
			|||||||
		geth.cmd.Wait()
 | 
							geth.cmd.Wait()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Retrieve the DAO config flag from the database
 | 
						// Retrieve the DAO config flag from the database
 | 
				
			||||||
	path := filepath.Join(datadir, "chaindata")
 | 
						path := filepath.Join(datadir, "geth", "chaindata")
 | 
				
			||||||
	if testnet && genesis == "" {
 | 
						if testnet && genesis == "" {
 | 
				
			||||||
		path = filepath.Join(datadir, "testnet", "chaindata")
 | 
							path = filepath.Join(datadir, "testnet", "geth", "chaindata")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	db, err := ethdb.NewLDBDatabase(path, 0, 0)
 | 
						db, err := ethdb.NewLDBDatabase(path, 0, 0)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,7 +36,6 @@ import (
 | 
				
			|||||||
	"github.com/ethereum/go-ethereum/core"
 | 
						"github.com/ethereum/go-ethereum/core"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/core/state"
 | 
						"github.com/ethereum/go-ethereum/core/state"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/eth"
 | 
						"github.com/ethereum/go-ethereum/eth"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/ethdb"
 | 
					 | 
				
			||||||
	"github.com/ethereum/go-ethereum/internal/debug"
 | 
						"github.com/ethereum/go-ethereum/internal/debug"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/logger"
 | 
						"github.com/ethereum/go-ethereum/logger"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/logger/glog"
 | 
						"github.com/ethereum/go-ethereum/logger/glog"
 | 
				
			||||||
@@ -46,7 +45,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	clientIdentifier = "Geth" // Client identifier to advertise over the network
 | 
						clientIdentifier = "geth" // Client identifier to advertise over the network
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -245,17 +244,15 @@ func initGenesis(ctx *cli.Context) error {
 | 
				
			|||||||
		state.StartingNonce = 1048576 // (2**20)
 | 
							state.StartingNonce = 1048576 // (2**20)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chainDb, err := ethdb.NewLDBDatabase(filepath.Join(utils.MustMakeDataDir(ctx), "chaindata"), 0, 0)
 | 
						stack := makeFullNode(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						chaindb := utils.MakeChainDatabase(ctx, stack)
 | 
				
			||||||
		utils.Fatalf("could not open database: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	genesisFile, err := os.Open(genesisPath)
 | 
						genesisFile, err := os.Open(genesisPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		utils.Fatalf("failed to read genesis file: %v", err)
 | 
							utils.Fatalf("failed to read genesis file: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	block, err := core.WriteGenesisBlock(chainDb, genesisFile)
 | 
						block, err := core.WriteGenesisBlock(chaindb, genesisFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		utils.Fatalf("failed to write genesis block: %v", err)
 | 
							utils.Fatalf("failed to write genesis block: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -296,9 +293,6 @@ func makeFullNode(ctx *cli.Context) *node.Node {
 | 
				
			|||||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
 | 
					// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
 | 
				
			||||||
// miner.
 | 
					// miner.
 | 
				
			||||||
func startNode(ctx *cli.Context, stack *node.Node) {
 | 
					func startNode(ctx *cli.Context, stack *node.Node) {
 | 
				
			||||||
	// Report geth version
 | 
					 | 
				
			||||||
	glog.V(logger.Info).Infof("instance: Geth/%s/%s/%s\n", utils.Version, runtime.Version(), runtime.GOOS)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Start up the node itself
 | 
						// Start up the node itself
 | 
				
			||||||
	utils.StartNode(stack)
 | 
						utils.StartNode(stack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -379,7 +373,7 @@ func gpubench(ctx *cli.Context) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func version(c *cli.Context) error {
 | 
					func version(c *cli.Context) error {
 | 
				
			||||||
	fmt.Println(clientIdentifier)
 | 
						fmt.Println(strings.Title(clientIdentifier))
 | 
				
			||||||
	fmt.Println("Version:", utils.Version)
 | 
						fmt.Println("Version:", utils.Version)
 | 
				
			||||||
	if gitCommit != "" {
 | 
						if gitCommit != "" {
 | 
				
			||||||
		fmt.Println("Git Commit:", gitCommit)
 | 
							fmt.Println("Git Commit:", gitCommit)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ import (
 | 
				
			|||||||
var (
 | 
					var (
 | 
				
			||||||
	monitorCommandAttachFlag = cli.StringFlag{
 | 
						monitorCommandAttachFlag = cli.StringFlag{
 | 
				
			||||||
		Name:  "attach",
 | 
							Name:  "attach",
 | 
				
			||||||
		Value: node.DefaultIPCEndpoint(),
 | 
							Value: node.DefaultIPCEndpoint(clientIdentifier),
 | 
				
			||||||
		Usage: "API endpoint to attach to",
 | 
							Usage: "API endpoint to attach to",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	monitorCommandRowsFlag = cli.IntFlag{
 | 
						monitorCommandRowsFlag = cli.IntFlag{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,7 +88,7 @@ func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) {
 | 
				
			|||||||
	// Create a networkless protocol stack
 | 
						// Create a networkless protocol stack
 | 
				
			||||||
	stack, err := node.New(&node.Config{
 | 
						stack, err := node.New(&node.Config{
 | 
				
			||||||
		UseLightweightKDF: true,
 | 
							UseLightweightKDF: true,
 | 
				
			||||||
		IPCPath:           node.DefaultIPCEndpoint(),
 | 
							IPCPath:           node.DefaultIPCEndpoint(""),
 | 
				
			||||||
		HTTPHost:          common.DefaultHTTPHost,
 | 
							HTTPHost:          common.DefaultHTTPHost,
 | 
				
			||||||
		HTTPPort:          common.DefaultHTTPPort,
 | 
							HTTPPort:          common.DefaultHTTPPort,
 | 
				
			||||||
		HTTPModules:       []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"},
 | 
							HTTPModules:       []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -396,13 +396,14 @@ var (
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MustMakeDataDir retrieves the currently requested data directory, terminating
 | 
					// MakeDataDir retrieves the currently requested data directory, terminating
 | 
				
			||||||
// if none (or the empty string) is specified. If the node is starting a testnet,
 | 
					// if none (or the empty string) is specified. If the node is starting a testnet,
 | 
				
			||||||
// the a subdirectory of the specified datadir will be used.
 | 
					// the a subdirectory of the specified datadir will be used.
 | 
				
			||||||
func MustMakeDataDir(ctx *cli.Context) string {
 | 
					func MakeDataDir(ctx *cli.Context) string {
 | 
				
			||||||
	if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
 | 
						if path := ctx.GlobalString(DataDirFlag.Name); path != "" {
 | 
				
			||||||
 | 
							// TODO: choose a different location outside of the regular datadir.
 | 
				
			||||||
		if ctx.GlobalBool(TestNetFlag.Name) {
 | 
							if ctx.GlobalBool(TestNetFlag.Name) {
 | 
				
			||||||
			return filepath.Join(path, "/testnet")
 | 
								return filepath.Join(path, "testnet")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return path
 | 
							return path
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -447,16 +448,16 @@ func MakeNodeKey(ctx *cli.Context) *ecdsa.PrivateKey {
 | 
				
			|||||||
	return key
 | 
						return key
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MakeNodeName creates a node name from a base set and the command line flags.
 | 
					// makeNodeUserIdent creates the user identifier from CLI flags.
 | 
				
			||||||
func MakeNodeName(client, version string, ctx *cli.Context) string {
 | 
					func makeNodeUserIdent(ctx *cli.Context) string {
 | 
				
			||||||
	name := common.MakeName(client, version)
 | 
						var comps []string
 | 
				
			||||||
	if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 {
 | 
						if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 {
 | 
				
			||||||
		name += "/" + identity
 | 
							comps = append(comps, identity)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if ctx.GlobalBool(VMEnableJitFlag.Name) {
 | 
						if ctx.GlobalBool(VMEnableJitFlag.Name) {
 | 
				
			||||||
		name += "/JIT"
 | 
							comps = append(comps, "JIT")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return name
 | 
						return strings.Join(comps, "/")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MakeBootstrapNodes creates a list of bootstrap nodes from the command line
 | 
					// MakeBootstrapNodes creates a list of bootstrap nodes from the command line
 | 
				
			||||||
@@ -612,11 +613,13 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config := &node.Config{
 | 
						config := &node.Config{
 | 
				
			||||||
		DataDir:           MustMakeDataDir(ctx),
 | 
							DataDir:           MakeDataDir(ctx),
 | 
				
			||||||
		KeyStoreDir:       ctx.GlobalString(KeyStoreDirFlag.Name),
 | 
							KeyStoreDir:       ctx.GlobalString(KeyStoreDirFlag.Name),
 | 
				
			||||||
		UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name),
 | 
							UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name),
 | 
				
			||||||
		PrivateKey:        MakeNodeKey(ctx),
 | 
							PrivateKey:        MakeNodeKey(ctx),
 | 
				
			||||||
		Name:              MakeNodeName(name, vsn, ctx),
 | 
							Name:              name,
 | 
				
			||||||
 | 
							Version:           vsn,
 | 
				
			||||||
 | 
							UserIdent:         makeNodeUserIdent(ctx),
 | 
				
			||||||
		NoDiscovery:       ctx.GlobalBool(NoDiscoverFlag.Name),
 | 
							NoDiscovery:       ctx.GlobalBool(NoDiscoverFlag.Name),
 | 
				
			||||||
		BootstrapNodes:    MakeBootstrapNodes(ctx),
 | 
							BootstrapNodes:    MakeBootstrapNodes(ctx),
 | 
				
			||||||
		ListenAddr:        MakeListenAddress(ctx),
 | 
							ListenAddr:        MakeListenAddress(ctx),
 | 
				
			||||||
@@ -674,7 +677,7 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	ethConf := ð.Config{
 | 
						ethConf := ð.Config{
 | 
				
			||||||
		Etherbase:               MakeEtherbase(stack.AccountManager(), ctx),
 | 
							Etherbase:               MakeEtherbase(stack.AccountManager(), ctx),
 | 
				
			||||||
		ChainConfig:             MustMakeChainConfig(ctx),
 | 
							ChainConfig:             MakeChainConfig(ctx, stack),
 | 
				
			||||||
		FastSync:                ctx.GlobalBool(FastSyncFlag.Name),
 | 
							FastSync:                ctx.GlobalBool(FastSyncFlag.Name),
 | 
				
			||||||
		DatabaseCache:           ctx.GlobalInt(CacheFlag.Name),
 | 
							DatabaseCache:           ctx.GlobalInt(CacheFlag.Name),
 | 
				
			||||||
		DatabaseHandles:         MakeDatabaseHandles(),
 | 
							DatabaseHandles:         MakeDatabaseHandles(),
 | 
				
			||||||
@@ -748,16 +751,16 @@ func SetupNetwork(ctx *cli.Context) {
 | 
				
			|||||||
	params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name))
 | 
						params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MustMakeChainConfig reads the chain configuration from the database in ctx.Datadir.
 | 
					// MakeChainConfig reads the chain configuration from the database in ctx.Datadir.
 | 
				
			||||||
func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
 | 
					func MakeChainConfig(ctx *cli.Context, stack *node.Node) *core.ChainConfig {
 | 
				
			||||||
	db := MakeChainDatabase(ctx)
 | 
						db := MakeChainDatabase(ctx, stack)
 | 
				
			||||||
	defer db.Close()
 | 
						defer db.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return MustMakeChainConfigFromDb(ctx, db)
 | 
						return MakeChainConfigFromDb(ctx, db)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MustMakeChainConfigFromDb reads the chain configuration from the given database.
 | 
					// MakeChainConfigFromDb reads the chain configuration from the given database.
 | 
				
			||||||
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
 | 
					func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
 | 
				
			||||||
	// If the chain is already initialized, use any existing chain configs
 | 
						// If the chain is already initialized, use any existing chain configs
 | 
				
			||||||
	config := new(core.ChainConfig)
 | 
						config := new(core.ChainConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -800,14 +803,13 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
 | 
					// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
 | 
				
			||||||
func MakeChainDatabase(ctx *cli.Context) ethdb.Database {
 | 
					func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		datadir = MustMakeDataDir(ctx)
 | 
					 | 
				
			||||||
		cache   = ctx.GlobalInt(CacheFlag.Name)
 | 
							cache   = ctx.GlobalInt(CacheFlag.Name)
 | 
				
			||||||
		handles = MakeDatabaseHandles()
 | 
							handles = MakeDatabaseHandles()
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chainDb, err := ethdb.NewLDBDatabase(filepath.Join(datadir, "chaindata"), cache, handles)
 | 
						chainDb, err := stack.OpenDatabase("chaindata", cache, handles)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		Fatalf("Could not open database: %v", err)
 | 
							Fatalf("Could not open database: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -815,9 +817,9 @@ func MakeChainDatabase(ctx *cli.Context) ethdb.Database {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MakeChain creates a chain manager from set command line flags.
 | 
					// MakeChain creates a chain manager from set command line flags.
 | 
				
			||||||
func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database) {
 | 
					func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) {
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	chainDb = MakeChainDatabase(ctx)
 | 
						chainDb = MakeChainDatabase(ctx, stack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.GlobalBool(OlympicFlag.Name) {
 | 
						if ctx.GlobalBool(OlympicFlag.Name) {
 | 
				
			||||||
		_, err := core.WriteTestNetGenesisBlock(chainDb)
 | 
							_, err := core.WriteTestNetGenesisBlock(chainDb)
 | 
				
			||||||
@@ -825,7 +827,7 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
 | 
				
			|||||||
			glog.Fatalln(err)
 | 
								glog.Fatalln(err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	chainConfig := MustMakeChainConfigFromDb(ctx, chainDb)
 | 
						chainConfig := MakeChainConfigFromDb(ctx, chainDb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pow := pow.PoW(core.FakePow{})
 | 
						pow := pow.PoW(core.FakePow{})
 | 
				
			||||||
	if !ctx.GlobalBool(FakePoWFlag.Name) {
 | 
						if !ctx.GlobalBool(FakePoWFlag.Name) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								node/api.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								node/api.go
									
									
									
									
									
								
							@@ -85,16 +85,16 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *rpc.HexNumber, cors *st
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if host == nil {
 | 
						if host == nil {
 | 
				
			||||||
		h := common.DefaultHTTPHost
 | 
							h := common.DefaultHTTPHost
 | 
				
			||||||
		if api.node.httpHost != "" {
 | 
							if api.node.config.HTTPHost != "" {
 | 
				
			||||||
			h = api.node.httpHost
 | 
								h = api.node.config.HTTPHost
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		host = &h
 | 
							host = &h
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if port == nil {
 | 
						if port == nil {
 | 
				
			||||||
		port = rpc.NewHexNumber(api.node.httpPort)
 | 
							port = rpc.NewHexNumber(api.node.config.HTTPPort)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if cors == nil {
 | 
						if cors == nil {
 | 
				
			||||||
		cors = &api.node.httpCors
 | 
							cors = &api.node.config.HTTPCors
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modules := api.node.httpWhitelist
 | 
						modules := api.node.httpWhitelist
 | 
				
			||||||
@@ -134,19 +134,19 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *rpc.HexNumber, allowedOr
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if host == nil {
 | 
						if host == nil {
 | 
				
			||||||
		h := common.DefaultWSHost
 | 
							h := common.DefaultWSHost
 | 
				
			||||||
		if api.node.wsHost != "" {
 | 
							if api.node.config.WSHost != "" {
 | 
				
			||||||
			h = api.node.wsHost
 | 
								h = api.node.config.WSHost
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		host = &h
 | 
							host = &h
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if port == nil {
 | 
						if port == nil {
 | 
				
			||||||
		port = rpc.NewHexNumber(api.node.wsPort)
 | 
							port = rpc.NewHexNumber(api.node.config.WSPort)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if allowedOrigins == nil {
 | 
						if allowedOrigins == nil {
 | 
				
			||||||
		allowedOrigins = &api.node.wsOrigins
 | 
							allowedOrigins = &api.node.config.WSOrigins
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modules := api.node.wsWhitelist
 | 
						modules := api.node.config.WSModules
 | 
				
			||||||
	if apis != nil {
 | 
						if apis != nil {
 | 
				
			||||||
		modules = nil
 | 
							modules = nil
 | 
				
			||||||
		for _, m := range strings.Split(*apis, ",") {
 | 
							for _, m := range strings.Split(*apis, ",") {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										129
									
								
								node/config.go
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								node/config.go
									
									
									
									
									
								
							@@ -18,7 +18,6 @@ package node
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/ecdsa"
 | 
						"crypto/ecdsa"
 | 
				
			||||||
	"encoding/json"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
@@ -48,6 +47,18 @@ var (
 | 
				
			|||||||
// P2P network layer of a protocol stack. These values can be further extended by
 | 
					// P2P network layer of a protocol stack. These values can be further extended by
 | 
				
			||||||
// all registered services.
 | 
					// all registered services.
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
 | 
						// Name sets the instance name of the node. It must not contain the / character and is
 | 
				
			||||||
 | 
						// used in the devp2p node identifier. The instance name of geth is "geth". If no
 | 
				
			||||||
 | 
						// value is specified, the basename of the current executable is used.
 | 
				
			||||||
 | 
						Name string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// UserIdent, if set, is used as an additional component in the devp2p node identifier.
 | 
				
			||||||
 | 
						UserIdent string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Version should be set to the version number of the program. It is used
 | 
				
			||||||
 | 
						// in the devp2p node identifier.
 | 
				
			||||||
 | 
						Version string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// DataDir is the file system folder the node should use for any data storage
 | 
						// DataDir is the file system folder the node should use for any data storage
 | 
				
			||||||
	// requirements. The configured data directory will not be directly shared with
 | 
						// requirements. The configured data directory will not be directly shared with
 | 
				
			||||||
	// registered services, instead those can use utility methods to create/access
 | 
						// registered services, instead those can use utility methods to create/access
 | 
				
			||||||
@@ -80,10 +91,6 @@ type Config struct {
 | 
				
			|||||||
	// needed.
 | 
						// needed.
 | 
				
			||||||
	PrivateKey *ecdsa.PrivateKey
 | 
						PrivateKey *ecdsa.PrivateKey
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Name sets the node name of this server. Use common.MakeName to create a name
 | 
					 | 
				
			||||||
	// that follows existing conventions.
 | 
					 | 
				
			||||||
	Name string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// NoDiscovery specifies whether the peer discovery mechanism should be started
 | 
						// NoDiscovery specifies whether the peer discovery mechanism should be started
 | 
				
			||||||
	// or not. Disabling is usually useful for protocol debugging (manual topology).
 | 
						// or not. Disabling is usually useful for protocol debugging (manual topology).
 | 
				
			||||||
	NoDiscovery bool
 | 
						NoDiscovery bool
 | 
				
			||||||
@@ -178,9 +185,23 @@ func (c *Config) IPCEndpoint() string {
 | 
				
			|||||||
	return c.IPCPath
 | 
						return c.IPCPath
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NodeDB returns the path to the discovery node database.
 | 
				
			||||||
 | 
					func (c *Config) NodeDB() string {
 | 
				
			||||||
 | 
						if c.DataDir == "" {
 | 
				
			||||||
 | 
							return "" // ephemeral
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return c.resolvePath("nodes")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DefaultIPCEndpoint returns the IPC path used by default.
 | 
					// DefaultIPCEndpoint returns the IPC path used by default.
 | 
				
			||||||
func DefaultIPCEndpoint() string {
 | 
					func DefaultIPCEndpoint(clientIdentifier string) string {
 | 
				
			||||||
	config := &Config{DataDir: common.DefaultDataDir(), IPCPath: common.DefaultIPCSocket}
 | 
						if clientIdentifier == "" {
 | 
				
			||||||
 | 
							clientIdentifier = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe")
 | 
				
			||||||
 | 
							if clientIdentifier == "" {
 | 
				
			||||||
 | 
								panic("empty executable name")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						config := &Config{DataDir: common.DefaultDataDir(), IPCPath: clientIdentifier + ".ipc"}
 | 
				
			||||||
	return config.IPCEndpoint()
 | 
						return config.IPCEndpoint()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -214,15 +235,76 @@ func DefaultWSEndpoint() string {
 | 
				
			|||||||
	return config.WSEndpoint()
 | 
						return config.WSEndpoint()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NodeName returns the devp2p node identifier.
 | 
				
			||||||
 | 
					func (c *Config) NodeName() string {
 | 
				
			||||||
 | 
						name := c.name()
 | 
				
			||||||
 | 
						// Backwards compatibility: previous versions used title-cased "Geth", keep that.
 | 
				
			||||||
 | 
						if name == "geth" || name == "geth-testnet" {
 | 
				
			||||||
 | 
							name = "Geth"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.UserIdent != "" {
 | 
				
			||||||
 | 
							name += "/" + c.UserIdent
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Version != "" {
 | 
				
			||||||
 | 
							name += "/v" + c.Version
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						name += "/" + runtime.GOOS
 | 
				
			||||||
 | 
						name += "/" + runtime.Version()
 | 
				
			||||||
 | 
						return name
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *Config) name() string {
 | 
				
			||||||
 | 
						if c.Name == "" {
 | 
				
			||||||
 | 
							progname := strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe")
 | 
				
			||||||
 | 
							if progname == "" {
 | 
				
			||||||
 | 
								panic("empty executable name, set Config.Name")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return progname
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return c.Name
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// These resources are resolved differently for the "geth" and "geth-testnet" instances.
 | 
				
			||||||
 | 
					var isOldGethResource = map[string]bool{
 | 
				
			||||||
 | 
						"chaindata":          true,
 | 
				
			||||||
 | 
						"nodes":              true,
 | 
				
			||||||
 | 
						"nodekey":            true,
 | 
				
			||||||
 | 
						"static-nodes.json":  true,
 | 
				
			||||||
 | 
						"trusted-nodes.json": true,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// resolvePath resolves path in the instance directory.
 | 
				
			||||||
 | 
					func (c *Config) resolvePath(path string) string {
 | 
				
			||||||
 | 
						if filepath.IsAbs(path) {
 | 
				
			||||||
 | 
							return path
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.DataDir == "" {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Backwards-compatibility: ensure that data directory files created
 | 
				
			||||||
 | 
						// by geth 1.4 are used if they exist.
 | 
				
			||||||
 | 
						if c.name() == "geth" && isOldGethResource[path] {
 | 
				
			||||||
 | 
							oldpath := ""
 | 
				
			||||||
 | 
							if c.Name == "geth" {
 | 
				
			||||||
 | 
								oldpath = filepath.Join(c.DataDir, path)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if oldpath != "" && common.FileExist(oldpath) {
 | 
				
			||||||
 | 
								// TODO: print warning
 | 
				
			||||||
 | 
								return oldpath
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return filepath.Join(c.DataDir, c.name(), path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NodeKey retrieves the currently configured private key of the node, checking
 | 
					// NodeKey retrieves the currently configured private key of the node, checking
 | 
				
			||||||
// first any manually set key, falling back to the one found in the configured
 | 
					// first any manually set key, falling back to the one found in the configured
 | 
				
			||||||
// data folder. If no key can be found, a new one is generated.
 | 
					// data folder. If no key can be found, a new one is generated.
 | 
				
			||||||
func (c *Config) NodeKey() *ecdsa.PrivateKey {
 | 
					func (c *Config) NodeKey() *ecdsa.PrivateKey {
 | 
				
			||||||
	// Use any specifically configured key
 | 
						// Use any specifically configured key.
 | 
				
			||||||
	if c.PrivateKey != nil {
 | 
						if c.PrivateKey != nil {
 | 
				
			||||||
		return c.PrivateKey
 | 
							return c.PrivateKey
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Generate ephemeral key if no datadir is being used
 | 
						// Generate ephemeral key if no datadir is being used.
 | 
				
			||||||
	if c.DataDir == "" {
 | 
						if c.DataDir == "" {
 | 
				
			||||||
		key, err := crypto.GenerateKey()
 | 
							key, err := crypto.GenerateKey()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -230,16 +312,22 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return key
 | 
							return key
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Fall back to persistent key from the data directory
 | 
					
 | 
				
			||||||
	keyfile := filepath.Join(c.DataDir, datadirPrivateKey)
 | 
						keyfile := c.resolvePath(datadirPrivateKey)
 | 
				
			||||||
	if key, err := crypto.LoadECDSA(keyfile); err == nil {
 | 
						if key, err := crypto.LoadECDSA(keyfile); err == nil {
 | 
				
			||||||
		return key
 | 
							return key
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// No persistent key found, generate and store a new one
 | 
						// No persistent key found, generate and store a new one.
 | 
				
			||||||
	key, err := crypto.GenerateKey()
 | 
						key, err := crypto.GenerateKey()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		glog.Fatalf("Failed to generate node key: %v", err)
 | 
							glog.Fatalf("Failed to generate node key: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						instanceDir := filepath.Join(c.DataDir, c.name())
 | 
				
			||||||
 | 
						if err := os.MkdirAll(instanceDir, 0700); err != nil {
 | 
				
			||||||
 | 
							glog.V(logger.Error).Infof("Failed to persist node key: %v", err)
 | 
				
			||||||
 | 
							return key
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						keyfile = filepath.Join(instanceDir, datadirPrivateKey)
 | 
				
			||||||
	if err := crypto.SaveECDSA(keyfile, key); err != nil {
 | 
						if err := crypto.SaveECDSA(keyfile, key); err != nil {
 | 
				
			||||||
		glog.V(logger.Error).Infof("Failed to persist node key: %v", err)
 | 
							glog.V(logger.Error).Infof("Failed to persist node key: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -248,12 +336,12 @@ func (c *Config) NodeKey() *ecdsa.PrivateKey {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// StaticNodes returns a list of node enode URLs configured as static nodes.
 | 
					// StaticNodes returns a list of node enode URLs configured as static nodes.
 | 
				
			||||||
func (c *Config) StaticNodes() []*discover.Node {
 | 
					func (c *Config) StaticNodes() []*discover.Node {
 | 
				
			||||||
	return c.parsePersistentNodes(datadirStaticNodes)
 | 
						return c.parsePersistentNodes(c.resolvePath(datadirStaticNodes))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TrusterNodes returns a list of node enode URLs configured as trusted nodes.
 | 
					// TrusterNodes returns a list of node enode URLs configured as trusted nodes.
 | 
				
			||||||
func (c *Config) TrusterNodes() []*discover.Node {
 | 
					func (c *Config) TrusterNodes() []*discover.Node {
 | 
				
			||||||
	return c.parsePersistentNodes(datadirTrustedNodes)
 | 
						return c.parsePersistentNodes(c.resolvePath(datadirTrustedNodes))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
 | 
					// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
 | 
				
			||||||
@@ -267,15 +355,10 @@ func (c *Config) parsePersistentNodes(file string) []*discover.Node {
 | 
				
			|||||||
	if _, err := os.Stat(path); err != nil {
 | 
						if _, err := os.Stat(path); err != nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Load the nodes from the config file
 | 
						// Load the nodes from the config file.
 | 
				
			||||||
	blob, err := ioutil.ReadFile(path)
 | 
						var nodelist []string
 | 
				
			||||||
	if err != nil {
 | 
						if err := common.LoadJSON(path, &nodelist); err != nil {
 | 
				
			||||||
		glog.V(logger.Error).Infof("Failed to access nodes: %v", err)
 | 
							glog.V(logger.Error).Infof("Can't load node file %s: %v", path, err)
 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	nodelist := []string{}
 | 
					 | 
				
			||||||
	if err := json.Unmarshal(blob, &nodelist); err != nil {
 | 
					 | 
				
			||||||
		glog.V(logger.Error).Infof("Failed to load nodes: %v", err)
 | 
					 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Interpret the list as a discovery node array
 | 
						// Interpret the list as a discovery node array
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -96,57 +96,55 @@ func TestIPCPathResolution(t *testing.T) {
 | 
				
			|||||||
// ephemeral.
 | 
					// ephemeral.
 | 
				
			||||||
func TestNodeKeyPersistency(t *testing.T) {
 | 
					func TestNodeKeyPersistency(t *testing.T) {
 | 
				
			||||||
	// Create a temporary folder and make sure no key is present
 | 
						// Create a temporary folder and make sure no key is present
 | 
				
			||||||
	dir, err := ioutil.TempDir("", "")
 | 
						dir, err := ioutil.TempDir("", "node-test")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to create temporary data directory: %v", err)
 | 
							t.Fatalf("failed to create temporary data directory: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer os.RemoveAll(dir)
 | 
						defer os.RemoveAll(dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := os.Stat(filepath.Join(dir, datadirPrivateKey)); err == nil {
 | 
						keyfile := filepath.Join(dir, "unit-test", datadirPrivateKey)
 | 
				
			||||||
		t.Fatalf("non-created node key already exists")
 | 
					
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Configure a node with a preset key and ensure it's not persisted
 | 
						// Configure a node with a preset key and ensure it's not persisted
 | 
				
			||||||
	key, err := crypto.GenerateKey()
 | 
						key, err := crypto.GenerateKey()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to generate one-shot node key: %v", err)
 | 
							t.Fatalf("failed to generate one-shot node key: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, err := New(&Config{DataDir: dir, PrivateKey: key}); err != nil {
 | 
						config := &Config{Name: "unit-test", DataDir: dir, PrivateKey: key}
 | 
				
			||||||
		t.Fatalf("failed to create empty stack: %v", err)
 | 
						config.NodeKey()
 | 
				
			||||||
	}
 | 
						if _, err := os.Stat(filepath.Join(keyfile)); err == nil {
 | 
				
			||||||
	if _, err := os.Stat(filepath.Join(dir, datadirPrivateKey)); err == nil {
 | 
					 | 
				
			||||||
		t.Fatalf("one-shot node key persisted to data directory")
 | 
							t.Fatalf("one-shot node key persisted to data directory")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Configure a node with no preset key and ensure it is persisted this time
 | 
						// Configure a node with no preset key and ensure it is persisted this time
 | 
				
			||||||
	if _, err := New(&Config{DataDir: dir}); err != nil {
 | 
						config = &Config{Name: "unit-test", DataDir: dir}
 | 
				
			||||||
		t.Fatalf("failed to create newly keyed stack: %v", err)
 | 
						config.NodeKey()
 | 
				
			||||||
	}
 | 
						if _, err := os.Stat(keyfile); err != nil {
 | 
				
			||||||
	if _, err := os.Stat(filepath.Join(dir, datadirPrivateKey)); err != nil {
 | 
					 | 
				
			||||||
		t.Fatalf("node key not persisted to data directory: %v", err)
 | 
							t.Fatalf("node key not persisted to data directory: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	key, err = crypto.LoadECDSA(filepath.Join(dir, datadirPrivateKey))
 | 
						key, err = crypto.LoadECDSA(keyfile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to load freshly persisted node key: %v", err)
 | 
							t.Fatalf("failed to load freshly persisted node key: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	blob1, err := ioutil.ReadFile(filepath.Join(dir, datadirPrivateKey))
 | 
						blob1, err := ioutil.ReadFile(keyfile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to read freshly persisted node key: %v", err)
 | 
							t.Fatalf("failed to read freshly persisted node key: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Configure a new node and ensure the previously persisted key is loaded
 | 
						// Configure a new node and ensure the previously persisted key is loaded
 | 
				
			||||||
	if _, err := New(&Config{DataDir: dir}); err != nil {
 | 
						config = &Config{Name: "unit-test", DataDir: dir}
 | 
				
			||||||
		t.Fatalf("failed to create previously keyed stack: %v", err)
 | 
						config.NodeKey()
 | 
				
			||||||
	}
 | 
						blob2, err := ioutil.ReadFile(filepath.Join(keyfile))
 | 
				
			||||||
	blob2, err := ioutil.ReadFile(filepath.Join(dir, datadirPrivateKey))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to read previously persisted node key: %v", err)
 | 
							t.Fatalf("failed to read previously persisted node key: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if bytes.Compare(blob1, blob2) != 0 {
 | 
						if bytes.Compare(blob1, blob2) != 0 {
 | 
				
			||||||
		t.Fatalf("persisted node key mismatch: have %x, want %x", blob2, blob1)
 | 
							t.Fatalf("persisted node key mismatch: have %x, want %x", blob2, blob1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Configure ephemeral node and ensure no key is dumped locally
 | 
						// Configure ephemeral node and ensure no key is dumped locally
 | 
				
			||||||
	if _, err := New(&Config{DataDir: ""}); err != nil {
 | 
						config = &Config{Name: "unit-test", DataDir: ""}
 | 
				
			||||||
		t.Fatalf("failed to create ephemeral stack: %v", err)
 | 
						config.NodeKey()
 | 
				
			||||||
	}
 | 
						if _, err := os.Stat(filepath.Join(".", "unit-test", datadirPrivateKey)); err == nil {
 | 
				
			||||||
	if _, err := os.Stat(filepath.Join(".", datadirPrivateKey)); err == nil {
 | 
					 | 
				
			||||||
		t.Fatalf("ephemeral node key persisted to disk")
 | 
							t.Fatalf("ephemeral node key persisted to disk")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										90
									
								
								node/doc.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								node/doc.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					// Copyright 2016 The go-ethereum Authors
 | 
				
			||||||
 | 
					// This file is part of the go-ethereum library.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The go-ethereum library is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU Lesser General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The go-ethereum library is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | 
				
			||||||
 | 
					// GNU Lesser General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU Lesser General Public License
 | 
				
			||||||
 | 
					// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Package node sets up multi-protocol Ethereum nodes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In the model exposed by this package, a node is a collection of services which use shared
 | 
				
			||||||
 | 
					resources to provide RPC APIs. Services can also offer devp2p protocols, which are wired
 | 
				
			||||||
 | 
					up to the devp2p network when the node instance is started.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Resources Managed By Node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All file-system resources used by a node instance are located in a directory called the
 | 
				
			||||||
 | 
					data directory. The location of each resource can be overridden through additional node
 | 
				
			||||||
 | 
					configuration. The data directory is optional. If it is not set and the location of a
 | 
				
			||||||
 | 
					resource is otherwise unspecified, package node will create the resource in memory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To access to the devp2p network, Node configures and starts p2p.Server. Each host on the
 | 
				
			||||||
 | 
					devp2p network has a unique identifier, the node key. The Node instance persists this key
 | 
				
			||||||
 | 
					across restarts. Node also loads static and trusted node lists and ensures that knowledge
 | 
				
			||||||
 | 
					about other hosts is persisted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					JSON-RPC servers which run HTTP, WebSocket or IPC can be started on a Node. RPC modules
 | 
				
			||||||
 | 
					offered by registered services will be offered on those endpoints. Users can restrict any
 | 
				
			||||||
 | 
					endpoint to a subset of RPC modules. Node itself offers the "debug", "admin" and "web3"
 | 
				
			||||||
 | 
					modules.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Service implementations can open LevelDB databases through the service context. Package
 | 
				
			||||||
 | 
					node chooses the file system location of each database. If the node is configured to run
 | 
				
			||||||
 | 
					without a data directory, databases are opened in memory instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Node also creates the shared store of encrypted Ethereum account keys. Services can access
 | 
				
			||||||
 | 
					the account manager through the service context.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sharing Data Directory Among Instances
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Multiple node instances can share a single data directory if they have distinct instance
 | 
				
			||||||
 | 
					names (set through the Name config option). Sharing behaviour depends on the type of
 | 
				
			||||||
 | 
					resource.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					devp2p-related resources (node key, static/trusted node lists, known hosts database) are
 | 
				
			||||||
 | 
					stored in a directory with the same name as the instance. Thus, multiple node instances
 | 
				
			||||||
 | 
					using the same data directory will store this information in different subdirectories of
 | 
				
			||||||
 | 
					the data directory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LevelDB databases are also stored within the instance subdirectory. If multiple node
 | 
				
			||||||
 | 
					instances use the same data directory, openening the databases with identical names will
 | 
				
			||||||
 | 
					create one database for each instance.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The account key store is shared among all node instances using the same data directory
 | 
				
			||||||
 | 
					unless its location is changed through the KeyStoreDir configuration option.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Data Directory Sharing Example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In this exanple, two node instances named A and B are started with the same data
 | 
				
			||||||
 | 
					directory. Mode instance A opens the database "db", node instance B opens the databases
 | 
				
			||||||
 | 
					"db" and "db-2". The following files will be created in the data directory:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   data-directory/
 | 
				
			||||||
 | 
					        A/
 | 
				
			||||||
 | 
					            nodekey            -- devp2p node key of instance A
 | 
				
			||||||
 | 
					            nodes/             -- devp2p discovery knowledge database of instance A
 | 
				
			||||||
 | 
					            db/                -- LevelDB content for "db"
 | 
				
			||||||
 | 
					        A.ipc                  -- JSON-RPC UNIX domain socket endpoint of instance A
 | 
				
			||||||
 | 
					        B/
 | 
				
			||||||
 | 
					            nodekey            -- devp2p node key of node B
 | 
				
			||||||
 | 
					            nodes/             -- devp2p discovery knowledge database of instance B
 | 
				
			||||||
 | 
					            static-nodes.json  -- devp2p static node list of instance B
 | 
				
			||||||
 | 
					            db/                -- LevelDB content for "db"
 | 
				
			||||||
 | 
					            db-2/              -- LevelDB content for "db-2"
 | 
				
			||||||
 | 
					        B.ipc                  -- JSON-RPC UNIX domain socket endpoint of instance A
 | 
				
			||||||
 | 
					        keystore/              -- account key store, used by both instances
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					package node
 | 
				
			||||||
							
								
								
									
										157
									
								
								node/node.go
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								node/node.go
									
									
									
									
									
								
							@@ -14,7 +14,6 @@
 | 
				
			|||||||
// You should have received a copy of the GNU Lesser General Public License
 | 
					// You should have received a copy of the GNU Lesser General Public License
 | 
				
			||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | 
					// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Package node represents the Ethereum protocol stack container.
 | 
					 | 
				
			||||||
package node
 | 
					package node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@@ -23,16 +22,19 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/accounts"
 | 
						"github.com/ethereum/go-ethereum/accounts"
 | 
				
			||||||
 | 
						"github.com/ethereum/go-ethereum/ethdb"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/event"
 | 
						"github.com/ethereum/go-ethereum/event"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/internal/debug"
 | 
						"github.com/ethereum/go-ethereum/internal/debug"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/logger"
 | 
						"github.com/ethereum/go-ethereum/logger"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/logger/glog"
 | 
						"github.com/ethereum/go-ethereum/logger/glog"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/p2p"
 | 
						"github.com/ethereum/go-ethereum/p2p"
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/rpc"
 | 
						"github.com/ethereum/go-ethereum/rpc"
 | 
				
			||||||
 | 
						"github.com/syndtr/goleveldb/leveldb/storage"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@@ -44,14 +46,14 @@ var (
 | 
				
			|||||||
	datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
 | 
						datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Node represents a P2P node into which arbitrary (uniquely typed) services might
 | 
					// Node is a container on which services can be registered.
 | 
				
			||||||
// be registered.
 | 
					 | 
				
			||||||
type Node struct {
 | 
					type Node struct {
 | 
				
			||||||
	datadir  string         // Path to the currently used data directory
 | 
					 | 
				
			||||||
	eventmux *event.TypeMux // Event multiplexer used between the services of a stack
 | 
						eventmux *event.TypeMux // Event multiplexer used between the services of a stack
 | 
				
			||||||
 | 
						config   *Config
 | 
				
			||||||
	accman   *accounts.Manager
 | 
						accman   *accounts.Manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ephemeralKeystore string          // if non-empty, the key directory that will be removed by Stop
 | 
						ephemeralKeystore string          // if non-empty, the key directory that will be removed by Stop
 | 
				
			||||||
 | 
						instanceDirLock   storage.Storage // prevents concurrent use of instance directory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	serverConfig p2p.Config
 | 
						serverConfig p2p.Config
 | 
				
			||||||
	server       *p2p.Server // Currently running P2P networking layer
 | 
						server       *p2p.Server // Currently running P2P networking layer
 | 
				
			||||||
@@ -66,19 +68,12 @@ type Node struct {
 | 
				
			|||||||
	ipcListener net.Listener // IPC RPC listener socket to serve API requests
 | 
						ipcListener net.Listener // IPC RPC listener socket to serve API requests
 | 
				
			||||||
	ipcHandler  *rpc.Server  // IPC RPC request handler to process the API requests
 | 
						ipcHandler  *rpc.Server  // IPC RPC request handler to process the API requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	httpHost      string       // HTTP hostname
 | 
					 | 
				
			||||||
	httpPort      int          // HTTP post
 | 
					 | 
				
			||||||
	httpEndpoint  string       // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
 | 
						httpEndpoint  string       // HTTP endpoint (interface + port) to listen at (empty = HTTP disabled)
 | 
				
			||||||
	httpWhitelist []string     // HTTP RPC modules to allow through this endpoint
 | 
						httpWhitelist []string     // HTTP RPC modules to allow through this endpoint
 | 
				
			||||||
	httpCors      string       // HTTP RPC Cross-Origin Resource Sharing header
 | 
					 | 
				
			||||||
	httpListener  net.Listener // HTTP RPC listener socket to server API requests
 | 
						httpListener  net.Listener // HTTP RPC listener socket to server API requests
 | 
				
			||||||
	httpHandler   *rpc.Server  // HTTP RPC request handler to process the API requests
 | 
						httpHandler   *rpc.Server  // HTTP RPC request handler to process the API requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wsHost      string       // Websocket host
 | 
					 | 
				
			||||||
	wsPort      int          // Websocket post
 | 
					 | 
				
			||||||
	wsEndpoint string       // Websocket endpoint (interface + port) to listen at (empty = websocket disabled)
 | 
						wsEndpoint string       // Websocket endpoint (interface + port) to listen at (empty = websocket disabled)
 | 
				
			||||||
	wsWhitelist []string     // Websocket RPC modules to allow through this endpoint
 | 
					 | 
				
			||||||
	wsOrigins   string       // Websocket RPC allowed origin domains
 | 
					 | 
				
			||||||
	wsListener net.Listener // Websocket RPC listener socket to server API requests
 | 
						wsListener net.Listener // Websocket RPC listener socket to server API requests
 | 
				
			||||||
	wsHandler  *rpc.Server  // Websocket RPC request handler to process the API requests
 | 
						wsHandler  *rpc.Server  // Websocket RPC request handler to process the API requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -88,53 +83,44 @@ type Node struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// New creates a new P2P node, ready for protocol registration.
 | 
					// New creates a new P2P node, ready for protocol registration.
 | 
				
			||||||
func New(conf *Config) (*Node, error) {
 | 
					func New(conf *Config) (*Node, error) {
 | 
				
			||||||
	// Ensure the data directory exists, failing if it cannot be created
 | 
						// Copy config and resolve the datadir so future changes to the current
 | 
				
			||||||
 | 
						// working directory don't affect the node.
 | 
				
			||||||
 | 
						confCopy := *conf
 | 
				
			||||||
 | 
						conf = &confCopy
 | 
				
			||||||
	if conf.DataDir != "" {
 | 
						if conf.DataDir != "" {
 | 
				
			||||||
		if err := os.MkdirAll(conf.DataDir, 0700); err != nil {
 | 
							absdatadir, err := filepath.Abs(conf.DataDir)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							conf.DataDir = absdatadir
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Ensure that the instance name doesn't cause weird conflicts with
 | 
				
			||||||
 | 
						// other files in the data directory.
 | 
				
			||||||
 | 
						if strings.ContainsAny(conf.Name, `/\`) {
 | 
				
			||||||
 | 
							return nil, errors.New(`Config.Name must not contain '/' or '\'`)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if conf.Name == datadirDefaultKeyStore {
 | 
				
			||||||
 | 
							return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if strings.HasSuffix(conf.Name, ".ipc") {
 | 
				
			||||||
 | 
							return nil, errors.New(`Config.Name cannot end in ".ipc"`)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Ensure that the AccountManager method works before the node has started.
 | 
				
			||||||
 | 
						// We rely on this in cmd/geth.
 | 
				
			||||||
	am, ephemeralKeystore, err := makeAccountManager(conf)
 | 
						am, ephemeralKeystore, err := makeAccountManager(conf)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						// Note: any interaction with Config that would create/touch files
 | 
				
			||||||
	// Assemble the networking layer and the node itself
 | 
						// in the data directory or instance directory is delayed until Start.
 | 
				
			||||||
	nodeDbPath := ""
 | 
					 | 
				
			||||||
	if conf.DataDir != "" {
 | 
					 | 
				
			||||||
		nodeDbPath = filepath.Join(conf.DataDir, datadirNodeDatabase)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return &Node{
 | 
						return &Node{
 | 
				
			||||||
		datadir:           conf.DataDir,
 | 
					 | 
				
			||||||
		accman:            am,
 | 
							accman:            am,
 | 
				
			||||||
		ephemeralKeystore: ephemeralKeystore,
 | 
							ephemeralKeystore: ephemeralKeystore,
 | 
				
			||||||
		serverConfig: p2p.Config{
 | 
							config:            conf,
 | 
				
			||||||
			PrivateKey:      conf.NodeKey(),
 | 
					 | 
				
			||||||
			Name:            conf.Name,
 | 
					 | 
				
			||||||
			Discovery:       !conf.NoDiscovery,
 | 
					 | 
				
			||||||
			BootstrapNodes:  conf.BootstrapNodes,
 | 
					 | 
				
			||||||
			StaticNodes:     conf.StaticNodes(),
 | 
					 | 
				
			||||||
			TrustedNodes:    conf.TrusterNodes(),
 | 
					 | 
				
			||||||
			NodeDatabase:    nodeDbPath,
 | 
					 | 
				
			||||||
			ListenAddr:      conf.ListenAddr,
 | 
					 | 
				
			||||||
			NAT:             conf.NAT,
 | 
					 | 
				
			||||||
			Dialer:          conf.Dialer,
 | 
					 | 
				
			||||||
			NoDial:          conf.NoDial,
 | 
					 | 
				
			||||||
			MaxPeers:        conf.MaxPeers,
 | 
					 | 
				
			||||||
			MaxPendingPeers: conf.MaxPendingPeers,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		serviceFuncs:      []ServiceConstructor{},
 | 
							serviceFuncs:      []ServiceConstructor{},
 | 
				
			||||||
		ipcEndpoint:       conf.IPCEndpoint(),
 | 
							ipcEndpoint:       conf.IPCEndpoint(),
 | 
				
			||||||
		httpHost:      conf.HTTPHost,
 | 
					 | 
				
			||||||
		httpPort:      conf.HTTPPort,
 | 
					 | 
				
			||||||
		httpEndpoint:      conf.HTTPEndpoint(),
 | 
							httpEndpoint:      conf.HTTPEndpoint(),
 | 
				
			||||||
		httpWhitelist: conf.HTTPModules,
 | 
					 | 
				
			||||||
		httpCors:      conf.HTTPCors,
 | 
					 | 
				
			||||||
		wsHost:        conf.WSHost,
 | 
					 | 
				
			||||||
		wsPort:        conf.WSPort,
 | 
					 | 
				
			||||||
		wsEndpoint:        conf.WSEndpoint(),
 | 
							wsEndpoint:        conf.WSEndpoint(),
 | 
				
			||||||
		wsWhitelist:   conf.WSModules,
 | 
					 | 
				
			||||||
		wsOrigins:     conf.WSOrigins,
 | 
					 | 
				
			||||||
		eventmux:          new(event.TypeMux),
 | 
							eventmux:          new(event.TypeMux),
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -161,13 +147,36 @@ func (n *Node) Start() error {
 | 
				
			|||||||
	if n.server != nil {
 | 
						if n.server != nil {
 | 
				
			||||||
		return ErrNodeRunning
 | 
							return ErrNodeRunning
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Otherwise copy and specialize the P2P configuration
 | 
						if err := n.openDataDir(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Initialize the p2p server. This creates the node key and
 | 
				
			||||||
 | 
						// discovery databases.
 | 
				
			||||||
 | 
						n.serverConfig = p2p.Config{
 | 
				
			||||||
 | 
							PrivateKey:      n.config.NodeKey(),
 | 
				
			||||||
 | 
							Name:            n.config.NodeName(),
 | 
				
			||||||
 | 
							Discovery:       !n.config.NoDiscovery,
 | 
				
			||||||
 | 
							BootstrapNodes:  n.config.BootstrapNodes,
 | 
				
			||||||
 | 
							StaticNodes:     n.config.StaticNodes(),
 | 
				
			||||||
 | 
							TrustedNodes:    n.config.TrusterNodes(),
 | 
				
			||||||
 | 
							NodeDatabase:    n.config.NodeDB(),
 | 
				
			||||||
 | 
							ListenAddr:      n.config.ListenAddr,
 | 
				
			||||||
 | 
							NAT:             n.config.NAT,
 | 
				
			||||||
 | 
							Dialer:          n.config.Dialer,
 | 
				
			||||||
 | 
							NoDial:          n.config.NoDial,
 | 
				
			||||||
 | 
							MaxPeers:        n.config.MaxPeers,
 | 
				
			||||||
 | 
							MaxPendingPeers: n.config.MaxPendingPeers,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	running := &p2p.Server{Config: n.serverConfig}
 | 
						running := &p2p.Server{Config: n.serverConfig}
 | 
				
			||||||
 | 
						glog.V(logger.Info).Infoln("instance:", n.serverConfig.Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Otherwise copy and specialize the P2P configuration
 | 
				
			||||||
	services := make(map[reflect.Type]Service)
 | 
						services := make(map[reflect.Type]Service)
 | 
				
			||||||
	for _, constructor := range n.serviceFuncs {
 | 
						for _, constructor := range n.serviceFuncs {
 | 
				
			||||||
		// Create a new context for the particular service
 | 
							// Create a new context for the particular service
 | 
				
			||||||
		ctx := &ServiceContext{
 | 
							ctx := &ServiceContext{
 | 
				
			||||||
			datadir:        n.datadir,
 | 
								config:         n.config,
 | 
				
			||||||
			services:       make(map[reflect.Type]Service),
 | 
								services:       make(map[reflect.Type]Service),
 | 
				
			||||||
			EventMux:       n.eventmux,
 | 
								EventMux:       n.eventmux,
 | 
				
			||||||
			AccountManager: n.accman,
 | 
								AccountManager: n.accman,
 | 
				
			||||||
@@ -227,6 +236,26 @@ func (n *Node) Start() error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *Node) openDataDir() error {
 | 
				
			||||||
 | 
						if n.config.DataDir == "" {
 | 
				
			||||||
 | 
							return nil // ephemeral
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						instdir := filepath.Join(n.config.DataDir, n.config.name())
 | 
				
			||||||
 | 
						if err := os.MkdirAll(instdir, 0700); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Try to open the instance directory as LevelDB storage. This creates a lock file
 | 
				
			||||||
 | 
						// which prevents concurrent use by another instance as well as accidental use of the
 | 
				
			||||||
 | 
						// instance directory as a database.
 | 
				
			||||||
 | 
						storage, err := storage.OpenFile(instdir, true)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						n.instanceDirLock = storage
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// startRPC is a helper method to start all the various RPC endpoint during node
 | 
					// startRPC is a helper method to start all the various RPC endpoint during node
 | 
				
			||||||
// startup. It's not meant to be called at any time afterwards as it makes certain
 | 
					// startup. It's not meant to be called at any time afterwards as it makes certain
 | 
				
			||||||
// assumptions about the state of the node.
 | 
					// assumptions about the state of the node.
 | 
				
			||||||
@@ -244,12 +273,12 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
 | 
				
			|||||||
		n.stopInProc()
 | 
							n.stopInProc()
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := n.startHTTP(n.httpEndpoint, apis, n.httpWhitelist, n.httpCors); err != nil {
 | 
						if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors); err != nil {
 | 
				
			||||||
		n.stopIPC()
 | 
							n.stopIPC()
 | 
				
			||||||
		n.stopInProc()
 | 
							n.stopInProc()
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := n.startWS(n.wsEndpoint, apis, n.wsWhitelist, n.wsOrigins); err != nil {
 | 
						if err := n.startWS(n.wsEndpoint, apis, n.config.WSModules, n.config.WSOrigins); err != nil {
 | 
				
			||||||
		n.stopHTTP()
 | 
							n.stopHTTP()
 | 
				
			||||||
		n.stopIPC()
 | 
							n.stopIPC()
 | 
				
			||||||
		n.stopInProc()
 | 
							n.stopInProc()
 | 
				
			||||||
@@ -381,7 +410,6 @@ func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors
 | 
				
			|||||||
	n.httpEndpoint = endpoint
 | 
						n.httpEndpoint = endpoint
 | 
				
			||||||
	n.httpListener = listener
 | 
						n.httpListener = listener
 | 
				
			||||||
	n.httpHandler = handler
 | 
						n.httpHandler = handler
 | 
				
			||||||
	n.httpCors = cors
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -436,7 +464,6 @@ func (n *Node) startWS(endpoint string, apis []rpc.API, modules []string, wsOrig
 | 
				
			|||||||
	n.wsEndpoint = endpoint
 | 
						n.wsEndpoint = endpoint
 | 
				
			||||||
	n.wsListener = listener
 | 
						n.wsListener = listener
 | 
				
			||||||
	n.wsHandler = handler
 | 
						n.wsHandler = handler
 | 
				
			||||||
	n.wsOrigins = wsOrigins
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -465,12 +492,12 @@ func (n *Node) Stop() error {
 | 
				
			|||||||
	if n.server == nil {
 | 
						if n.server == nil {
 | 
				
			||||||
		return ErrNodeStopped
 | 
							return ErrNodeStopped
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Otherwise terminate the API, all services and the P2P server too
 | 
					
 | 
				
			||||||
 | 
						// Terminate the API, services and the p2p server.
 | 
				
			||||||
	n.stopWS()
 | 
						n.stopWS()
 | 
				
			||||||
	n.stopHTTP()
 | 
						n.stopHTTP()
 | 
				
			||||||
	n.stopIPC()
 | 
						n.stopIPC()
 | 
				
			||||||
	n.rpcAPIs = nil
 | 
						n.rpcAPIs = nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
	failure := &StopError{
 | 
						failure := &StopError{
 | 
				
			||||||
		Services: make(map[reflect.Type]error),
 | 
							Services: make(map[reflect.Type]error),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -480,9 +507,16 @@ func (n *Node) Stop() error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	n.server.Stop()
 | 
						n.server.Stop()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	n.services = nil
 | 
						n.services = nil
 | 
				
			||||||
	n.server = nil
 | 
						n.server = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Release instance directory lock.
 | 
				
			||||||
 | 
						if n.instanceDirLock != nil {
 | 
				
			||||||
 | 
							n.instanceDirLock.Close()
 | 
				
			||||||
 | 
							n.instanceDirLock = nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// unblock n.Wait
 | 
				
			||||||
	close(n.stop)
 | 
						close(n.stop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Remove the keystore if it was created ephemerally.
 | 
						// Remove the keystore if it was created ephemerally.
 | 
				
			||||||
@@ -566,7 +600,7 @@ func (n *Node) Service(service interface{}) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// DataDir retrieves the current datadir used by the protocol stack.
 | 
					// DataDir retrieves the current datadir used by the protocol stack.
 | 
				
			||||||
func (n *Node) DataDir() string {
 | 
					func (n *Node) DataDir() string {
 | 
				
			||||||
	return n.datadir
 | 
						return n.config.DataDir
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AccountManager retrieves the account manager used by the protocol stack.
 | 
					// AccountManager retrieves the account manager used by the protocol stack.
 | 
				
			||||||
@@ -595,6 +629,21 @@ func (n *Node) EventMux() *event.TypeMux {
 | 
				
			|||||||
	return n.eventmux
 | 
						return n.eventmux
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OpenDatabase opens an existing database with the given name (or creates one if no
 | 
				
			||||||
 | 
					// previous can be found) from within the node's instance directory. If the node is
 | 
				
			||||||
 | 
					// ephemeral, a memory database is returned.
 | 
				
			||||||
 | 
					func (n *Node) OpenDatabase(name string, cache, handles int) (ethdb.Database, error) {
 | 
				
			||||||
 | 
						if n.config.DataDir == "" {
 | 
				
			||||||
 | 
							return ethdb.NewMemDatabase()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ethdb.NewLDBDatabase(n.config.resolvePath(name), cache, handles)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ResolvePath returns the absolute path of a resource in the instance directory.
 | 
				
			||||||
 | 
					func (n *Node) ResolvePath(x string) string {
 | 
				
			||||||
 | 
						return n.config.resolvePath(x)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// apis returns the collection of RPC descriptors this node offers.
 | 
					// apis returns the collection of RPC descriptors this node offers.
 | 
				
			||||||
func (n *Node) apis() []rpc.API {
 | 
					func (n *Node) apis() []rpc.API {
 | 
				
			||||||
	return []rpc.API{
 | 
						return []rpc.API{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@
 | 
				
			|||||||
package node
 | 
					package node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/ethereum/go-ethereum/accounts"
 | 
						"github.com/ethereum/go-ethereum/accounts"
 | 
				
			||||||
@@ -31,7 +30,7 @@ import (
 | 
				
			|||||||
// the protocol stack, that is passed to all constructors to be optionally used;
 | 
					// the protocol stack, that is passed to all constructors to be optionally used;
 | 
				
			||||||
// as well as utility methods to operate on the service environment.
 | 
					// as well as utility methods to operate on the service environment.
 | 
				
			||||||
type ServiceContext struct {
 | 
					type ServiceContext struct {
 | 
				
			||||||
	datadir        string                   // Data directory for protocol persistence
 | 
						config         *Config
 | 
				
			||||||
	services       map[reflect.Type]Service // Index of the already constructed services
 | 
						services       map[reflect.Type]Service // Index of the already constructed services
 | 
				
			||||||
	EventMux       *event.TypeMux           // Event multiplexer used for decoupled notifications
 | 
						EventMux       *event.TypeMux           // Event multiplexer used for decoupled notifications
 | 
				
			||||||
	AccountManager *accounts.Manager        // Account manager created by the node.
 | 
						AccountManager *accounts.Manager        // Account manager created by the node.
 | 
				
			||||||
@@ -41,10 +40,10 @@ type ServiceContext struct {
 | 
				
			|||||||
// if no previous can be found) from within the node's data directory. If the
 | 
					// if no previous can be found) from within the node's data directory. If the
 | 
				
			||||||
// node is an ephemeral one, a memory database is returned.
 | 
					// node is an ephemeral one, a memory database is returned.
 | 
				
			||||||
func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int) (ethdb.Database, error) {
 | 
					func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int) (ethdb.Database, error) {
 | 
				
			||||||
	if ctx.datadir == "" {
 | 
						if ctx.config.DataDir == "" {
 | 
				
			||||||
		return ethdb.NewMemDatabase()
 | 
							return ethdb.NewMemDatabase()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ethdb.NewLDBDatabase(filepath.Join(ctx.datadir, name), cache, handles)
 | 
						return ethdb.NewLDBDatabase(ctx.config.resolvePath(name), cache, handles)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Service retrieves a currently running service registered of a specific type.
 | 
					// Service retrieves a currently running service registered of a specific type.
 | 
				
			||||||
@@ -64,10 +63,12 @@ type ServiceConstructor func(ctx *ServiceContext) (Service, error)
 | 
				
			|||||||
// Service is an individual protocol that can be registered into a node.
 | 
					// Service is an individual protocol that can be registered into a node.
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// Notes:
 | 
					// Notes:
 | 
				
			||||||
//  - Service life-cycle management is delegated to the node. The service is
 | 
					//
 | 
				
			||||||
//    allowed to initialize itself upon creation, but no goroutines should be
 | 
					// • Service life-cycle management is delegated to the node. The service is allowed to
 | 
				
			||||||
//    spun up outside of the Start method.
 | 
					// initialize itself upon creation, but no goroutines should be spun up outside of the
 | 
				
			||||||
//  - Restart logic is not required as the node will create a fresh instance
 | 
					// Start method.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// • Restart logic is not required as the node will create a fresh instance
 | 
				
			||||||
// every time a service is started.
 | 
					// every time a service is started.
 | 
				
			||||||
type Service interface {
 | 
					type Service interface {
 | 
				
			||||||
	// Protocols retrieves the P2P protocols the service wishes to start.
 | 
						// Protocols retrieves the P2P protocols the service wishes to start.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,18 +38,18 @@ func TestContextDatabases(t *testing.T) {
 | 
				
			|||||||
		t.Fatalf("non-created database already exists")
 | 
							t.Fatalf("non-created database already exists")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Request the opening/creation of a database and ensure it persists to disk
 | 
						// Request the opening/creation of a database and ensure it persists to disk
 | 
				
			||||||
	ctx := &ServiceContext{datadir: dir}
 | 
						ctx := &ServiceContext{config: &Config{Name: "unit-test", DataDir: dir}}
 | 
				
			||||||
	db, err := ctx.OpenDatabase("persistent", 0, 0)
 | 
						db, err := ctx.OpenDatabase("persistent", 0, 0)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to open persistent database: %v", err)
 | 
							t.Fatalf("failed to open persistent database: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	db.Close()
 | 
						db.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := os.Stat(filepath.Join(dir, "persistent")); err != nil {
 | 
						if _, err := os.Stat(filepath.Join(dir, "unit-test", "persistent")); err != nil {
 | 
				
			||||||
		t.Fatalf("persistent database doesn't exists: %v", err)
 | 
							t.Fatalf("persistent database doesn't exists: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Request th opening/creation of an ephemeral database and ensure it's not persisted
 | 
						// Request th opening/creation of an ephemeral database and ensure it's not persisted
 | 
				
			||||||
	ctx = &ServiceContext{datadir: ""}
 | 
						ctx = &ServiceContext{config: &Config{DataDir: ""}}
 | 
				
			||||||
	db, err = ctx.OpenDatabase("ephemeral", 0, 0)
 | 
						db, err = ctx.OpenDatabase("ephemeral", 0, 0)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatalf("failed to open ephemeral database: %v", err)
 | 
							t.Fatalf("failed to open ephemeral database: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user