lnd.xprv/walletunlocker/service.go
2018-02-05 21:44:06 -08:00

138 lines
4.1 KiB
Go

package walletunlocker
import (
"fmt"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcwallet/wallet"
"golang.org/x/net/context"
)
// UnlockerService implements the WalletUnlocker service used to provide lnd
// with a password for wallet encryption at startup.
type UnlockerService struct {
// CreatePasswords is a channel where passwords provided by the rpc
// client to be used to initially create and encrypt a wallet will be
// sent.
CreatePasswords chan []byte
// UnlockPasswords is a channel where passwords provided by the rpc
// client to be used to unlock and decrypt an existing wallet will be
// sent.
UnlockPasswords chan []byte
chainDir string
netParams *chaincfg.Params
authSvc *macaroons.Service
}
// New creates and returns a new UnlockerService.
func New(authSvc *macaroons.Service, chainDir string,
params *chaincfg.Params) *UnlockerService {
return &UnlockerService{
CreatePasswords: make(chan []byte, 1),
UnlockPasswords: make(chan []byte, 1),
chainDir: chainDir,
netParams: params,
}
}
// CreateWallet will read the password provided in the CreateWalletRequest and
// send it over the CreatePasswords channel in case no wallet already exist in
// the chain's wallet database directory.
func (u *UnlockerService) CreateWallet(ctx context.Context,
in *lnrpc.CreateWalletRequest) (*lnrpc.CreateWalletResponse, error) {
// Require the provided password to have a length of at
// least 8 characters.
password := in.Password
if len(password) < 8 {
return nil, fmt.Errorf("password must have " +
"at least 8 characters")
}
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir)
// Check if wallet already exists.
walletExists, err := loader.WalletExists()
if err != nil {
return nil, err
}
if walletExists {
// Cannot create wallet if it already exists!
return nil, fmt.Errorf("wallet already exists")
}
// Attempt to create a password for the macaroon service.
if u.authSvc != nil {
err = u.authSvc.CreateUnlock(&password)
if err != nil {
return nil, fmt.Errorf("unable to create/unlock "+
"macaroon store: %v", err)
}
}
// We send the password over the CreatePasswords channel, such that it
// can be used by lnd to open or create the wallet.
u.CreatePasswords <- password
return &lnrpc.CreateWalletResponse{}, nil
}
// UnlockWallet sends the password provided by the incoming UnlockWalletRequest
// over the UnlockPasswords channel in case it successfully decrypts an
// existing wallet found in the chain's wallet database directory.
func (u *UnlockerService) UnlockWallet(ctx context.Context,
in *lnrpc.UnlockWalletRequest) (*lnrpc.UnlockWalletResponse, error) {
netDir := btcwallet.NetworkDir(u.chainDir, u.netParams)
loader := wallet.NewLoader(u.netParams, netDir)
// Check if wallet already exists.
walletExists, err := loader.WalletExists()
if err != nil {
return nil, err
}
if !walletExists {
// Cannot unlock a wallet that does not exist!
return nil, fmt.Errorf("wallet not found")
}
// Try opening the existing wallet with the provided password.
_, err = loader.OpenExistingWallet(in.Password, false)
if err != nil {
// Could not open wallet, most likely this means that
// provided password was incorrect.
return nil, err
}
// We successfully opened the wallet, but we'll need to unload
// it to make sure lnd can open it later.
if err := loader.UnloadWallet(); err != nil {
// TODO: not return error here?
return nil, err
}
// Attempt to create a password for the macaroon service.
if u.authSvc != nil {
err = u.authSvc.CreateUnlock(&in.Password)
if err != nil {
return nil, fmt.Errorf("unable to create/unlock "+
"macaroon store: %v", err)
}
}
// At this point we was able to open the existing wallet with the
// provided password. We send the password over the UnlockPasswords
// channel, such that it can be used by lnd to open the wallet.
u.UnlockPasswords <- in.Password
return &lnrpc.UnlockWalletResponse{}, nil
}