mirror of
https://github.com/bumi/lnrpc
synced 2026-02-22 02:07:49 +00:00
Compare commits
4 Commits
v0.6.1-bet
...
v0.7.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b0a8a7df2 | |||
| 809f80a8ed | |||
| 83d71fae31 | |||
| 26fc81e177 |
20
Gemfile.lock
20
Gemfile.lock
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
lnrpc (0.5.2)
|
||||
lnrpc (0.7.0)
|
||||
google-protobuf (>= 3.7)
|
||||
grpc (>= 1.19.0)
|
||||
|
||||
@@ -9,26 +9,26 @@ GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
diff-lcs (1.3)
|
||||
google-protobuf (3.7.0-x86_64-linux)
|
||||
googleapis-common-protos-types (1.0.3)
|
||||
google-protobuf (3.8.0)
|
||||
googleapis-common-protos-types (1.0.4)
|
||||
google-protobuf (~> 3.0)
|
||||
grpc (1.19.0-x86_64-linux)
|
||||
google-protobuf (~> 3.1)
|
||||
googleapis-common-protos-types (~> 1.0.0)
|
||||
grpc (1.21.0)
|
||||
google-protobuf (~> 3.7)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
rake (10.5.0)
|
||||
rspec (3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-core (3.8.0)
|
||||
rspec-core (3.8.2)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-expectations (3.8.2)
|
||||
rspec-expectations (3.8.4)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-mocks (3.8.0)
|
||||
rspec-mocks (3.8.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-support (3.8.0)
|
||||
rspec-support (3.8.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Lnrpc - ruby gRPC client for LND
|
||||
[](https://badge.fury.io/rb/lnrpc)
|
||||
|
||||
a [gRPC](https://grpc.io/) client for [LND, the Lightning Network Daemon](https://github.com/lightningnetwork/lnd/) packed as ruby gem.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```ruby
|
||||
gem 'lnrpc', '~> 0.6.1'
|
||||
gem 'lnrpc', '~> 0.7.0'
|
||||
```
|
||||
lnrpc follows the lnd versioning, thus it is recommended to specify the exact version you need for your lnd node as dependency (see [#Versioning](#Versioning)).
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ message UnlockWalletRequest {
|
||||
/**
|
||||
recovery_window is an optional argument specifying the address lookahead
|
||||
when restoring a wallet seed. The recovery window applies to each
|
||||
invdividual branch of the BIP44 derivation paths. Supplying a recovery
|
||||
individual branch of the BIP44 derivation paths. Supplying a recovery
|
||||
window of zero indicates that no addresses should be recovered, such after
|
||||
the first initialization of the wallet.
|
||||
*/
|
||||
@@ -212,7 +212,7 @@ service Lightning {
|
||||
/** lncli: `walletbalance`
|
||||
WalletBalance returns total unspent outputs(confirmed and unconfirmed), all
|
||||
confirmed unspent outputs and all unconfirmed unspent outputs under control
|
||||
of the wallet.
|
||||
of the wallet.
|
||||
*/
|
||||
rpc WalletBalance (WalletBalanceRequest) returns (WalletBalanceResponse) {
|
||||
option (google.api.http) = {
|
||||
@@ -398,7 +398,7 @@ service Lightning {
|
||||
rpc SubscribeChannelEvents (ChannelEventSubscription) returns (stream ChannelEventUpdate);
|
||||
|
||||
/** lncli: `closedchannels`
|
||||
ClosedChannels returns a description of all the closed channels that
|
||||
ClosedChannels returns a description of all the closed channels that
|
||||
this node was a participant in.
|
||||
*/
|
||||
rpc ClosedChannels (ClosedChannelsRequest) returns (ClosedChannelsResponse) {
|
||||
@@ -621,7 +621,7 @@ service Lightning {
|
||||
/** lncli: `queryroutes`
|
||||
QueryRoutes attempts to query the daemon's Channel Router for a possible
|
||||
route to a target destination capable of carrying a specific amount of
|
||||
satoshis. The retuned route contains the full details required to craft and
|
||||
satoshis. The returned route contains the full details required to craft and
|
||||
send an HTLC, also including the necessary information that should be
|
||||
present within the Sphinx packet encapsulated within the HTLC.
|
||||
*/
|
||||
@@ -814,6 +814,9 @@ message Transaction {
|
||||
|
||||
/// Addresses that received funds for this transaction
|
||||
repeated string dest_addresses = 8 [ json_name = "dest_addresses" ];
|
||||
|
||||
/// The raw transaction hex.
|
||||
string raw_tx_hex = 9 [ json_name = "raw_tx_hex" ];
|
||||
}
|
||||
message GetTransactionsRequest {
|
||||
}
|
||||
@@ -896,13 +899,7 @@ message SendToRouteRequest {
|
||||
/// An optional hex-encoded payment hash to be used for the HTLC.
|
||||
string payment_hash_string = 2;
|
||||
|
||||
/**
|
||||
Deprecated. The set of routes that should be used to attempt to complete the
|
||||
payment. The possibility to pass in multiple routes is deprecated and
|
||||
instead the single route field below should be used in combination with the
|
||||
streaming variant of SendToRoute.
|
||||
*/
|
||||
repeated Route routes = 3 [deprecated = true];
|
||||
reserved 3;
|
||||
|
||||
/// Route that should be used to attempt to complete the payment.
|
||||
Route route = 4;
|
||||
@@ -1162,8 +1159,16 @@ message Channel {
|
||||
/// True if we were the ones that created the channel.
|
||||
bool initiator = 18 [json_name = "initiator"];
|
||||
|
||||
/// A set of flags showing the current state of the cahnnel.
|
||||
/// A set of flags showing the current state of the channel.
|
||||
string chan_status_flags = 19 [json_name = "chan_status_flags"];
|
||||
|
||||
/// The minimum satoshis this node is required to reserve in its balance.
|
||||
int64 local_chan_reserve_sat = 20 [json_name = "local_chan_reserve_sat"];
|
||||
|
||||
/**
|
||||
The minimum satoshis the other node is required to reserve in its balance.
|
||||
*/
|
||||
int64 remote_chan_reserve_sat = 21 [json_name = "remote_chan_reserve_sat"];
|
||||
}
|
||||
|
||||
|
||||
@@ -1335,6 +1340,9 @@ message GetInfoResponse {
|
||||
|
||||
/// A list of active chains the node is connected to
|
||||
repeated Chain chains = 16 [json_name = "chains"];
|
||||
|
||||
/// The color of the current node in hex code format
|
||||
string color = 17 [json_name = "color"];
|
||||
}
|
||||
|
||||
message Chain {
|
||||
@@ -1468,6 +1476,15 @@ message PendingChannelsResponse {
|
||||
|
||||
int64 local_balance = 4 [ json_name = "local_balance" ];
|
||||
int64 remote_balance = 5 [ json_name = "remote_balance" ];
|
||||
|
||||
/// The minimum satoshis this node is required to reserve in its balance.
|
||||
int64 local_chan_reserve_sat = 6 [json_name = "local_chan_reserve_sat"];
|
||||
|
||||
/**
|
||||
The minimum satoshis the other node is required to reserve in its
|
||||
balance.
|
||||
*/
|
||||
int64 remote_chan_reserve_sat = 7 [json_name = "remote_chan_reserve_sat"];
|
||||
}
|
||||
|
||||
message PendingOpenChannel {
|
||||
@@ -1523,7 +1540,7 @@ message PendingChannelsResponse {
|
||||
/// The balance in satoshis encumbered in this pending channel
|
||||
int64 limbo_balance = 3 [ json_name = "limbo_balance" ];
|
||||
|
||||
/// The height at which funds can be sweeped into the wallet
|
||||
/// The height at which funds can be swept into the wallet
|
||||
uint32 maturity_height = 4 [ json_name = "maturity_height" ];
|
||||
|
||||
/*
|
||||
@@ -1606,11 +1623,7 @@ message QueryRoutesRequest {
|
||||
/// The amount to send expressed in satoshis
|
||||
int64 amt = 2;
|
||||
|
||||
/**
|
||||
Deprecated. The max number of routes to return. In the future, QueryRoutes
|
||||
will only return a single route.
|
||||
*/
|
||||
int32 num_routes = 3 [deprecated = true];
|
||||
reserved 3;
|
||||
|
||||
/// An optional CLTV delta from the current height that should be used for the timelock of the final hop
|
||||
int32 final_cltv_delta = 4;
|
||||
@@ -1638,6 +1651,12 @@ message QueryRoutesRequest {
|
||||
self is assumed.
|
||||
*/
|
||||
string source_pub_key = 8;
|
||||
|
||||
/**
|
||||
If set to true, edge probabilities from mission control will be used to get
|
||||
the optimal route.
|
||||
*/
|
||||
bool use_mission_control = 9;
|
||||
}
|
||||
|
||||
message EdgeLocator {
|
||||
@@ -1698,7 +1717,7 @@ message Route {
|
||||
/**
|
||||
The sum of the fees paid at each hop within the final route. In the case
|
||||
of a one-hop payment, this value will be zero as we don't need to pay a fee
|
||||
it ourself.
|
||||
to ourselves.
|
||||
*/
|
||||
int64 total_fees = 2 [json_name = "total_fees", deprecated = true];
|
||||
|
||||
@@ -1730,6 +1749,9 @@ message Route {
|
||||
message NodeInfoRequest {
|
||||
/// The 33-byte hex-encoded compressed public of the target node
|
||||
string pub_key = 1;
|
||||
|
||||
/// If true, will include all known channels associated with the node.
|
||||
bool include_channels = 2;
|
||||
}
|
||||
|
||||
message NodeInfo {
|
||||
@@ -1742,8 +1764,14 @@ message NodeInfo {
|
||||
*/
|
||||
LightningNode node = 1 [json_name = "node"];
|
||||
|
||||
/// The total number of channels for the node.
|
||||
uint32 num_channels = 2 [json_name = "num_channels"];
|
||||
|
||||
/// The sum of all channels capacity for the node, denominated in satoshis.
|
||||
int64 total_capacity = 3 [json_name = "total_capacity"];
|
||||
|
||||
/// A list of all public channels for the node.
|
||||
repeated ChannelEdge channels = 4 [json_name = "channels"];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1772,6 +1800,7 @@ message RoutingPolicy {
|
||||
int64 fee_rate_milli_msat = 4 [json_name = "fee_rate_milli_msat"];
|
||||
bool disabled = 5 [json_name = "disabled"];
|
||||
uint64 max_htlc_msat = 6 [json_name = "max_htlc_msat"];
|
||||
uint32 last_update = 7 [json_name = "last_update"];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1791,7 +1820,7 @@ message ChannelEdge {
|
||||
uint64 channel_id = 1 [json_name = "channel_id"];
|
||||
string chan_point = 2 [json_name = "chan_point"];
|
||||
|
||||
uint32 last_update = 3 [json_name = "last_update"];
|
||||
uint32 last_update = 3 [json_name = "last_update", deprecated = true];
|
||||
|
||||
string node1_pub = 4 [json_name = "node1_pub"];
|
||||
string node2_pub = 5 [json_name = "node2_pub"];
|
||||
@@ -1846,6 +1875,9 @@ message NetworkInfo {
|
||||
int64 max_channel_size = 9 [json_name = "max_channel_size"];
|
||||
int64 median_channel_size_sat = 10 [json_name = "median_channel_size_sat"];
|
||||
|
||||
// The number of edges marked as zombies.
|
||||
uint64 num_zombie_chans = 11 [json_name = "num_zombie_chans"];
|
||||
|
||||
// TODO(roasbeef): fee rate info, expiry
|
||||
// * also additional RPC for tracking fee info once in
|
||||
}
|
||||
@@ -1864,6 +1896,7 @@ message NodeUpdate {
|
||||
string identity_key = 2;
|
||||
bytes global_features = 3;
|
||||
string alias = 4;
|
||||
string color = 5;
|
||||
}
|
||||
message ChannelEdgeUpdate {
|
||||
/**
|
||||
@@ -2141,8 +2174,8 @@ message Payment {
|
||||
/// The path this payment took
|
||||
repeated string path = 4 [ json_name = "path" ];
|
||||
|
||||
/// The fee paid for this payment in satoshis
|
||||
int64 fee = 5 [json_name = "fee"];
|
||||
/// Deprecated, use fee_sat or fee_msat.
|
||||
int64 fee = 5 [json_name = "fee", deprecated = true];
|
||||
|
||||
/// The payment preimage
|
||||
string payment_preimage = 6 [json_name = "payment_preimage"];
|
||||
@@ -2152,9 +2185,34 @@ message Payment {
|
||||
|
||||
/// The value of the payment in milli-satoshis
|
||||
int64 value_msat = 8 [json_name = "value_msat"];
|
||||
|
||||
/// The optional payment request being fulfilled.
|
||||
string payment_request = 9 [json_name = "payment_request"];
|
||||
|
||||
enum PaymentStatus {
|
||||
UNKNOWN = 0;
|
||||
IN_FLIGHT = 1;
|
||||
SUCCEEDED = 2;
|
||||
FAILED = 3;
|
||||
}
|
||||
|
||||
// The status of the payment.
|
||||
PaymentStatus status = 10 [json_name = "status"];
|
||||
|
||||
/// The fee paid for this payment in satoshis
|
||||
int64 fee_sat = 11 [json_name = "fee_sat"];
|
||||
|
||||
/// The fee paid for this payment in milli-satoshis
|
||||
int64 fee_msat = 12 [json_name = "fee_msat"];
|
||||
}
|
||||
|
||||
message ListPaymentsRequest {
|
||||
/**
|
||||
If true, then return payments that have not yet fully completed. This means
|
||||
that pending payments, as well as failed payments will show up if this
|
||||
field is set to True.
|
||||
*/
|
||||
bool include_incomplete = 1;
|
||||
}
|
||||
|
||||
message ListPaymentsResponse {
|
||||
@@ -2298,7 +2356,7 @@ message ForwardingHistoryResponse {
|
||||
}
|
||||
|
||||
message ExportChannelBackupRequest {
|
||||
/// The target chanenl point to obtain a back up for.
|
||||
/// The target channel point to obtain a back up for.
|
||||
ChannelPoint chan_point = 1;
|
||||
}
|
||||
|
||||
@@ -2310,7 +2368,7 @@ message ChannelBackup {
|
||||
|
||||
/**
|
||||
Is an encrypted single-chan backup. this can be passed to
|
||||
RestoreChannelBackups, or the WalletUnlocker Innit and Unlock methods in
|
||||
RestoreChannelBackups, or the WalletUnlocker Init and Unlock methods in
|
||||
order to trigger the recovery protocol.
|
||||
*/
|
||||
bytes chan_backup = 2 [ json_name = "chan_backup" ];
|
||||
|
||||
@@ -52,6 +52,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :time_stamp, :int64, 6
|
||||
optional :total_fees, :int64, 7
|
||||
repeated :dest_addresses, :string, 8
|
||||
optional :raw_tx_hex, :string, 9
|
||||
end
|
||||
add_message "lnrpc.GetTransactionsRequest" do
|
||||
end
|
||||
@@ -85,7 +86,6 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
add_message "lnrpc.SendToRouteRequest" do
|
||||
optional :payment_hash, :bytes, 1
|
||||
optional :payment_hash_string, :string, 2
|
||||
repeated :routes, :message, 3, "lnrpc.Route"
|
||||
optional :route, :message, 4, "lnrpc.Route"
|
||||
end
|
||||
add_message "lnrpc.ChannelPoint" do
|
||||
@@ -194,6 +194,8 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :private, :bool, 17
|
||||
optional :initiator, :bool, 18
|
||||
optional :chan_status_flags, :string, 19
|
||||
optional :local_chan_reserve_sat, :int64, 20
|
||||
optional :remote_chan_reserve_sat, :int64, 21
|
||||
end
|
||||
add_message "lnrpc.ListChannelsRequest" do
|
||||
optional :active_only, :bool, 1
|
||||
@@ -273,6 +275,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :version, :string, 14
|
||||
optional :num_inactive_channels, :uint32, 15
|
||||
repeated :chains, :message, 16, "lnrpc.Chain"
|
||||
optional :color, :string, 17
|
||||
end
|
||||
add_message "lnrpc.Chain" do
|
||||
optional :chain, :string, 1
|
||||
@@ -348,6 +351,8 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :capacity, :int64, 3
|
||||
optional :local_balance, :int64, 4
|
||||
optional :remote_balance, :int64, 5
|
||||
optional :local_chan_reserve_sat, :int64, 6
|
||||
optional :remote_chan_reserve_sat, :int64, 7
|
||||
end
|
||||
add_message "lnrpc.PendingChannelsResponse.PendingOpenChannel" do
|
||||
optional :channel, :message, 1, "lnrpc.PendingChannelsResponse.PendingChannel"
|
||||
@@ -406,12 +411,12 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
add_message "lnrpc.QueryRoutesRequest" do
|
||||
optional :pub_key, :string, 1
|
||||
optional :amt, :int64, 2
|
||||
optional :num_routes, :int32, 3
|
||||
optional :final_cltv_delta, :int32, 4
|
||||
optional :fee_limit, :message, 5, "lnrpc.FeeLimit"
|
||||
repeated :ignored_nodes, :bytes, 6
|
||||
repeated :ignored_edges, :message, 7, "lnrpc.EdgeLocator"
|
||||
optional :source_pub_key, :string, 8
|
||||
optional :use_mission_control, :bool, 9
|
||||
end
|
||||
add_message "lnrpc.EdgeLocator" do
|
||||
optional :channel_id, :uint64, 1
|
||||
@@ -440,11 +445,13 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
end
|
||||
add_message "lnrpc.NodeInfoRequest" do
|
||||
optional :pub_key, :string, 1
|
||||
optional :include_channels, :bool, 2
|
||||
end
|
||||
add_message "lnrpc.NodeInfo" do
|
||||
optional :node, :message, 1, "lnrpc.LightningNode"
|
||||
optional :num_channels, :uint32, 2
|
||||
optional :total_capacity, :int64, 3
|
||||
repeated :channels, :message, 4, "lnrpc.ChannelEdge"
|
||||
end
|
||||
add_message "lnrpc.LightningNode" do
|
||||
optional :last_update, :uint32, 1
|
||||
@@ -464,6 +471,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :fee_rate_milli_msat, :int64, 4
|
||||
optional :disabled, :bool, 5
|
||||
optional :max_htlc_msat, :uint64, 6
|
||||
optional :last_update, :uint32, 7
|
||||
end
|
||||
add_message "lnrpc.ChannelEdge" do
|
||||
optional :channel_id, :uint64, 1
|
||||
@@ -498,6 +506,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :min_channel_size, :int64, 8
|
||||
optional :max_channel_size, :int64, 9
|
||||
optional :median_channel_size_sat, :int64, 10
|
||||
optional :num_zombie_chans, :uint64, 11
|
||||
end
|
||||
add_message "lnrpc.StopRequest" do
|
||||
end
|
||||
@@ -515,6 +524,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :identity_key, :string, 2
|
||||
optional :global_features, :bytes, 3
|
||||
optional :alias, :string, 4
|
||||
optional :color, :string, 5
|
||||
end
|
||||
add_message "lnrpc.ChannelEdgeUpdate" do
|
||||
optional :chan_id, :uint64, 1
|
||||
@@ -602,8 +612,19 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
||||
optional :payment_preimage, :string, 6
|
||||
optional :value_sat, :int64, 7
|
||||
optional :value_msat, :int64, 8
|
||||
optional :payment_request, :string, 9
|
||||
optional :status, :enum, 10, "lnrpc.Payment.PaymentStatus"
|
||||
optional :fee_sat, :int64, 11
|
||||
optional :fee_msat, :int64, 12
|
||||
end
|
||||
add_enum "lnrpc.Payment.PaymentStatus" do
|
||||
value :UNKNOWN, 0
|
||||
value :IN_FLIGHT, 1
|
||||
value :SUCCEEDED, 2
|
||||
value :FAILED, 3
|
||||
end
|
||||
add_message "lnrpc.ListPaymentsRequest" do
|
||||
optional :include_incomplete, :bool, 1
|
||||
end
|
||||
add_message "lnrpc.ListPaymentsResponse" do
|
||||
repeated :payments, :message, 1, "lnrpc.Payment"
|
||||
@@ -832,6 +853,7 @@ module Lnrpc
|
||||
ListInvoiceResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("lnrpc.ListInvoiceResponse").msgclass
|
||||
InvoiceSubscription = Google::Protobuf::DescriptorPool.generated_pool.lookup("lnrpc.InvoiceSubscription").msgclass
|
||||
Payment = Google::Protobuf::DescriptorPool.generated_pool.lookup("lnrpc.Payment").msgclass
|
||||
Payment::PaymentStatus = Google::Protobuf::DescriptorPool.generated_pool.lookup("lnrpc.Payment.PaymentStatus").enummodule
|
||||
ListPaymentsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("lnrpc.ListPaymentsRequest").msgclass
|
||||
ListPaymentsResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("lnrpc.ListPaymentsResponse").msgclass
|
||||
DeleteAllPaymentsRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("lnrpc.DeleteAllPaymentsRequest").msgclass
|
||||
|
||||
@@ -287,7 +287,7 @@ module Lnrpc
|
||||
# * lncli: `queryroutes`
|
||||
# QueryRoutes attempts to query the daemon's Channel Router for a possible
|
||||
# route to a target destination capable of carrying a specific amount of
|
||||
# satoshis. The retuned route contains the full details required to craft and
|
||||
# satoshis. The returned route contains the full details required to craft and
|
||||
# send an HTLC, also including the necessary information that should be
|
||||
# present within the Sphinx packet encapsulated within the HTLC.
|
||||
rpc :QueryRoutes, QueryRoutesRequest, QueryRoutesResponse
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module Lnrpc
|
||||
VERSION = "0.6.1"
|
||||
VERSION = "0.7.1"
|
||||
end
|
||||
|
||||
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
||||
spec.add_development_dependency "bundler", "~> 1.17"
|
||||
spec.add_development_dependency "rake", "~> 10.0"
|
||||
spec.add_development_dependency "rspec", "~> 3.0"
|
||||
|
||||
|
||||
spec.add_dependency "grpc", ">= 1.19.0"
|
||||
spec.add_dependency "google-protobuf", ">=3.7"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user