Skip to content

Commit

Permalink
chain_bridge: fetch block headers over blocks
Browse files Browse the repository at this point in the history
In this commit, we update the header verifier to use the new
GetBlockHeader RPC when supported, and avoid fetching a full block.
This improves the reliability of proof validation and universe sync for
light clients.
  • Loading branch information
jharveyb committed Nov 14, 2023
1 parent f4fc9b9 commit 6013359
Showing 1 changed file with 49 additions and 1 deletion.
50 changes: 49 additions & 1 deletion chain_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ import (
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/taproot-assets/tapgarden"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/lnrpc/verrpc"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)

// LndRpcChainBridge is an implementation of the tapgarden.ChainBridge
// interface backed by an active remote lnd node.
type LndRpcChainBridge struct {
lnd *lndclient.LndServices

getBlockHeaderSupported *bool
}

// NewLndRpcChainBridge creates a new chain bridge from an active lnd services
Expand Down Expand Up @@ -79,6 +82,19 @@ func (l *LndRpcChainBridge) GetBlock(ctx context.Context,
return block, nil
}

// GetBlockHeader returns a block header given its hash.
func (l *LndRpcChainBridge) GetBlockHeader(ctx context.Context,
hash chainhash.Hash) (*wire.BlockHeader, error) {

header, err := l.lnd.ChainKit.GetBlockHeader(ctx, hash)
if err != nil {
return nil, fmt.Errorf("unable to retrieve block header: %w",
err)
}

return header, nil
}

// GetBlockHash returns the hash of the block in the best blockchain at the
// given height.
func (l *LndRpcChainBridge) GetBlockHash(ctx context.Context,
Expand All @@ -93,6 +109,31 @@ func (l *LndRpcChainBridge) GetBlockHash(ctx context.Context,
return blockHash, nil
}

// GetBlockHeaderSupported returns true if the chain backend supports the
// `GetBlockHeader` RPC call.
func (l *LndRpcChainBridge) GetBlockHeaderSupported(ctx context.Context) bool {
// Check if we've already asserted the compatibility of the chain
// backend.
if l.getBlockHeaderSupported != nil {
return *l.getBlockHeaderSupported
}

// The ChainKit.GetBlockHeader() RPC call was added in lnd v0.17.1.
getBlockHeaderMinimalVersion := &verrpc.Version{
AppMajor: 0,
AppMinor: 17,
AppPatch: 1,
}

getBlockHeaderUnsupported := lndclient.AssertVersionCompatible(
l.lnd.Version, getBlockHeaderMinimalVersion,
)
getBlockHeaderSupported := getBlockHeaderUnsupported == nil

l.getBlockHeaderSupported = &getBlockHeaderSupported
return *l.getBlockHeaderSupported
}

// VerifyBlock returns an error if a block (with given header and height) is not
// present on-chain. It also checks to ensure that block height corresponds to
// the given block header.
Expand Down Expand Up @@ -121,7 +162,14 @@ func (l *LndRpcChainBridge) VerifyBlock(ctx context.Context,
"expectedHash: %s)", height, hash, expectedHash)
}

// Ensure that the block header corresponds to a block on-chain.
// Ensure that the block header corresponds to a block on-chain. Fetch
// only the corresponding block header and not the entire block if
// supported.
if l.GetBlockHeaderSupported(ctx) {
_, err = l.GetBlockHeader(ctx, header.BlockHash())
return err
}

_, err = l.GetBlock(ctx, header.BlockHash())
return err
}
Expand Down

0 comments on commit 6013359

Please sign in to comment.