diff --git a/cmd/geth/main.go b/cmd/geth/main.go index d556ad92c3..a45009e6cb 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -261,15 +261,7 @@ func geth(ctx *cli.Context) error { return nil } -// startNode boots up the system node and all registered protocols, after which -// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the -// miner. -func startNode(ctx *cli.Context, stack *node.Node) { - debug.Memsize.Add("node", stack) - - // Start up the node itself - utils.StartNode(stack) - +func startInternalAccountManagement(ctx *cli.Context, stack *node.Node) { // Unlock any account specifically requested ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) @@ -283,7 +275,6 @@ func startNode(ctx *cli.Context, stack *node.Node) { // Register wallet event handlers to open and auto-derive wallets events := make(chan accounts.WalletEvent, 16) stack.AccountManager().Subscribe(events) - go func() { // Create a chain state reader for self-derivation rpcClient, err := stack.Attach() @@ -321,6 +312,25 @@ func startNode(ctx *cli.Context, stack *node.Node) { } } }() +} + +// startNode boots up the system node and all registered protocols, after which +// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the +// miner. +func startNode(ctx *cli.Context, stack *node.Node) { + debug.Memsize.Add("node", stack) + + // Start up the node itself + utils.StartNode(stack) + + // Start account management + if ctx.IsSet(utils.ExternalSignerFlag.Name){ + extApi := ctx.GlobalString(utils.ExternalSignerFlag.Name) + + }else{ + startInternalAccountManagement(ctx, stack) + } + // Start auxiliary services if enabled if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) { // Mining only makes sense if a full Ethereum node is running diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index d6142f246c..475dd909fd 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -355,6 +355,11 @@ var ( Usage: "Password file to use for non-interactive password input", Value: "", } + ExternalSignerFlag = cli.StringFlag{ + Name: "signer", + Usage: "External signer (url or path to ipc file)", + Value: "", + } VMEnableDebugFlag = cli.BoolFlag{ Name: "vmdebug", diff --git a/eth/backend.go b/eth/backend.go index 865534b198..a0212b9f1e 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -78,6 +78,7 @@ type Ethereum struct { eventMux *event.TypeMux engine consensus.Engine accountManager *accounts.Manager + externalSigner string bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports @@ -372,6 +373,7 @@ func (s *Ethereum) IsListening() bool { return true } // Always func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } func (s *Ethereum) NetVersion() uint64 { return s.networkID } func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } +func (s *Ethereum) ExternalSigner() string { return s.externalSigner } // Protocols implements node.Service, returning all the currently configured // network protocols to start. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index d1e6eeefb0..f70bbda97c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -211,13 +211,20 @@ type PrivateAccountAPI struct { // NewPrivateAccountAPI create a new PrivateAccountAPI. func NewPrivateAccountAPI(b Backend, nonceLock *AddrLocker) *PrivateAccountAPI { - extapi, _ := NewExternalSigner() - return &PrivateAccountAPI{ + p := &PrivateAccountAPI{ am: b.AccountManager(), nonceLock: nonceLock, b: b, - extapi: extapi, } + if b.ExternalSigner() != "" { + extapi, err := NewExternalSigner(b.ExternalSigner()) + if err == nil { + p.extapi = extapi + } else { + log.Error("Error initializing external signer", "url", b.ExternalSigner(), "error", err) + } + } + return p } // ListAccounts will return a list of addresses for accounts this node manages. @@ -984,7 +991,18 @@ type PublicTransactionPoolAPI struct { // NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI { - return &PublicTransactionPoolAPI{b, nonceLock, nil} + var ( + extapi *ExternalSignerAPI + err error + ) + if b.ExternalSigner() != "" { + extapi, err = NewExternalSigner(b.ExternalSigner()) + if err != nil { + log.Error("Error initializing external signer", "url", b.ExternalSigner(), "error", err) + } + + } + return &PublicTransactionPoolAPI{b, nonceLock, extapi} } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. @@ -1534,8 +1552,8 @@ type ExternalSignerAPI struct { client *rpc.Client } -func NewExternalSigner() (*ExternalSignerAPI, error) { - client, err := rpc.DialHTTP("http://localhost:8550") +func NewExternalSigner(endpoint string) (*ExternalSignerAPI, error) { + client, err := rpc.DialHTTP(endpoint) if err != nil { return nil, err } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index c9ffe230c6..f7946b367f 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -44,7 +44,8 @@ type Backend interface { ChainDb() ethdb.Database EventMux() *event.TypeMux AccountManager() *accounts.Manager - + ExternalSigner() string + // BlockChain API SetHead(number uint64) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) diff --git a/les/api_backend.go b/les/api_backend.go index 4232d3ae07..bd5a9b873b 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -187,6 +187,10 @@ func (b *LesApiBackend) AccountManager() *accounts.Manager { return b.eth.accountManager } +func (b *LesApiBackend) ExternalSigner() string { + return b.eth.externalSigner +} + func (b *LesApiBackend) BloomStatus() (uint64, uint64) { if b.eth.bloomIndexer == nil { return 0, 0 diff --git a/les/backend.go b/les/backend.go index 178bc1e0e4..3aaafa4c88 100644 --- a/les/backend.go +++ b/les/backend.go @@ -73,6 +73,7 @@ type LightEthereum struct { eventMux *event.TypeMux engine consensus.Engine accountManager *accounts.Manager + externalSigner string networkId uint64 netRPCService *ethapi.PublicNetAPI diff --git a/node/config.go b/node/config.go index a4d5920a8b..8bee88c116 100644 --- a/node/config.go +++ b/node/config.go @@ -86,6 +86,10 @@ type Config struct { // NoUSB disables hardware wallet monitoring and connectivity. NoUSB bool `toml:",omitempty"` + // ExternalSigner path/url to external signer. Implicitly disables native account + // management + ExternalSigner string `toml:",omitempty"` + // IPCPath is the requested location to place the IPC endpoint. If the path is // a simple file name, it is placed inside the data directory (or on the root // pipe path on Windows), whereas if it's a resolvable path name (absolute or diff --git a/node/node.go b/node/node.go index ada3837217..429cec89d1 100644 --- a/node/node.go +++ b/node/node.go @@ -41,6 +41,7 @@ type Node struct { eventmux *event.TypeMux // Event multiplexer used between the services of a stack config *Config accman *accounts.Manager + extapi string // If non-empty, the ipc-path or http(s)-url to an external signer ephemeralKeystore string // if non-empty, the key directory that will be removed by Stop instanceDirLock flock.Releaser // prevents concurrent use of instance directory @@ -99,9 +100,15 @@ func New(conf *Config) (*Node, error) { } // Ensure that the AccountManager method works before the node has started. // We rely on this in cmd/geth. - am, ephemeralKeystore, err := makeAccountManager(conf) - if err != nil { - return nil, err + var ( + am *accounts.Manager + ephemeralKeystore string + ) + if conf.ExternalSigner != "" { + var err error + if am, ephemeralKeystore, err = makeAccountManager(conf); err != nil { + return nil, err + } } if conf.Logger == nil { conf.Logger = log.New() @@ -110,6 +117,7 @@ func New(conf *Config) (*Node, error) { // in the data directory or instance directory is delayed until Start. return &Node{ accman: am, + extapi: conf.ExternalSigner, ephemeralKeystore: ephemeralKeystore, config: conf, serviceFuncs: []ServiceConstructor{},