| | 1 | | using NBitcoin; |
| | 2 | |
|
| | 3 | | namespace NLightning.Infrastructure.Protocol.Models; |
| | 4 | |
|
| | 5 | | using Crypto.Hashes; |
| | 6 | |
|
| | 7 | | /// <summary> |
| | 8 | | /// Manages Lightning Network commitment numbers and their obscuring as defined in BOLT3. |
| | 9 | | /// </summary> |
| | 10 | | public sealed class CommitmentNumber |
| | 11 | | { |
| | 12 | | /// <summary> |
| | 13 | | /// Gets the commitment number value. |
| | 14 | | /// </summary> |
| 556 | 15 | | public ulong Value { get; private set; } |
| | 16 | |
|
| | 17 | | /// <summary> |
| | 18 | | /// Gets the obscuring factor derived from payment basepoints. |
| | 19 | | /// </summary> |
| 296 | 20 | | public ulong ObscuringFactor { get; } |
| | 21 | |
|
| | 22 | | /// <summary> |
| | 23 | | /// Gets the obscured commitment number (value XOR obscuring factor). |
| | 24 | | /// </summary> |
| 288 | 25 | | public ulong ObscuredValue => Value ^ ObscuringFactor; |
| | 26 | |
|
| | 27 | | /// <summary> |
| | 28 | | /// Represents a commitment number in the Lightning Network. |
| | 29 | | /// </summary> |
| 252 | 30 | | public CommitmentNumber(PubKey localPaymentBasepoint, PubKey remotePaymentBasepoint, |
| 252 | 31 | | ulong initialValue = 0) |
| | 32 | | { |
| 252 | 33 | | ArgumentNullException.ThrowIfNull(localPaymentBasepoint); |
| 252 | 34 | | ArgumentNullException.ThrowIfNull(remotePaymentBasepoint); |
| | 35 | |
|
| 252 | 36 | | Value = initialValue; |
| 252 | 37 | | ObscuringFactor = CalculateObscuringFactor(localPaymentBasepoint, remotePaymentBasepoint); |
| 252 | 38 | | } |
| | 39 | |
|
| | 40 | | /// <summary> |
| | 41 | | /// Increments the commitment number. |
| | 42 | | /// </summary> |
| | 43 | | /// <returns>This instance for chaining.</returns> |
| | 44 | | public CommitmentNumber Increment() |
| | 45 | | { |
| 4 | 46 | | Value++; |
| 4 | 47 | | return this; |
| | 48 | | } |
| | 49 | |
|
| | 50 | | /// <summary> |
| | 51 | | /// Calculates the transaction locktime value using the obscured commitment number. |
| | 52 | | /// </summary> |
| | 53 | | /// <returns>The transaction locktime.</returns> |
| | 54 | | public LockTime CalculateLockTime() |
| | 55 | | { |
| 140 | 56 | | return new LockTime((0x20 << 24) | (uint)(ObscuredValue & 0xFFFFFF)); |
| | 57 | | } |
| | 58 | |
|
| | 59 | | /// <summary> |
| | 60 | | /// Calculates the transaction sequence value using the obscured commitment number. |
| | 61 | | /// </summary> |
| | 62 | | /// <returns>The transaction sequence.</returns> |
| | 63 | | public Sequence CalculateSequence() |
| | 64 | | { |
| 144 | 65 | | return new Sequence((uint)((0x80UL << 24) | ((ObscuredValue >> 24) & 0xFFFFFF))); |
| | 66 | | } |
| | 67 | |
|
| | 68 | | /// <summary> |
| | 69 | | /// Calculates the 48-bit obscuring factor by hashing the concatenation of payment basepoints. |
| | 70 | | /// </summary> |
| | 71 | | /// <param name="localBasepoint">The local payment basepoint.</param> |
| | 72 | | /// <param name="remoteBasepoint">The remote payment basepoint.</param> |
| | 73 | | /// <returns>The 48-bit obscuring factor as ulong.</returns> |
| | 74 | | private static ulong CalculateObscuringFactor(PubKey localBasepoint, PubKey remoteBasepoint) |
| | 75 | | { |
| 252 | 76 | | using var sha256 = new Sha256(); |
| | 77 | |
|
| | 78 | | // Hash the concatenation of payment basepoints |
| 252 | 79 | | sha256.AppendData(localBasepoint.ToBytes()); |
| 252 | 80 | | sha256.AppendData(remoteBasepoint.ToBytes()); |
| | 81 | |
|
| 252 | 82 | | Span<byte> hashResult = stackalloc byte[32]; |
| 252 | 83 | | sha256.GetHashAndReset(hashResult); |
| | 84 | |
|
| | 85 | | // Extract the lower 48 bits (6 bytes) of the hash |
| 252 | 86 | | ulong obscuringFactor = 0; |
| 3528 | 87 | | for (var i = 26; i < 32; i++) // Last 6 bytes of the 32-byte hash |
| | 88 | | { |
| 1512 | 89 | | obscuringFactor = (obscuringFactor << 8) | hashResult[i]; |
| | 90 | | } |
| | 91 | |
|
| 252 | 92 | | return obscuringFactor; |
| 252 | 93 | | } |
| | 94 | | } |