From 6a2f3e791bd5ec345fa4bb0d71a698503af653ee Mon Sep 17 00:00:00 2001 From: TheDude Date: Wed, 14 Jul 2021 18:09:15 +0100 Subject: [PATCH] Added close transaction and renamed hash to script in OutPoint.cs --- .../Bolt3/Bolt3CommitmentTestContext.cs | 2 +- .../Bolt3/Bolt3CommitmentTests.cs | 6 +-- .../Bolt3/Bolt3FundingTests.cs | 4 +- src/Lyn.Protocol.Tests/TransactionHelper.cs | 2 +- src/Lyn.Protocol/Bolt3/ILightningScripts.cs | 27 +++------- src/Lyn.Protocol/Bolt3/LightningScripts.cs | 19 +++++-- .../Bolt3/LightningTransactions.cs | 51 +++++++++++++++---- .../Bolt3/Types/ClosingTransactionIn.cs | 20 ++++++++ src/Lyn.Types/Bitcoin/OutPoint.cs | 4 +- .../Serializers/OutPointSerializer.cs | 4 +- 10 files changed, 96 insertions(+), 43 deletions(-) diff --git a/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTestContext.cs b/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTestContext.cs index 1d7cfc5..f20da63 100644 --- a/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTestContext.cs +++ b/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTestContext.cs @@ -81,7 +81,7 @@ public Bolt3CommitmentTestContext() FundingOutputIndex = 0; FundingAmount = 10000000; FundingTxid = UInt256.Parse("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be"); - FundingTxOutpoint = new OutPoint { Hash = FundingTxid, Index = FundingOutputIndex }; + FundingTxOutpoint = new OutPoint { TxId = FundingTxid, Index = FundingOutputIndex }; CommitmentNumber = 42; ToSelfDelay = 144; diff --git a/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTests.cs b/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTests.cs index 2ac9176..4210640 100644 --- a/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTests.cs +++ b/src/Lyn.Protocol.Tests/Bolt3/Bolt3CommitmentTests.cs @@ -156,12 +156,12 @@ public void Bolt3CommitmentAndHtlcTransactionTest(Bolt3CommitmentTestVectors vec continue; } - OutPoint outPoint = new OutPoint { Hash = localTransaction.Hash, Index = (uint)htlcIndex }; + OutPoint outPoint = new OutPoint { TxId = localTransaction.Hash, Index = (uint)htlcIndex }; Transaction htlcTransaction; byte[] redeemScript; if (htlc.Htlc.Side == ChannelSide.Local) { - redeemScript = Context.LightningScripts.GetHtlcOfferedRedeemscript( + redeemScript = Context.LightningScripts.GetHtlcOfferedRedeemScript( Context.LocalHtlckey, Context.RemoteHtlckey, htlc.Htlc.Rhash, @@ -183,7 +183,7 @@ public void Bolt3CommitmentAndHtlcTransactionTest(Bolt3CommitmentTestVectors vec } else { - redeemScript = Context.LightningScripts.GetHtlcReceivedRedeemscript( + redeemScript = Context.LightningScripts.GetHtlcReceivedRedeemScript( htlc.Htlc.Expirylocktime, Context.LocalHtlckey, Context.RemoteHtlckey, diff --git a/src/Lyn.Protocol.Tests/Bolt3/Bolt3FundingTests.cs b/src/Lyn.Protocol.Tests/Bolt3/Bolt3FundingTests.cs index 8cc65f4..8b996e3 100644 --- a/src/Lyn.Protocol.Tests/Bolt3/Bolt3FundingTests.cs +++ b/src/Lyn.Protocol.Tests/Bolt3/Bolt3FundingTests.cs @@ -26,7 +26,7 @@ public void AppendixBFundingWitnessScriptTest() byte[] localFundingPubkey = Hex.FromString("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb"); byte[] remoteFundingPubkey = Hex.FromString("030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1"); - byte[] script = lightningScripts.CreaateFundingTransactionScript(new PublicKey(localFundingPubkey), new PublicKey(remoteFundingPubkey)); + byte[] script = lightningScripts.CreateFundingTransactionScript(new PublicKey(localFundingPubkey), new PublicKey(remoteFundingPubkey)); Assert.Equal(script, Hex.FromString("5221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae")); } @@ -45,7 +45,7 @@ public void AppendixBCreateFundingTransactionTest() byte[] localFundingPubkey = Hex.FromString("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb"); byte[] remoteFundingPubkey = Hex.FromString("030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c1"); - byte[] script = lightningScripts.CreaateFundingTransactionScript(new PublicKey(localFundingPubkey), new PublicKey(remoteFundingPubkey)); + byte[] script = lightningScripts.CreateFundingTransactionScript(new PublicKey(localFundingPubkey), new PublicKey(remoteFundingPubkey)); var trx = Transaction.Parse( "0200000001adbb20ea41a8423ea937e76e8151636bf6093b70eaff942930d20576600521fd000000006b48304502210090587b6201e166ad6af0227d3036a9454223d49a1f11839c1a362184340ef0240220577f7cd5cca78719405cbf1de7414ac027f0239ef6e214c90fcaab0454d84b3b012103535b32d5eb0a6ed0982a0479bbadc9868d9836f6ba94dd5a63be16d875069184ffffffff028096980000000000220020c015c4a6be010e21657068fc2e6a9d02b27ebe4d490a25846f7237f104d1a3cd20256d29010000001600143ca33c2e4446f4a305f23c80df8ad1afdcf652f900000000", NBitcoin.Network.RegTest); diff --git a/src/Lyn.Protocol.Tests/TransactionHelper.cs b/src/Lyn.Protocol.Tests/TransactionHelper.cs index 9542435..9fb8261 100644 --- a/src/Lyn.Protocol.Tests/TransactionHelper.cs +++ b/src/Lyn.Protocol.Tests/TransactionHelper.cs @@ -20,7 +20,7 @@ public static string ParseToString(Transaction transaction) foreach (var input in transaction.Inputs) { sb.AppendLine($"Sequence={input.Sequence}"); - sb.AppendLine($"PreviousOutput={input.PreviousOutput.Index}-{input.PreviousOutput.Hash}"); + sb.AppendLine($"PreviousOutput={input.PreviousOutput.Index}-{input.PreviousOutput.TxId}"); sb.AppendLine($"SignatureScript={ (input.SignatureScript == null ? string.Empty : new NBitcoin.Script(input.SignatureScript))}"); if (input.ScriptWitness?.Components != null) diff --git a/src/Lyn.Protocol/Bolt3/ILightningScripts.cs b/src/Lyn.Protocol/Bolt3/ILightningScripts.cs index 340f93d..84ad421 100644 --- a/src/Lyn.Protocol/Bolt3/ILightningScripts.cs +++ b/src/Lyn.Protocol/Bolt3/ILightningScripts.cs @@ -5,30 +5,19 @@ namespace Lyn.Protocol.Bolt3 { public interface ILightningScripts { - byte[] CreaateFundingTransactionScript(PublicKey pubkey1, PublicKey pubkey2); - byte[] GetRevokeableRedeemscript(PublicKey revocationKey, ushort contestDelay, PublicKey broadcasterDelayedPaymentKey); + byte[] CreateFundingTransactionScript(PublicKey pubkey1, PublicKey pubkey2); + + TransactionWitness CreateClosingTransactionWitnessScript(PublicKey pubkey1, PublicKey pubkey2); + byte[] GetRevokeableRedeemScript(PublicKey revocationKey, ushort contestDelay, PublicKey broadcasterDelayedPaymentKey); byte[] AnchorToRemoteRedeem(PublicKey remoteKey); byte[] AnchorOutput(PublicKey fundingPubkey); - byte[] GetHtlcOfferedRedeemscript( - PublicKey localhtlckey, - PublicKey remotehtlckey, - UInt256 paymenthash, - PublicKey revocationkey, - bool optionAnchorOutputs); + byte[] GetHtlcOfferedRedeemScript(PublicKey localhtlckey, PublicKey remotehtlckey, UInt256 paymenthash, PublicKey revocationkey, bool optionAnchorOutputs); - byte[] GetHtlcReceivedRedeemscript( - ulong expirylocktime, - PublicKey localhtlckey, - PublicKey remotehtlckey, - UInt256 paymenthash, - PublicKey revocationkey, - bool optionAnchorOutputs); - - ulong CommitNumberObscurer( - PublicKey openerPaymentBasepoint, - PublicKey accepterPaymentBasepoint); + byte[] GetHtlcReceivedRedeemScript(ulong expirylocktime, PublicKey localhtlckey, PublicKey remotehtlckey, UInt256 paymenthash, PublicKey revocationkey, bool optionAnchorOutputs); + ulong CommitNumberObscurer(PublicKey openerPaymentBasepoint, PublicKey accepterPaymentBasepoint); + byte[] FundingRedeemScript(PublicKey pubkey1, PublicKey pubkey2); void SetCommitmentInputWitness(TransactionInput transactionInput, BitcoinSignature localSignature, BitcoinSignature remoteSignature, byte[] pubkeyScriptToRedeem); void SetHtlcSuccessInputWitness(TransactionInput transactionInput, BitcoinSignature localSignature, BitcoinSignature remoteSignature, Preimage preimage, byte[] pubkeyScriptToRedeem); diff --git a/src/Lyn.Protocol/Bolt3/LightningScripts.cs b/src/Lyn.Protocol/Bolt3/LightningScripts.cs index 1054ea0..0d69a3e 100644 --- a/src/Lyn.Protocol/Bolt3/LightningScripts.cs +++ b/src/Lyn.Protocol/Bolt3/LightningScripts.cs @@ -11,7 +11,7 @@ namespace Lyn.Protocol.Bolt3 { public class LightningScripts : ILightningScripts { - public byte[] CreaateFundingTransactionScript(PublicKey pubkey1, PublicKey pubkey2) + public byte[] CreateFundingTransactionScript(PublicKey pubkey1, PublicKey pubkey2) { // todo: sort pubkeys lexicographically @@ -26,6 +26,17 @@ public byte[] CreaateFundingTransactionScript(PublicKey pubkey1, PublicKey pubke return script.ToBytes(); } + public TransactionWitness CreateClosingTransactionWitnessScript(PublicKey pubkey1, PublicKey pubkey2) + { + var script = new Script(OpcodeType.OP_0, Op.GetPushOp(pubkey1), Op.GetPushOp(pubkey2)) + .ToWitScript(); + + return new TransactionWitness + { + Components = script.Pushes.Select(_ => new TransactionWitnessComponent {RawData = _}).ToArray() + }; + } + /* BOLT #3: * * This output sends funds back to the owner of this commitment transaction and @@ -45,7 +56,7 @@ public byte[] CreaateFundingTransactionScript(PublicKey pubkey1, PublicKey pubke * OP_CHECKSIG */ - public byte[] GetRevokeableRedeemscript(PublicKey revocationKey, ushort contestDelay, PublicKey broadcasterDelayedPaymentKey) + public byte[] GetRevokeableRedeemScript(PublicKey revocationKey, ushort contestDelay, PublicKey broadcasterDelayedPaymentKey) { var script = new Script( OpcodeType.OP_IF, @@ -138,7 +149,7 @@ public byte[] AnchorOutput(PublicKey fundingPubkey) * OP_ENDIF */ - public byte[] GetHtlcOfferedRedeemscript( + public byte[] GetHtlcOfferedRedeemScript( PublicKey localhtlckey, PublicKey remotehtlckey, UInt256 paymenthash, @@ -239,7 +250,7 @@ public byte[] GetHtlcOfferedRedeemscript( * OP_ENDIF */ - public byte[] GetHtlcReceivedRedeemscript( + public byte[] GetHtlcReceivedRedeemScript( ulong expirylocktime, PublicKey localhtlckey, PublicKey remotehtlckey, diff --git a/src/Lyn.Protocol/Bolt3/LightningTransactions.cs b/src/Lyn.Protocol/Bolt3/LightningTransactions.cs index a962edd..84f42ba 100644 --- a/src/Lyn.Protocol/Bolt3/LightningTransactions.cs +++ b/src/Lyn.Protocol/Bolt3/LightningTransactions.cs @@ -1,14 +1,10 @@ using System; -using System.Buffers; using System.Collections.Generic; using System.Linq; using Lyn.Protocol.Bolt3.Types; -using Lyn.Protocol.Common; using Lyn.Protocol.Common.Messages; using Lyn.Types.Bitcoin; using Lyn.Types.Fundamental; -using Lyn.Types.Serialization; -using Lyn.Types.Serialization.Serializers; using Microsoft.Extensions.Logging; using NBitcoin; using Transaction = Lyn.Types.Bitcoin.Transaction; @@ -212,7 +208,7 @@ public CommitmenTransactionOut CommitmentTransaction(CommitmentTransactionIn com // todo round down msat to sat in s common method Satoshis amount = (Satoshis)htlc.AmountMsat; - byte[]? wscript = _lightningScripts.GetHtlcOfferedRedeemscript( + byte[]? wscript = _lightningScripts.GetHtlcOfferedRedeemScript( commitmentTransactionIn.Keyset.LocalHtlcKey, commitmentTransactionIn.Keyset.RemoteHtlcKey, htlc.Rhash, @@ -249,7 +245,7 @@ public CommitmenTransactionOut CommitmentTransaction(CommitmentTransactionIn com // todo round down msat to sat in s common method Satoshis amount = (Satoshis)htlc.AmountMsat; - var wscript = _lightningScripts.GetHtlcReceivedRedeemscript( + var wscript = _lightningScripts.GetHtlcReceivedRedeemScript( htlc.Expirylocktime, commitmentTransactionIn.Keyset.LocalHtlcKey, commitmentTransactionIn.Keyset.RemoteHtlcKey, @@ -288,7 +284,7 @@ public CommitmenTransactionOut CommitmentTransaction(CommitmentTransactionIn com // todo round down msat to sat in s common method Satoshis amount = (Satoshis)commitmentTransactionIn.SelfPayMsat; - var wscript = _lightningScripts.GetRevokeableRedeemscript(commitmentTransactionIn.Keyset.LocalRevocationKey, commitmentTransactionIn.ToSelfDelay, commitmentTransactionIn.Keyset.LocalDelayedPaymentKey); + var wscript = _lightningScripts.GetRevokeableRedeemScript(commitmentTransactionIn.Keyset.LocalRevocationKey, commitmentTransactionIn.ToSelfDelay, commitmentTransactionIn.Keyset.LocalDelayedPaymentKey); var wscriptinst = new Script(wscript); @@ -488,7 +484,7 @@ private Transaction CreateHtlcTransaction(HtlcTransactionIn htlcTransactionIn) } }; - byte[]? wscript = _lightningScripts.GetRevokeableRedeemscript(htlcTransactionIn.RevocationPubkey, htlcTransactionIn.ToSelfDelay, htlcTransactionIn.LocalDelayedkey); + byte[]? wscript = _lightningScripts.GetRevokeableRedeemScript(htlcTransactionIn.RevocationPubkey, htlcTransactionIn.ToSelfDelay, htlcTransactionIn.LocalDelayedkey); var wscriptinst = new Script(wscript); Script? p2Wsh = PayToWitScriptHashTemplate.Instance.GenerateScriptPubKey(new WitScriptId(wscriptinst)); // todo: dan - move this to interface @@ -540,7 +536,44 @@ public Satoshis HtlcSuccessFee(bool optionAnchorOutputs, Satoshis feeratePerKw) public Transaction ClosingTransaction(ClosingTransactionIn closingTransactionIn) { - throw new NotImplementedException(); + var list = new List {closingTransactionIn.LocalPublicKey, closingTransactionIn.RemotePublicKey}; + + list.Sort(new LexicographicByteComparer()); + + var input = new TransactionInput + { + Sequence = 0xFFFFFFFF, + PreviousOutput = closingTransactionIn.FundingCreatedTxout, + SignatureScript = new byte[0], + ScriptWitness = _lightningScripts + .CreateClosingTransactionWitnessScript(list.First(), list.Last()) + }; + + var outputs = new List(); + + if (closingTransactionIn.AmountToPayLocal > 0) + { + outputs.Add(new TransactionOutput + { + Value = closingTransactionIn.ChannelOpenedFromLocalNode + ? closingTransactionIn.AmountToPayLocal - closingTransactionIn.Fee + : closingTransactionIn.AmountToPayLocal, + PublicKeyScript = closingTransactionIn.LocalScriptPublicKey + }); + } + + if (closingTransactionIn.AmountToPayRemote > 0) + { + outputs.Add(new TransactionOutput + { + Value = closingTransactionIn.ChannelOpenedFromLocalNode + ? closingTransactionIn.AmountToPayRemote + : closingTransactionIn.AmountToPayRemote- closingTransactionIn.Fee, + PublicKeyScript = closingTransactionIn.RemoteScriptPublicKey + }); + } + + return new Transaction {Version = 2, LockTime = 0, Inputs = new[] {input}, Outputs = outputs.ToArray()}; } } } \ No newline at end of file diff --git a/src/Lyn.Protocol/Bolt3/Types/ClosingTransactionIn.cs b/src/Lyn.Protocol/Bolt3/Types/ClosingTransactionIn.cs index fba6cfa..20586f7 100644 --- a/src/Lyn.Protocol/Bolt3/Types/ClosingTransactionIn.cs +++ b/src/Lyn.Protocol/Bolt3/Types/ClosingTransactionIn.cs @@ -1,9 +1,29 @@ using System.Collections.Generic; using Lyn.Types.Bitcoin; +using Lyn.Types.Fundamental; namespace Lyn.Protocol.Bolt3.Types { public class ClosingTransactionIn { + public ChannelSide SideThatOpenedChannel { get; set; } + + public OutPoint FundingCreatedTxout { get; set; } + + public PublicKey LocalPublicKey { get; set; } + + public PublicKey RemotePublicKey { get; set; } + + + public byte[] LocalScriptPublicKey { get; set; } + public byte[] RemoteScriptPublicKey { get; set; } + + public long AmountToPayRemote { get; set; } + + public long AmountToPayLocal { get; set; } + + public Satoshis Fee { get; set; } + + public bool ChannelOpenedFromLocalNode => SideThatOpenedChannel == ChannelSide.Local; } } \ No newline at end of file diff --git a/src/Lyn.Types/Bitcoin/OutPoint.cs b/src/Lyn.Types/Bitcoin/OutPoint.cs index 0a26b5a..bc8998d 100644 --- a/src/Lyn.Types/Bitcoin/OutPoint.cs +++ b/src/Lyn.Types/Bitcoin/OutPoint.cs @@ -8,7 +8,7 @@ public class OutPoint /// /// The hash of the referenced transaction. /// - public UInt256 Hash { get; set; } = UInt256.Zero; + public UInt256 TxId { get; set; } = UInt256.Zero; /// /// The index of the specific output in the transaction. The first output is 0, etc. @@ -23,7 +23,7 @@ public class OutPoint /// public bool IsNull() { - return (Hash == UInt256.Zero && Index == uint.MaxValue); + return (TxId == UInt256.Zero && Index == uint.MaxValue); } } } \ No newline at end of file diff --git a/src/Lyn.Types/Serialization/Serializers/OutPointSerializer.cs b/src/Lyn.Types/Serialization/Serializers/OutPointSerializer.cs index 7212fce..3f98c1c 100644 --- a/src/Lyn.Types/Serialization/Serializers/OutPointSerializer.cs +++ b/src/Lyn.Types/Serialization/Serializers/OutPointSerializer.cs @@ -16,14 +16,14 @@ public OutPoint Deserialize(ref SequenceReader reader, ProtocolTypeSeriali { return new OutPoint { - Hash = reader.ReadWithSerializer(_uInt256Serializator), + TxId = reader.ReadWithSerializer(_uInt256Serializator), Index = reader.ReadUInt() }; } public int Serialize(OutPoint typeInstance, IBufferWriter writer, ProtocolTypeSerializerOptions? options = null) { - int size = writer.WriteWithSerializer(typeInstance.Hash!, _uInt256Serializator); + int size = writer.WriteWithSerializer(typeInstance.TxId!, _uInt256Serializator); size += writer.WriteUInt(typeInstance.Index); return size;