@@ -730,6 +730,194 @@ func (c *ChannelStateDB) FetchChannelByID(tx kvdb.RTx, id lnwire.ChannelID) (
730730 return c .channelScanner (tx , selector )
731731}
732732
733+ // ChanCount is used by the server in determining access control.
734+ type ChanCount struct {
735+ HasOpenOrClosedChan bool
736+ PendingOpenCount uint64
737+ }
738+
739+ // FetchPermAndTempPeers returns a map where the key is the remote node's
740+ // public key and the value is a struct that has a tally of the pending-open
741+ // channels and whether the peer has an open or closed channel with us.
742+ func (c * ChannelStateDB ) FetchPermAndTempPeers (
743+ chainHash []byte ) (map [string ]ChanCount , error ) {
744+
745+ peerCounts := make (map [string ]ChanCount )
746+
747+ err := kvdb .View (c .backend , func (tx kvdb.RTx ) error {
748+ openChanBucket := tx .ReadBucket (openChannelBucket )
749+ if openChanBucket == nil {
750+ return ErrNoChanDBExists
751+ }
752+
753+ openChanErr := openChanBucket .ForEach (func (nodePub ,
754+ v []byte ) error {
755+
756+ // If there is a value, this is not a bucket.
757+ if v != nil {
758+ return nil
759+ }
760+
761+ nodeChanBucket := openChanBucket .NestedReadBucket (
762+ nodePub ,
763+ )
764+ if nodeChanBucket == nil {
765+ return nil
766+ }
767+
768+ chainBucket := nodeChanBucket .NestedReadBucket (
769+ chainHash ,
770+ )
771+ if chainBucket == nil {
772+ return fmt .Errorf ("no chain bucket exists" )
773+ }
774+
775+ var isPermPeer bool
776+ var pendingOpenCount uint64
777+
778+ internalErr := chainBucket .ForEach (func (chanPoint ,
779+ val []byte ) error {
780+
781+ // If there is a value, this is not a bucket.
782+ if val != nil {
783+ return nil
784+ }
785+
786+ chanBucket := chainBucket .NestedReadBucket (
787+ chanPoint ,
788+ )
789+ if chanBucket == nil {
790+ return nil
791+ }
792+
793+ var op wire.OutPoint
794+ readErr := graphdb .ReadOutpoint (
795+ bytes .NewReader (chanPoint ), & op ,
796+ )
797+ if readErr != nil {
798+ return readErr
799+ }
800+
801+ // We need to go through each channel and look
802+ // at the IsPending status.
803+ openChan , err := fetchOpenChannel (
804+ chanBucket , & op ,
805+ )
806+ if err != nil {
807+ return err
808+ }
809+
810+ if openChan .IsPending {
811+ // Add to the pending-open count since
812+ // this is a temp peer.
813+ pendingOpenCount ++
814+ return nil
815+ }
816+
817+ // Since IsPending is false, this is a perm
818+ // peer.
819+ isPermPeer = true
820+
821+ return nil
822+ })
823+ if internalErr != nil {
824+ return internalErr
825+ }
826+
827+ peerCount := ChanCount {
828+ HasOpenOrClosedChan : isPermPeer ,
829+ PendingOpenCount : pendingOpenCount ,
830+ }
831+ peerCounts [string (nodePub )] = peerCount
832+
833+ return nil
834+ })
835+ if openChanErr != nil {
836+ return openChanErr
837+ }
838+
839+ // Now check the closed channel bucket.
840+ historicalChanBucket := tx .ReadBucket (historicalChannelBucket )
841+ if historicalChanBucket == nil {
842+ return ErrNoHistoricalBucket
843+ }
844+
845+ historicalErr := historicalChanBucket .ForEach (func (chanPoint ,
846+ v []byte ) error {
847+ // Parse each nested bucket and the chanInfoKey to get
848+ // the IsPending bool. This determines whether the
849+ // peer is protected or not.
850+ if v != nil {
851+ // This is not a bucket. This is currently not
852+ // possible.
853+ return nil
854+ }
855+
856+ chanBucket := historicalChanBucket .NestedReadBucket (
857+ chanPoint ,
858+ )
859+ if chanBucket == nil {
860+ // This is not possible.
861+ return fmt .Errorf ("no historical channel " +
862+ "bucket exists" )
863+ }
864+
865+ var op wire.OutPoint
866+ readErr := graphdb .ReadOutpoint (
867+ bytes .NewReader (chanPoint ), & op ,
868+ )
869+ if readErr != nil {
870+ return readErr
871+ }
872+
873+ // This channel is closed, but the structure of the
874+ // historical bucket is the same. This is by design,
875+ // which means we can call fetchOpenChannel.
876+ channel , fetchErr := fetchOpenChannel (chanBucket , & op )
877+ if fetchErr != nil {
878+ return fetchErr
879+ }
880+
881+ // Only include this peer in the protected class if
882+ // the closing transaction confirmed. Note that
883+ // CloseChannel can be called in the funding manager
884+ // while IsPending is true which is why we need this
885+ // special-casing to not count premature funding
886+ // manager calls to CloseChannel.
887+ if ! channel .IsPending {
888+ // Fetch the public key of the remote node. We
889+ // need to use the string-ified serialized,
890+ // compressed bytes as the key.
891+ remotePub := channel .IdentityPub
892+ remoteSer := remotePub .SerializeCompressed ()
893+ remoteKey := string (remoteSer )
894+
895+ count , exists := peerCounts [remoteKey ]
896+ if exists {
897+ count .HasOpenOrClosedChan = true
898+ peerCounts [remoteKey ] = count
899+ } else {
900+ peerCount := ChanCount {
901+ HasOpenOrClosedChan : true ,
902+ }
903+ peerCounts [remoteKey ] = peerCount
904+ }
905+ }
906+
907+ return nil
908+ })
909+ if historicalErr != nil {
910+ return historicalErr
911+ }
912+
913+ return nil
914+ }, func () {
915+ clear (peerCounts )
916+ })
917+
918+ return peerCounts , err
919+ }
920+
733921// channelSelector describes a function that takes a chain-hash bucket from
734922// within the open-channel DB and returns the wanted channel point bytes, and
735923// channel point. It must return the ErrChannelNotFound error if the wanted
0 commit comments