Merge pull request #3796 from carlaKC/3786-setupfrontshutdown

Add Upfront Shutdown Address to OpenChannelRequest
This commit is contained in:
Wilmer Paulino 2019-12-18 11:28:29 -08:00 committed by GitHub
commit 7f3771e74f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 783 additions and 599 deletions

View file

@ -590,6 +590,12 @@ var openChannelCommand = cli.Command{
amount to the remote node as part of the channel opening. Once the channel is open, amount to the remote node as part of the channel opening. Once the channel is open,
a channelPoint (txid:vout) of the funding output is returned. a channelPoint (txid:vout) of the funding output is returned.
If the remote peer supports the option upfront shutdown feature bit (query
listpeers to see their supported feature bits), an address to enforce
payout of funds on cooperative close can optionally be provided. Note that
if you set this value, you will not be able to cooperatively close out to
another address.
One can manually set the fee to be used for the funding transaction via either One can manually set the fee to be used for the funding transaction via either
the --conf_target or --sat_per_byte arguments. This is optional.`, the --conf_target or --sat_per_byte arguments. This is optional.`,
ArgsUsage: "node-key local-amt push-amt", ArgsUsage: "node-key local-amt push-amt",
@ -659,6 +665,13 @@ var openChannelCommand = cli.Command{
"transaction must satisfy", "transaction must satisfy",
Value: 1, Value: 1,
}, },
cli.StringFlag{
Name: "close_address",
Usage: "(optional) an address to enforce payout of our " +
"funds to on cooperative close. Note that if this " +
"value is set on channel open, you will *not* be " +
"able to cooperatively close to a different address.",
},
}, },
Action: actionDecorator(openChannel), Action: actionDecorator(openChannel),
} }
@ -686,6 +699,7 @@ func openChannel(ctx *cli.Context) error {
RemoteCsvDelay: uint32(ctx.Uint64("remote_csv_delay")), RemoteCsvDelay: uint32(ctx.Uint64("remote_csv_delay")),
MinConfs: minConfs, MinConfs: minConfs,
SpendUnconfirmed: minConfs == 0, SpendUnconfirmed: minConfs == 0,
CloseAddress: ctx.String("close_address"),
} }
switch { switch {
@ -1935,22 +1949,23 @@ func getInfo(ctx *cli.Context) error {
// We print a struct that mimics the proto definition of GetInfoResponse // We print a struct that mimics the proto definition of GetInfoResponse
// but has a better ordering for the same list of fields. // but has a better ordering for the same list of fields.
printJSON(struct { printJSON(struct {
Version string `json:"version"` Version string `json:"version"`
IdentityPubkey string `json:"identity_pubkey"` IdentityPubkey string `json:"identity_pubkey"`
Alias string `json:"alias"` Alias string `json:"alias"`
Color string `json:"color"` Color string `json:"color"`
NumPendingChannels uint32 `json:"num_pending_channels"` NumPendingChannels uint32 `json:"num_pending_channels"`
NumActiveChannels uint32 `json:"num_active_channels"` NumActiveChannels uint32 `json:"num_active_channels"`
NumInactiveChannels uint32 `json:"num_inactive_channels"` NumInactiveChannels uint32 `json:"num_inactive_channels"`
NumPeers uint32 `json:"num_peers"` NumPeers uint32 `json:"num_peers"`
BlockHeight uint32 `json:"block_height"` BlockHeight uint32 `json:"block_height"`
BlockHash string `json:"block_hash"` BlockHash string `json:"block_hash"`
BestHeaderTimestamp int64 `json:"best_header_timestamp"` BestHeaderTimestamp int64 `json:"best_header_timestamp"`
SyncedToChain bool `json:"synced_to_chain"` SyncedToChain bool `json:"synced_to_chain"`
SyncedToGraph bool `json:"synced_to_graph"` SyncedToGraph bool `json:"synced_to_graph"`
Testnet bool `json:"testnet"` Testnet bool `json:"testnet"`
Chains []chain `json:"chains"` Chains []chain `json:"chains"`
Uris []string `json:"uris"` Uris []string `json:"uris"`
Features map[uint32]*lnrpc.Feature `json:"features"`
}{ }{
Version: resp.Version, Version: resp.Version,
IdentityPubkey: resp.IdentityPubkey, IdentityPubkey: resp.IdentityPubkey,
@ -1968,6 +1983,7 @@ func getInfo(ctx *cli.Context) error {
Testnet: resp.Testnet, Testnet: resp.Testnet,
Chains: chains, Chains: chains,
Uris: resp.Uris, Uris: resp.Uris,
Features: resp.Features,
}) })
return nil return nil
} }

View file

@ -108,3 +108,14 @@ func (m *Manager) Get(set Set) *lnwire.FeatureVector {
raw := m.GetRaw(set) raw := m.GetRaw(set)
return lnwire.NewFeatureVector(raw, lnwire.Features) return lnwire.NewFeatureVector(raw, lnwire.Features)
} }
// ListSets returns a list of the feature sets that our node supports.
func (m *Manager) ListSets() []Set {
var sets []Set
for set := range m.fsets {
sets = append(sets, set)
}
return sets
}

File diff suppressed because it is too large Load diff

View file

@ -1344,6 +1344,15 @@ message Channel {
by the channel scoring system over the lifetime of the channel [EXPERIMENTAL]. by the channel scoring system over the lifetime of the channel [EXPERIMENTAL].
*/ */
int64 uptime = 24 [json_name = "uptime"]; int64 uptime = 24 [json_name = "uptime"];
/**
Close address is the address that we will enforce payout to on cooperative
close if the channel was opened utilizing option upfront shutdown. This
value can be set on channel open by setting close_address in an open channel
request. If this value is not set, you can still choose a payout address by
cooperatively closing with the delivery_address field set.
*/
string close_address = 25 [json_name ="close_address"];
} }
@ -1539,6 +1548,12 @@ message GetInfoResponse {
// Whether we consider ourselves synced with the public channel graph. // Whether we consider ourselves synced with the public channel graph.
bool synced_to_graph = 18 [json_name = "synced_to_graph"]; bool synced_to_graph = 18 [json_name = "synced_to_graph"];
/*
Features that our node has advertised in our init message, node
announcements and invoices.
*/
map<uint32, Feature> features = 19 [json_name = "features"];
} }
message Chain { message Chain {
@ -1643,6 +1658,18 @@ message OpenChannelRequest {
/// Whether unconfirmed outputs should be used as inputs for the funding transaction. /// Whether unconfirmed outputs should be used as inputs for the funding transaction.
bool spend_unconfirmed = 12 [json_name = "spend_unconfirmed"]; bool spend_unconfirmed = 12 [json_name = "spend_unconfirmed"];
/*
Close address is an optional address which specifies the address to which
funds should be paid out to upon cooperative close. This field may only be
set if the peer supports the option upfront feature bit (call listpeers
to check). The remote peer will only accept cooperative closes to this
address if it is set.
Note: If this value is set on channel creation, you will *not* be able to
cooperatively close out to a different address.
*/
string close_address = 13[json_name = "close_address"];
} }
message OpenStatusUpdate { message OpenStatusUpdate {
oneof update { oneof update {

View file

@ -1819,6 +1819,10 @@
"type": "string", "type": "string",
"format": "int64", "format": "int64",
"description": "*\nThe number of seconds that the remote peer has been observed as being online\nby the channel scoring system over the lifetime of the channel [EXPERIMENTAL]." "description": "*\nThe number of seconds that the remote peer has been observed as being online\nby the channel scoring system over the lifetime of the channel [EXPERIMENTAL]."
},
"close_address": {
"type": "string",
"description": "*\nClose address is the address that we will enforce payout to on cooperative\nclose if the channel was opened utilizing option upfront shutdown. This\nvalue can be set on channel open by setting close_address in an open channel\nrequest. If this value is not set, you can still choose a payout address by\ncooperatively closing with the delivery_address field set."
} }
} }
}, },
@ -2504,6 +2508,13 @@
"type": "boolean", "type": "boolean",
"format": "boolean", "format": "boolean",
"description": "Whether we consider ourselves synced with the public channel graph." "description": "Whether we consider ourselves synced with the public channel graph."
},
"features": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/lnrpcFeature"
},
"description": "Features that our node has advertised in our init message, node\nannouncements and invoices."
} }
} }
}, },
@ -3233,6 +3244,10 @@
"type": "boolean", "type": "boolean",
"format": "boolean", "format": "boolean",
"description": "/ Whether unconfirmed outputs should be used as inputs for the funding transaction." "description": "/ Whether unconfirmed outputs should be used as inputs for the funding transaction."
},
"close_address": {
"type": "string",
"description": "Close address is an optional address which specifies the address to which\nfunds should be paid out to upon cooperative close. This field may only be\nset if the peer supports the option upfront feature bit (call listpeers\nto check). The remote peer will only accept cooperative closes to this\naddress if it is set.\n\nNote: If this value is set on channel creation, you will *not* be able to\ncooperatively close out to a different address."
} }
} }
}, },

View file

@ -1542,6 +1542,11 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
rpcsLog.Debugf("[openchannel]: using fee of %v sat/kw for funding tx", rpcsLog.Debugf("[openchannel]: using fee of %v sat/kw for funding tx",
int64(feeRate)) int64(feeRate))
script, err := parseUpfrontShutdownAddress(in.CloseAddress)
if err != nil {
return fmt.Errorf("error parsing upfront shutdown: %v", err)
}
// Instruct the server to trigger the necessary events to attempt to // Instruct the server to trigger the necessary events to attempt to
// open a new channel. A stream is returned in place, this stream will // open a new channel. A stream is returned in place, this stream will
// be used to consume updates of the state of the pending channel. // be used to consume updates of the state of the pending channel.
@ -1555,6 +1560,7 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
private: in.Private, private: in.Private,
remoteCsvDelay: remoteCsvDelay, remoteCsvDelay: remoteCsvDelay,
minConfs: minConfs, minConfs: minConfs,
shutdownScript: script,
} }
updateChan, errChan := r.server.OpenChannel(req) updateChan, errChan := r.server.OpenChannel(req)
@ -1687,6 +1693,11 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
rpcsLog.Tracef("[openchannel] target sat/kw for funding tx: %v", rpcsLog.Tracef("[openchannel] target sat/kw for funding tx: %v",
int64(feeRate)) int64(feeRate))
script, err := parseUpfrontShutdownAddress(in.CloseAddress)
if err != nil {
return nil, fmt.Errorf("error parsing upfront shutdown: %v", err)
}
req := &openChanReq{ req := &openChanReq{
targetPubkey: nodepubKey, targetPubkey: nodepubKey,
chainHash: *activeNetParams.GenesisHash, chainHash: *activeNetParams.GenesisHash,
@ -1697,6 +1708,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
private: in.Private, private: in.Private,
remoteCsvDelay: remoteCsvDelay, remoteCsvDelay: remoteCsvDelay,
minConfs: minConfs, minConfs: minConfs,
shutdownScript: script,
} }
updateChan, errChan := r.server.OpenChannel(req) updateChan, errChan := r.server.OpenChannel(req)
@ -1730,6 +1742,24 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
} }
} }
// parseUpfrontShutdownScript attempts to parse an upfront shutdown address.
// If the address is empty, it returns nil. If it successfully decoded the
// address, it returns a script that pays out to the address.
func parseUpfrontShutdownAddress(address string) (lnwire.DeliveryAddress, error) {
if len(address) == 0 {
return nil, nil
}
addr, err := btcutil.DecodeAddress(
address, activeNetParams.Params,
)
if err != nil {
return nil, fmt.Errorf("invalid address: %v", err)
}
return txscript.PayToAddrScript(addr)
}
// GetChanPointFundingTxid returns the given channel point's funding txid in // GetChanPointFundingTxid returns the given channel point's funding txid in
// raw bytes. // raw bytes.
func GetChanPointFundingTxid(chanPoint *lnrpc.ChannelPoint) (*chainhash.Hash, error) { func GetChanPointFundingTxid(chanPoint *lnrpc.ChannelPoint) (*chainhash.Hash, error) {
@ -2190,6 +2220,22 @@ func (r *rpcServer) GetInfo(ctx context.Context,
isGraphSynced := r.server.authGossiper.SyncManager().IsGraphSynced() isGraphSynced := r.server.authGossiper.SyncManager().IsGraphSynced()
features := make(map[uint32]*lnrpc.Feature)
sets := r.server.featureMgr.ListSets()
for _, set := range sets {
// Get the a list of lnrpc features for each set we support.
featureVector := r.server.featureMgr.Get(set)
rpcFeatures := invoicesrpc.CreateRPCFeatures(featureVector)
// Add the features to our map of features, allowing over writing of
// existing values because features in different sets with the same bit
// are duplicated across sets.
for bit, feature := range rpcFeatures {
features[bit] = feature
}
}
// TODO(roasbeef): add synced height n stuff // TODO(roasbeef): add synced height n stuff
return &lnrpc.GetInfoResponse{ return &lnrpc.GetInfoResponse{
IdentityPubkey: encodedIDPub, IdentityPubkey: encodedIDPub,
@ -2208,6 +2254,7 @@ func (r *rpcServer) GetInfo(ctx context.Context,
BestHeaderTimestamp: int64(bestHeaderTimestamp), BestHeaderTimestamp: int64(bestHeaderTimestamp),
Version: build.Version(), Version: build.Version(),
SyncedToGraph: isGraphSynced, SyncedToGraph: isGraphSynced,
Features: features,
}, nil }, nil
} }
@ -2942,6 +2989,25 @@ func createRPCOpenChannel(r *rpcServer, graph *channeldb.ChannelGraph,
} }
channel.Uptime = int64(uptime.Seconds()) channel.Uptime = int64(uptime.Seconds())
if len(dbChannel.LocalShutdownScript) > 0 {
_, addresses, _, err := txscript.ExtractPkScriptAddrs(
dbChannel.LocalShutdownScript, activeNetParams.Params,
)
if err != nil {
return nil, err
}
// We only expect one upfront shutdown address for a channel. If
// LocalShutdownScript is non-zero, there should be one payout address
// set.
if len(addresses) != 1 {
return nil, fmt.Errorf("expected one upfront shutdown address, "+
"got: %v", len(addresses))
}
channel.CloseAddress = addresses[0].String()
}
return channel, nil return channel, nil
} }
@ -3020,6 +3086,7 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
OpenChannel: channel, OpenChannel: channel,
}, },
} }
case channelnotifier.ClosedChannelEvent: case channelnotifier.ClosedChannelEvent:
closedChannel := createRPCClosedChannel(event.CloseSummary) closedChannel := createRPCClosedChannel(event.CloseSummary)
update = &lnrpc.ChannelEventUpdate{ update = &lnrpc.ChannelEventUpdate{
@ -3028,6 +3095,7 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
ClosedChannel: closedChannel, ClosedChannel: closedChannel,
}, },
} }
case channelnotifier.ActiveChannelEvent: case channelnotifier.ActiveChannelEvent:
update = &lnrpc.ChannelEventUpdate{ update = &lnrpc.ChannelEventUpdate{
Type: lnrpc.ChannelEventUpdate_ACTIVE_CHANNEL, Type: lnrpc.ChannelEventUpdate_ACTIVE_CHANNEL,
@ -3040,6 +3108,7 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
}, },
}, },
} }
case channelnotifier.InactiveChannelEvent: case channelnotifier.InactiveChannelEvent:
update = &lnrpc.ChannelEventUpdate{ update = &lnrpc.ChannelEventUpdate{
Type: lnrpc.ChannelEventUpdate_INACTIVE_CHANNEL, Type: lnrpc.ChannelEventUpdate_INACTIVE_CHANNEL,
@ -3052,6 +3121,7 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription,
}, },
}, },
} }
default: default:
return fmt.Errorf("unexpected channel event update: %v", event) return fmt.Errorf("unexpected channel event update: %v", event)
} }