Skip to content

Add messages for open channel v1 #45

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/NLightning.Common/BitUtils/BitWriter.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;

namespace NLightning.Common.BitUtils;

public class BitWriter
public class BitWriter : IDisposable
{
private int _bitOffset;
private byte[] _buffer;
Expand All @@ -17,7 +18,7 @@ public BitWriter(int totalBits)

TotalBits = totalBits;
var totalBytes = (totalBits + 7) / 8;
_buffer = new byte[totalBytes];
_buffer = ArrayPool<byte>.Shared.Rent(totalBytes);
}

public void GrowByBits(int additionalBits)
Expand Down Expand Up @@ -226,4 +227,9 @@ public byte[] ToArray()

return bytes;
}

public void Dispose()
{
ArrayPool<byte>.Shared.Return(_buffer);
}
}
135 changes: 129 additions & 6 deletions src/NLightning.Common/Factories/MessageFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.Extensions.Options;
using NBitcoin;
using NBitcoin.Crypto;
using NLightning.Common.Managers;

namespace NLightning.Common.Factories;

Expand Down Expand Up @@ -395,6 +396,50 @@ public IMessage CreateClosingSignedMessage(ChannelId channelId, ulong feeSatoshi
return new ClosingSignedMessage(payload, new FeeRangeTlv(minFeeSatoshis, maxFeeSatoshis));
}

/// <summary>
/// Create a OpenChannel1 message.
/// </summary>
/// <param name="temporaryChannelId">The temporary channel id.</param>
/// <param name="fundingAmount">The amount of satoshis we're adding to the channel.</param>
/// <param name="pushAmount">The amount of satoshis we're pushing to the other side.</param>
/// <param name="channelReserveAmount">The channel reserve amount.</param>
/// <param name="feeRatePerKw">The fee rate per kw.</param>
/// <param name="maxAcceptedHtlcs">The max accepted htlcs.</param>
/// <param name="revocationBasepoint">The revocation pubkey.</param>
/// <param name="paymentBasepoint">The payment pubkey.</param>
/// <param name="delayedPaymentBasepoint">The delayed payment pubkey.</param>
/// <param name="htlcBasepoint">The htlc pubkey.</param>
/// <param name="firstPerCommitmentPoint">The first per commitment pubkey.</param>
/// <param name="channelFlags">The flags for the channel.</param>
/// <param name="upfrontShutdownScriptTlv">The upfront shutdown script tlv.</param>
/// <param name="channelTypeTlv">The channel type tlv.</param>
/// <returns>The OpenChannel1 message.</returns>
/// <seealso cref="OpenChannel1Message"/>
/// <seealso cref="ChannelId"/>
/// <seealso cref="LightningMoney"/>
/// <seealso cref="PubKey"/>
/// <seealso cref="UpfrontShutdownScriptTlv"/>
/// <seealso cref="ChannelTypeTlv"/>
public IMessage CreateOpenChannel1Message(ChannelId temporaryChannelId, LightningMoney fundingAmount,
LightningMoney pushAmount, LightningMoney channelReserveAmount,
LightningMoney feeRatePerKw, ushort maxAcceptedHtlcs,
PubKey revocationBasepoint, PubKey paymentBasepoint,
PubKey delayedPaymentBasepoint, PubKey htlcBasepoint,
PubKey firstPerCommitmentPoint, ChannelFlags channelFlags,
UpfrontShutdownScriptTlv? upfrontShutdownScriptTlv,
ChannelTypeTlv? channelTypeTlv)
{
var payload = new OpenChannel1Payload(_nodeOptions.Network.ChainHash, temporaryChannelId, fundingAmount,
pushAmount, _nodeOptions.DustLimitAmount,
_nodeOptions.MaxHtlcValueInFlight, channelReserveAmount,
_nodeOptions.HtlcMinimumAmount, feeRatePerKw, _nodeOptions.ToSelfDelay,
maxAcceptedHtlcs, SecureKeyManager.GetPrivateKey().PubKey,
revocationBasepoint, paymentBasepoint, delayedPaymentBasepoint,
htlcBasepoint, firstPerCommitmentPoint, channelFlags);

return new OpenChannel1Message(payload, upfrontShutdownScriptTlv, channelTypeTlv);
}

/// <summary>
/// Create a OpenChannel2 message.
/// </summary>
Expand Down Expand Up @@ -447,6 +492,45 @@ channelType is null ?
requireConfirmedInputs ? new RequireConfirmedInputsTlv() : null);
}

/// <summary>
/// Create a AcceptChannel1 message.
/// </summary>
/// <param name="temporaryChannelId">The temporary channel id.</param>
/// <param name="channelReserveAmount">The channel reserve amount.</param>
/// <param name="minimumDepth">The minimum depth.</param>
/// <param name="maxAcceptedHtlcs">The max accepted htlcs.</param>
/// <param name="revocationBasepoint">The revocation pubkey.</param>
/// <param name="paymentBasepoint">The payment pubkey.</param>
/// <param name="delayedPaymentBasepoint">The delayed payment pubkey.</param>
/// <param name="htlcBasepoint">The htlc pubkey.</param>
/// <param name="firstPerCommitmentPoint">The first per commitment pubkey.</param>
/// <param name="upfrontShutdownScriptTlv">The upfront shutdown script tlv.</param>
/// <param name="channelTypeTlv">The channel type tlv.</param>
/// <returns>The AcceptChannel1 message.</returns>
/// <seealso cref="AcceptChannel1Message"/>
/// <seealso cref="ChannelId"/>
/// <seealso cref="LightningMoney"/>
/// <seealso cref="PubKey"/>
/// <seealso cref="UpfrontShutdownScriptTlv"/>
/// <seealso cref="ChannelTypeTlv"/>
public IMessage CreateAcceptChannel1Message(ChannelId temporaryChannelId, LightningMoney channelReserveAmount,
uint minimumDepth, ushort maxAcceptedHtlcs,
PubKey revocationBasepoint, PubKey paymentBasepoint,
PubKey delayedPaymentBasepoint, PubKey htlcBasepoint,
PubKey firstPerCommitmentPoint,
UpfrontShutdownScriptTlv? upfrontShutdownScriptTlv,
ChannelTypeTlv? channelTypeTlv)
{
var payload = new AcceptChannel1Payload(temporaryChannelId, _nodeOptions.DustLimitAmount,
_nodeOptions.MaxHtlcValueInFlight, channelReserveAmount,
_nodeOptions.HtlcMinimumAmount, minimumDepth, _nodeOptions.ToSelfDelay,
maxAcceptedHtlcs, SecureKeyManager.GetPrivateKey().PubKey,
revocationBasepoint, paymentBasepoint, delayedPaymentBasepoint,
htlcBasepoint, firstPerCommitmentPoint);

return new AcceptChannel1Message(payload, upfrontShutdownScriptTlv, channelTypeTlv);
}

/// <summary>
/// Create a AcceptChannel2 message.
/// </summary>
Expand Down Expand Up @@ -489,6 +573,43 @@ channelType is null ?
: new ChannelTypeTlv(channelType),
requireConfirmedInputs ? new RequireConfirmedInputsTlv() : null);
}

/// <summary>
/// Create a FundingCreated message.
/// </summary>
/// <param name="temporaryChannelId">The temporary channel id.</param>
/// <param name="fundingTxId">The funding transaction id.</param>
/// <param name="fundingOutputIndex">The funding output index.</param>
/// <param name="signature">The signature for the funding transaction.</param>
/// <returns>The FundingCreated message.</returns>
/// <seealso cref="FundingCreatedMessage"/>
/// <seealso cref="ChannelId"/>
/// <seealso cref="ECDSASignature"/>
/// <seealso cref="FundingCreatedPayload"/>
public IMessage CreatedFundingCreatedMessage(ChannelId temporaryChannelId, ReadOnlySpan<byte> fundingTxId,
ushort fundingOutputIndex, ECDSASignature signature)
{
var payload = new FundingCreatedPayload(temporaryChannelId, fundingTxId, fundingOutputIndex, signature);

return new FundingCreatedMessage(payload);
}

/// <summary>
/// Create a FundingSigned message.
/// </summary>
/// <param name="channelId">The channel id.</param>
/// <param name="signature"></param>
/// <returns>The FundingSigned message.</returns>
/// <seealso cref="FundingCreatedMessage"/>
/// <seealso cref="ChannelId"/>
/// <seealso cref="ECDSASignature"/>
/// <seealso cref="FundingCreatedPayload"/>
public IMessage CreatedFundingSignedMessage(ChannelId channelId, ECDSASignature signature)
{
var payload = new FundingSignedPayload(channelId, signature);

return new FundingSignedMessage(payload);
}
#endregion

#region Commitment
Expand Down Expand Up @@ -671,6 +792,14 @@ public IMessage CreateChannelReestablishMessage(ChannelId channelId, ulong nextC
return await PingMessage.DeserializeAsync(stream); // 18 -> 0x12
case MessageTypes.PONG:
return await PongMessage.DeserializeAsync(stream); // 19 -> 0x13
case MessageTypes.OPEN_CHANNEL:
return await OpenChannel1Message.DeserializeAsync(stream); // 32 -> 0x20
case MessageTypes.ACCEPT_CHANNEL:
return await AcceptChannel1Message.DeserializeAsync(stream); // 33 -> 0x21
case MessageTypes.FUNDING_CREATED:
return await FundingCreatedMessage.DeserializeAsync(stream); // 34 -> 0x22
case MessageTypes.FUNDING_SIGNED:
return await FundingSignedMessage.DeserializeAsync(stream); // 35 -> 0x23
case MessageTypes.CHANNEL_READY:
return await ChannelReadyMessage.DeserializeAsync(stream); // 36 -> 0x24
case MessageTypes.SHUTDOWN:
Expand Down Expand Up @@ -716,12 +845,6 @@ public IMessage CreateChannelReestablishMessage(ChannelId channelId, ulong nextC
case MessageTypes.CHANNEL_REESTABLISH:
return await ChannelReestablishMessage.DeserializeAsync(stream); // 136 -> 0x88

case MessageTypes.OPEN_CHANNEL:
case MessageTypes.ACCEPT_CHANNEL:
case MessageTypes.FUNDING_CREATED:
case MessageTypes.FUNDING_SIGNED:
throw new InvalidMessageException("You must use OpenChannel2 flow");

default:
{
// If type is unknown and even, throw exception
Expand Down
85 changes: 85 additions & 0 deletions src/NLightning.Common/Messages/AcceptChannel1Message.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System.Runtime.Serialization;

namespace NLightning.Common.Messages;

using Constants;
using Exceptions;
using Payloads;
using TLVs;
using Types;

/// <summary>
/// Represents an open_channel message.
/// </summary>
/// <remarks>
/// The accept_channel message is sent to the initiator in order to accept the channel opening.
/// The message type is 33.
/// </remarks>
public sealed class AcceptChannel1Message : BaseMessage
{
/// <summary>
/// The payload of the message.
/// </summary>
public new AcceptChannel1Payload Payload { get => (AcceptChannel1Payload)base.Payload; }

/// <summary>
/// Optional UpfrontShutdownScriptTlv
/// </summary>
public UpfrontShutdownScriptTlv? UpfrontShutdownScriptTlv { get; }

/// <summary>
/// Optional ChannelTypeTlv
/// </summary>
public ChannelTypeTlv? ChannelTypeTlv { get; }

public AcceptChannel1Message(AcceptChannel1Payload payload,
UpfrontShutdownScriptTlv? upfrontShutdownScriptTlv = null,
ChannelTypeTlv? channelTypeTlv = null)
: base(MessageTypes.ACCEPT_CHANNEL_2, payload)
{
UpfrontShutdownScriptTlv = upfrontShutdownScriptTlv;
ChannelTypeTlv = channelTypeTlv;

if (UpfrontShutdownScriptTlv is not null || ChannelTypeTlv is not null)
{
Extension = new TlvStream();
Extension.Add(UpfrontShutdownScriptTlv, ChannelTypeTlv);
}
}

/// <summary>
/// Deserialize a OpenChannel1Message from a stream.
/// </summary>
/// <param name="stream">The stream to deserialize from.</param>
/// <returns>The deserialized AcceptChannel1Message.</returns>
/// <exception cref="MessageSerializationException">Error deserializing OpenChannel1Message</exception>
public static async Task<AcceptChannel1Message> DeserializeAsync(Stream stream)
{
try
{
// Deserialize payload
var payload = await AcceptChannel1Payload.DeserializeAsync(stream);

// Deserialize extension
var extension = await TlvStream.DeserializeAsync(stream);
if (extension is null)
{
return new AcceptChannel1Message(payload);
}

var upfrontShutdownScriptTlv = extension.TryGetTlv(TlvConstants.UPFRONT_SHUTDOWN_SCRIPT, out var tlv)
? UpfrontShutdownScriptTlv.FromTlv(tlv!)
: null;

var channelTypeTlv = extension.TryGetTlv(TlvConstants.CHANNEL_TYPE, out tlv)
? ChannelTypeTlv.FromTlv(tlv!)
: null;

return new AcceptChannel1Message(payload, upfrontShutdownScriptTlv, channelTypeTlv);
}
catch (SerializationException e)
{
throw new MessageSerializationException("Error deserializing AcceptChannel1Message", e);
}
}
}
46 changes: 46 additions & 0 deletions src/NLightning.Common/Messages/FundingCreatedMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Runtime.Serialization;

namespace NLightning.Common.Messages;

using Constants;
using Exceptions;
using Payloads;

/// <summary>
/// Represents a funding_created message.
/// </summary>
/// <remarks>
/// The funding_created message is sent by the funder to the fundee after the funding transaction has been created.
/// The message type is 34.
/// </remarks>
public sealed class FundingCreatedMessage : BaseMessage
{
/// <summary>
/// The payload of the message.
/// </summary>
public new FundingCreatedPayload Payload { get => (FundingCreatedPayload)base.Payload; }

public FundingCreatedMessage(FundingCreatedPayload payload) : base(MessageTypes.ACCEPT_CHANNEL_2, payload)
{ }

/// <summary>
/// Deserialize a FundingCreatedMessage from a stream.
/// </summary>
/// <param name="stream">The stream to deserialize from.</param>
/// <returns>The deserialized FundingCreatedMessage.</returns>
/// <exception cref="MessageSerializationException">Error deserializing FundingCreatedMessage</exception>
public static async Task<FundingCreatedMessage> DeserializeAsync(Stream stream)
{
try
{
// Deserialize payload
var payload = await FundingCreatedPayload.DeserializeAsync(stream);

return new FundingCreatedMessage(payload);
}
catch (SerializationException e)
{
throw new MessageSerializationException("Error deserializing FundingCreatedMessage", e);
}
}
}
46 changes: 46 additions & 0 deletions src/NLightning.Common/Messages/FundingSignedMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Runtime.Serialization;

namespace NLightning.Common.Messages;

using Constants;
using Exceptions;
using Payloads;

/// <summary>
/// Represents a funding_signed message.
/// </summary>
/// <remarks>
/// The funding_signed message is sent by the funder to the fundee after the funding transaction has been created.
/// The message type is 35.
/// </remarks>
public sealed class FundingSignedMessage : BaseMessage
{
/// <summary>
/// The payload of the message.
/// </summary>
public new FundingSignedPayload Payload { get => (FundingSignedPayload)base.Payload; }

public FundingSignedMessage(FundingSignedPayload payload) : base(MessageTypes.ACCEPT_CHANNEL_2, payload)
{ }

/// <summary>
/// Deserialize a FundingSignedMessage from a stream.
/// </summary>
/// <param name="stream">The stream to deserialize from.</param>
/// <returns>The deserialized FundingSignedMessage.</returns>
/// <exception cref="MessageSerializationException">Error deserializing FundingSignedMessage</exception>
public static async Task<FundingSignedMessage> DeserializeAsync(Stream stream)
{
try
{
// Deserialize payload
var payload = await FundingSignedPayload.DeserializeAsync(stream);

return new FundingSignedMessage(payload);
}
catch (SerializationException e)
{
throw new MessageSerializationException("Error deserializing FundingCreatedMessage", e);
}
}
}
Loading
Loading