From 0bdac59a8cd617cfccb3b2c2aada335a640a5163 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 5 Jan 2022 11:04:31 +0100 Subject: [PATCH] multi: add nochainbackend option --- chainreg/chainregistry.go | 16 +++ chainreg/no_chain_backend.go | 215 +++++++++++++++++++++++++++++++++++ config.go | 4 + lncfg/chain.go | 2 +- 4 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 chainreg/no_chain_backend.go diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go index 8364b67df..48cc5df37 100644 --- a/chainreg/chainregistry.go +++ b/chainreg/chainregistry.go @@ -690,6 +690,22 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) { return nil, nil, err } } + + case "nochainbackend": + backend := &NoChainBackend{} + source := &NoChainSource{ + BestBlockTime: time.Now(), + } + + cc.ChainNotifier = backend + cc.ChainView = backend + cc.FeeEstimator = backend + + cc.ChainSource = source + cc.HealthCheck = func() error { + return nil + } + default: return nil, nil, fmt.Errorf("unknown node type: %s", homeChainConfig.Node) diff --git a/chainreg/no_chain_backend.go b/chainreg/no_chain_backend.go new file mode 100644 index 000000000..8f5bc0fbe --- /dev/null +++ b/chainreg/no_chain_backend.go @@ -0,0 +1,215 @@ +package chainreg + +import ( + "errors" + "time" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/chain" + "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/routing/chainview" +) + +var ( + // defaultFee is the fee that is returned by NoChainBackend. + defaultFee = chainfee.FeePerKwFloor + + // noChainBackendName is the backend name returned by NoChainBackend. + noChainBackendName = "nochainbackend" + + // errNotImplemented is the error that is returned by NoChainBackend for + // any operation that is not supported by it. Such paths should in + // practice never been hit, so seeing this error either means a remote + // signing instance was used for an unsupported purpose or a previously + // forgotten edge case path was hit. + errNotImplemented = errors.New("not implemented in nochainbackend " + + "mode") + + // noChainBackendBestHash is the chain hash of the chain tip that is + // returned by NoChainBackend. + noChainBackendBestHash = &chainhash.Hash{0x01} + + // noChainBackendBestHeight is the best height that is returned by + // NoChainBackend. + noChainBackendBestHeight int32 = 1 +) + +// NoChainBackend is a mock implementation of the following interfaces: +// - chainview.FilteredChainView +// - chainntnfs.ChainNotifier +// - chainfee.Estimator +type NoChainBackend struct { +} + +func (n *NoChainBackend) EstimateFeePerKW(uint32) (chainfee.SatPerKWeight, + error) { + + return defaultFee, nil +} + +func (n *NoChainBackend) RelayFeePerKW() chainfee.SatPerKWeight { + return defaultFee +} + +func (n *NoChainBackend) RegisterConfirmationsNtfn(*chainhash.Hash, []byte, + uint32, uint32) (*chainntnfs.ConfirmationEvent, error) { + + return nil, errNotImplemented +} + +func (n *NoChainBackend) RegisterSpendNtfn(*wire.OutPoint, []byte, + uint32) (*chainntnfs.SpendEvent, error) { + + return nil, errNotImplemented +} + +func (n *NoChainBackend) RegisterBlockEpochNtfn( + *chainntnfs.BlockEpoch) (*chainntnfs.BlockEpochEvent, error) { + + epochChan := make(chan *chainntnfs.BlockEpoch) + return &chainntnfs.BlockEpochEvent{ + Epochs: epochChan, + Cancel: func() { + close(epochChan) + }, + }, nil +} + +func (n *NoChainBackend) Started() bool { + return true +} + +func (n *NoChainBackend) FilteredBlocks() <-chan *chainview.FilteredBlock { + return make(chan *chainview.FilteredBlock) +} + +func (n *NoChainBackend) DisconnectedBlocks() <-chan *chainview.FilteredBlock { + return make(chan *chainview.FilteredBlock) +} + +func (n *NoChainBackend) UpdateFilter([]channeldb.EdgePoint, uint32) error { + return nil +} + +func (n *NoChainBackend) FilterBlock(*chainhash.Hash) (*chainview.FilteredBlock, + error) { + + return nil, errNotImplemented +} + +func (n *NoChainBackend) Start() error { + return nil +} + +func (n *NoChainBackend) Stop() error { + return nil +} + +var _ chainview.FilteredChainView = (*NoChainBackend)(nil) +var _ chainntnfs.ChainNotifier = (*NoChainBackend)(nil) +var _ chainfee.Estimator = (*NoChainBackend)(nil) + +// NoChainSource is a mock implementation of chain.Interface. +// The mock is designed to return static values where necessary to make any +// caller believe the chain is fully synced to virtual block height 1 (hash +// 0x0000..0001). That should avoid calls to other methods completely since they +// are only used for advancing the chain forward. +type NoChainSource struct { + notifChan chan interface{} + + BestBlockTime time.Time +} + +func (n *NoChainSource) Start() error { + n.notifChan = make(chan interface{}) + + go func() { + n.notifChan <- &chain.RescanFinished{ + Hash: noChainBackendBestHash, + Height: noChainBackendBestHeight, + Time: n.BestBlockTime, + } + }() + + return nil +} + +func (n *NoChainSource) Stop() { +} + +func (n *NoChainSource) WaitForShutdown() { +} + +func (n *NoChainSource) GetBestBlock() (*chainhash.Hash, int32, error) { + return noChainBackendBestHash, noChainBackendBestHeight, nil +} + +func (n *NoChainSource) GetBlock(*chainhash.Hash) (*wire.MsgBlock, error) { + return &wire.MsgBlock{ + Header: wire.BlockHeader{ + Timestamp: n.BestBlockTime, + }, + Transactions: []*wire.MsgTx{}, + }, nil +} + +func (n *NoChainSource) GetBlockHash(int64) (*chainhash.Hash, error) { + return noChainBackendBestHash, nil +} + +func (n *NoChainSource) GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, + error) { + + return &wire.BlockHeader{ + Timestamp: n.BestBlockTime, + }, nil +} + +func (n *NoChainSource) IsCurrent() bool { + return true +} + +func (n *NoChainSource) FilterBlocks( + *chain.FilterBlocksRequest) (*chain.FilterBlocksResponse, error) { + + return nil, errNotImplemented +} + +func (n *NoChainSource) BlockStamp() (*waddrmgr.BlockStamp, error) { + return nil, errNotImplemented +} + +func (n *NoChainSource) SendRawTransaction(*wire.MsgTx, bool) (*chainhash.Hash, + error) { + + return nil, errNotImplemented +} + +func (n *NoChainSource) Rescan(*chainhash.Hash, []btcutil.Address, + map[wire.OutPoint]btcutil.Address) error { + + return nil +} + +func (n *NoChainSource) NotifyReceived([]btcutil.Address) error { + return nil +} + +func (n *NoChainSource) NotifyBlocks() error { + return nil +} + +func (n *NoChainSource) Notifications() <-chan interface{} { + return n.notifChan +} + +func (n *NoChainSource) BackEnd() string { + return noChainBackendName +} + +var _ chain.Interface = (*NoChainSource)(nil) diff --git a/config.go b/config.go index 0928ead3e..218b42b24 100644 --- a/config.go +++ b/config.go @@ -1230,6 +1230,10 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser, case "neutrino": // No need to get RPC parameters. + case "nochainbackend": + // Nothing to configure, we're running without any chain + // backend whatsoever (pure signing mode). + default: str := "only btcd, bitcoind, and neutrino mode " + "supported for bitcoin at this time" diff --git a/lncfg/chain.go b/lncfg/chain.go index 014cd19ca..0ee4e3ada 100644 --- a/lncfg/chain.go +++ b/lncfg/chain.go @@ -11,7 +11,7 @@ type Chain struct { Active bool `long:"active" description:"If the chain should be active or not."` ChainDir string `long:"chaindir" description:"The directory to store the chain's data within."` - Node string `long:"node" description:"The blockchain interface to use." choice:"btcd" choice:"bitcoind" choice:"neutrino" choice:"ltcd" choice:"litecoind"` + Node string `long:"node" description:"The blockchain interface to use." choice:"btcd" choice:"bitcoind" choice:"neutrino" choice:"ltcd" choice:"litecoind" choice:"nochainbackend"` MainNet bool `long:"mainnet" description:"Use the main network"` TestNet3 bool `long:"testnet" description:"Use the test network"`