-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add example channel interceptor plugin #4
base: master
Are you sure you want to change the base?
Conversation
4865b5a
to
7da270d
Compare
- Use inherited unsealed `InterceptOpenChannelCommand` trait from eclair instead of sealed plugin specific `Command`
- this decouples it better from ChannelData.LocalParams and makes it clear what parameters can be altered
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's going to be a bit painful, but I'd like to rename that plugin...it is currently a sample plugin that shouldn't be used, but I don't think this is very useful to have. We know we'll want a channel interceptor plugin for dual-funding, that decides how much funds (if any) the non-initiator should put into the channel. Since eclair only allows one plugin that intercepts channel requests, that plugin should implement optional additional restrictions (what you currently do in this PR) and liquidity allocation logic (what I'll add later when doing liquidity ads).
So it would make sense to name it to something that reflects that right now, this way I can simply extend it later when working on liquidity ads. Node operators would likely fork that plugin to tweak the liquidity allocation logic (and add new restrictions if needed).
I think that the funding aspect will be more important to users than the additional restrictions (especially since we moved most of the restrictions directly inside eclair
), so I believe naming the plugin channel-funding
makes more sense? WDYT?
channel-interceptor/README.md
Outdated
|
||
Disclaimer: this plugin is for demonstration purposes only. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the license here? The whole repository already has an Apache-2 license?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why I did that; fixed in 2ac483e.
channel-interceptor/README.md
Outdated
## Commands | ||
|
||
There are no cli commands for this plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary, if there are no commands, we don't even need to mention it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 2ac483e
// load or generate seed | ||
config = loadConfiguration(setup.datadir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wrong copy-paste for the comment. Also, why do you load the config early since you only use it once inside onKit
? If you loaded it inside onKit
you wouldn't need to have a var config: Config = _
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is necessary because of the two-step initialization of plugins. We can only access setup.datadir
in OnSetup
which we need to load config
. We can either have a var config
or load the parameters from config
in OnSetup
and create a var
to hold them until OnKit
is called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed the nit in 2ac483e
assert(minActiveChannels >= 0) | ||
assert(minTotalCapacity >= Satoshi(0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should assert on exact values here, it would make more sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in ee57ca2
private sealed trait ErrorResponse | ||
private object LessThanMinActiveChannelsError extends ErrorResponse { | ||
override def toString: String = s"rejected, less than $minActiveChannels active channels" | ||
} | ||
private object LessThanMinTotalCapacityError extends ErrorResponse { | ||
override def toString: String = s"rejected, less than $minTotalCapacity total capacity" | ||
} | ||
private object NoPublicChannelsError extends ErrorResponse { | ||
override def toString: String = s"rejected, no public channels" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There errors are used only once and only for their string value, we can remove them and inline the error message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in fc2f404
case WrappedGetNodeResponse(o, PublicNode(_, _, totalCapacity)) if totalCapacity < minTotalCapacity => | ||
rejectOpenChannel(o, LessThanMinTotalCapacityError) | ||
Behaviors.same | ||
case WrappedGetNodeResponse(o, UnknownNode(_)) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be configurable: otherwise when using this plugin we'd have no way of accepting channels from private nodes. There should be a boolean in the config, something like allow-private-nodes = true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 798a849
val publicKey: Crypto.PublicKey = PrivateKey(ByteVector32.One).publicKey | ||
val fundingAmount: Satoshi = 1 sat | ||
val remoteNodeId: Crypto.PublicKey = PrivateKey(ByteVector32.One).publicKey | ||
val temporaryChannelId: ByteVector32 = ByteVector32.Zeroes | ||
val openChannel: OpenChannel = OpenChannel(ByteVector32.Zeroes, temporaryChannelId, fundingAmount, 0 msat, 1 sat, UInt64(1), 1 sat, 1 msat, FeeratePerKw(1 sat), CltvExpiryDelta(1), 1, publicKey, publicKey, publicKey, publicKey, publicKey, publicKey, ChannelFlags.Private) | ||
val openDualChannel: OpenDualFundedChannel = OpenDualFundedChannel(ByteVector32.Zeroes, ByteVector32.One, FeeratePerKw(5000 sat), FeeratePerKw(4000 sat), 250_000 sat, 500 sat, UInt64(50_000), 15 msat, CltvExpiryDelta(144), 483, 650_000, publicKey(1), publicKey(2), publicKey(3), publicKey(4), publicKey(5), publicKey(6), publicKey(7), ChannelFlags(true)) | ||
val defaultParams: DefaultParams = DefaultParams(100 sat, 100000 msat, 100 msat, CltvExpiryDelta(288), 10) | ||
val channels: Map[Peer.FinalChannelId, actor.ActorRef] = Map(Peer.FinalChannelId(randomBytes32()) -> system.classicSystem.deadLetters) | ||
val connectedData: Peer.ConnectedData = Peer.ConnectedData(NodeAddress.fromParts("1.2.3.4", 42000).get, system.classicSystem.deadLetters, Init(TestConstants.Alice.nodeParams.features.initFeatures()), Init(TestConstants.Bob.nodeParams.features.initFeatures()), channels.map { case (k: ChannelId, v) => (k, v) }) | ||
val openChannelNonInitiator: OpenChannelNonInitiator = OpenChannelNonInitiator(remoteNodeId, Left(openChannel), connectedData.localFeatures, connectedData.remoteFeatures, connectedData.peerConnection.toTyped) | ||
val bobAnnouncement: NodeAnnouncement = announcement(TestConstants.Bob.nodeParams.nodeId) | ||
|
||
def publicKey(fill: Byte): PublicKey = PrivateKey(ByteVector.fill(32)(fill)).publicKey |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like what we did in the eclair PR, this can be greatly simplified, some of these values are unused or used only once, it deserves a clean-up to be less confusing and focus only on the test itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, cleaned up in dc34b5a.
It makes sense to create a potentially useful plugin rather than a generic example. It's good to also demonstrate other plugin features like using the config file to select which inbound peers though too so people can rift on that for their own plugins. |
- also update comments to expand functionality by determining amount of funding to contribute to dual-funding requests
This PR creates an example of a plugin that intercepts channel open messages sent to a node and continues the process of accepting a valid request, or returns a failure message to the sender.
This example plugin reads a custom configuration file with parameters
min-active-channels
andmin-total-capacity
and rejectsOpenChannel
requests from remote peers if they have less than the minimum public active channels or public total capacity.Depends on Eclair PR# 2552.