diff --git a/firewall/privacy_mapper.go b/firewall/privacy_mapper.go index 1df2053c..41db864f 100644 --- a/firewall/privacy_mapper.go +++ b/firewall/privacy_mapper.go @@ -305,6 +305,13 @@ func (p *PrivacyMapper) checkers(db firewalldb.PrivacyMapDB, handleBatchOpenChannelResponse(db, flags), mid.PassThroughErrorHandler, ), + "/lnrpc.Lightning/OpenChannelSync": mid.NewFullRewriter( + &lnrpc.OpenChannelRequest{}, + &lnrpc.ChannelPoint{}, + handleChannelOpenRequest(db, flags), + handleChannelOpenResponse(db, flags), + mid.PassThroughErrorHandler, + ), } } @@ -1454,6 +1461,157 @@ func handleBatchOpenChannelResponse(db firewalldb.PrivacyMapDB, } } +func handleChannelOpenRequest(db firewalldb.PrivacyMapDB, + flags session.PrivacyFlags) func(ctx context.Context, + r *lnrpc.OpenChannelRequest) (proto.Message, error) { + + return func(_ context.Context, r *lnrpc.OpenChannelRequest) ( + proto.Message, error) { + + var nodePubkey []byte + + err := db.View(func(tx firewalldb.PrivacyMapTx) error { + var err error + + // We use the byte slice representation of the + // pubkey and fall back to the hex string if present. + nodePubkey = r.NodePubkey + if len(nodePubkey) == 0 && r.NodePubkeyString != "" { + nodePubkey, err = hex.DecodeString( + r.NodePubkeyString, + ) + if err != nil { + return err + } + } + + if !flags.Contains(session.ClearPubkeys) { + nodePubkey, err = firewalldb.RevealBytes( + tx, nodePubkey, + ) + if err != nil { + return err + } + } + + return nil + }) + if err != nil { + return nil, err + } + + return &lnrpc.OpenChannelRequest{ + // Obfuscated fields. + NodePubkey: nodePubkey, + + // Omitted fields. + // NodePubkeyString + + // Non-obfuscated fields. + SatPerVbyte: r.SatPerVbyte, + LocalFundingAmount: r.LocalFundingAmount, + PushSat: r.PushSat, + TargetConf: r.TargetConf, + SatPerByte: r.SatPerByte, + Private: r.Private, + MinHtlcMsat: r.MinHtlcMsat, + RemoteCsvDelay: r.RemoteCsvDelay, + MinConfs: r.MinConfs, + SpendUnconfirmed: r.SpendUnconfirmed, + CloseAddress: r.CloseAddress, + FundingShim: r.FundingShim, + RemoteMaxValueInFlightMsat: r.RemoteMaxValueInFlightMsat, + RemoteMaxHtlcs: r.RemoteMaxHtlcs, + MaxLocalCsv: r.MaxLocalCsv, + CommitmentType: r.CommitmentType, + ZeroConf: r.ZeroConf, + ScidAlias: r.ScidAlias, + BaseFee: r.BaseFee, + FeeRate: r.FeeRate, + UseBaseFee: r.UseBaseFee, + UseFeeRate: r.UseFeeRate, + RemoteChanReserveSat: r.RemoteChanReserveSat, + FundMax: r.FundMax, + Memo: r.Memo, + Outpoints: r.Outpoints, + }, nil + } +} + +func handleChannelOpenResponse(db firewalldb.PrivacyMapDB, + flags session.PrivacyFlags) func(ctx context.Context, + r *lnrpc.ChannelPoint) (proto.Message, error) { + + return func(_ context.Context, r *lnrpc.ChannelPoint) ( + proto.Message, error) { + + var ( + txid string + index uint32 + ) + + err := db.Update(func(tx firewalldb.PrivacyMapTx) error { + var err error + + txid = r.GetFundingTxidStr() + if len(r.GetFundingTxidBytes()) != 0 { + hash, err := chainhash.NewHash( + r.GetFundingTxidBytes(), + ) + if err != nil { + return err + } + + txid = hash.String() + } + + index = r.OutputIndex + + if !flags.Contains(session.ClearChanIDs) { + txid, index, err = firewalldb.HideChanPoint( + tx, txid, index, + ) + if err != nil { + return err + } + } + + return nil + }) + + if err != nil { + return nil, err + } + + switch { + case len(r.GetFundingTxidBytes()) != 0: + hash, err := chainhash.NewHashFromStr(txid) + if err != nil { + return nil, err + } + + return &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: hash[:], + }, + OutputIndex: index, + }, nil + + case r.GetFundingTxidStr() != "": + return &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ + FundingTxidStr: txid, + }, + OutputIndex: index, + }, nil + + default: + return nil, fmt.Errorf("channel point has no funding " + + "txid") + } + } +} + // maybeHideAmount hides an amount if the privacy flag is not set. func maybeHideAmount(flags session.PrivacyFlags, randIntn func(int) (int, error), a int64) (int64, error) { diff --git a/firewall/privacy_mapper_test.go b/firewall/privacy_mapper_test.go index bed9ce84..2a0d0a36 100644 --- a/firewall/privacy_mapper_test.go +++ b/firewall/privacy_mapper_test.go @@ -728,6 +728,105 @@ func TestPrivacyMapper(t *testing.T) { }, }, }, + { + name: "OpenChannelSync Request", + uri: "/lnrpc.Lightning/OpenChannelSync", + msgType: rpcperms.TypeRequest, + msg: &lnrpc.OpenChannelRequest{ + NodePubkey: []byte{ + 200, 19, 68, 149, + }, + LocalFundingAmount: 1_000_000, + PushSat: 1_000_000, + MinHtlcMsat: 100, + }, + expectedReplacement: &lnrpc.OpenChannelRequest{ + NodePubkey: []byte{ + 1, 2, 3, 4, + }, + LocalFundingAmount: 1_000_000, + PushSat: 1_000_000, + MinHtlcMsat: 100, + }, + }, + { + name: "OpenChannelSync Request clear", + uri: "/lnrpc.Lightning/OpenChannelSync", + msgType: rpcperms.TypeRequest, + privacyFlags: []session.PrivacyFlag{ + session.ClearPubkeys, + }, + msg: &lnrpc.OpenChannelRequest{ + NodePubkey: []byte{ + 200, 19, 68, 149, + }, + LocalFundingAmount: 1_000_000, + PushSat: 1_000_000, + MinHtlcMsat: 100, + }, + expectedReplacement: &lnrpc.OpenChannelRequest{ + NodePubkey: []byte{ + 200, 19, 68, 149, + }, + LocalFundingAmount: 1_000_000, + PushSat: 1_000_000, + MinHtlcMsat: 100, + }, + }, + { + name: "OpenChannelSync Response bytes", + uri: "/lnrpc.Lightning/OpenChannelSync", + msgType: rpcperms.TypeResponse, + msg: &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ + FundingTxidStr: clearTxID, + }, + OutputIndex: 0, + }, + expectedReplacement: &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ + FundingTxidStr: obfusTxID0, + }, + OutputIndex: obfusOut0, + }, + }, + { + name: "OpenChannelSync Response string", + uri: "/lnrpc.Lightning/OpenChannelSync", + msgType: rpcperms.TypeResponse, + msg: &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: clearTxIDReveresed[:], + }, + OutputIndex: 0, + }, + expectedReplacement: &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: obfusTxID0Reversed[:], + }, + OutputIndex: obfusOut0, + }, + }, + { + name: "OpenChannelSync Response clear", + uri: "/lnrpc.Lightning/OpenChannelSync", + msgType: rpcperms.TypeResponse, + privacyFlags: []session.PrivacyFlag{ + session.ClearChanIDs, + }, + msg: &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: clearTxIDReveresed[:], + }, + OutputIndex: 0, + }, + expectedReplacement: &lnrpc.ChannelPoint{ + FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{ + FundingTxidBytes: clearTxIDReveresed[:], + }, + OutputIndex: 0, + }, + }, } decodedID := &lnrpc.MacaroonId{