Beispiel #1
0
        public void CanValidateCETSigs()
        {
            var offer  = Parse <Messages.Offer>("Data/Offer2.json");
            var accept = Parse <Messages.Accept>("Data/Accept2.json");
            var sign   = Parse <Messages.Sign>("Data/Sign2.json");

            var funding = Transaction.Parse("02000000000102118df2c93e3be1f05af4fe16ba82eab135ba624bb96db5b218222ad7f99f1c400000000000ffffffff9ff82cc6f2509b2bc9f0d56fa37bea855d40b74cd2633e75ca343469508e892d0000000000ffffffff0372dc010000000000220020a9dbe7e77d7f966d4b8ab36d576e19f4c392caf039ea5e53a0078d771a3e92eefb2a042a01000000160014ec8d64ed4742016d12deac1106577f94747f09f41b5afa2f01000000160014c22a6d286c108ad458ba60e00576859a78ae8de002473044022071e5e088ba8e4434ab044350448479561ad8179bf2a88789a9459215bcf610c80220128535940b083dca52791f373b6474dd24f3a9096c039411943ffcff6fb293a801210333dea605a7d2c223de68c1189bd5e171cb94bd35712122eff08a9cdbed5dca8d02473044022059d6a3ae8cee036998065d2139459a029c4d925f952a860434910b12f9e9a3a002205871d75c28b8486caa90a6d44c3ae59eeb98bbbc90990f5b510466178eb80d9c01210310f8a8f195fe3167af0db8c6bb208e9a8ac075c99276a0150c1d374d0d13d26a00000000", Network.Main);
            var cet     = Transaction.Parse("020000000001016b3ea350dfa327d9c4c030fc3e2e49c04aae28d6020f1624202c57bf620dddbc0000000000feffffff01a08601000000000016001404bea358f0a1c3bdabe6148fd2ea44838110ef830400483045022100c3d1901ae606851946edaaa007cc5ed3ba05a1a812e76d8e4f5a3b034fe11dd4022063d51c44bc8a78ee752e735e3c84be2329e0eb15022db552a1181eaf1edb7dff0147304402205f5ba5027909abef13c61bc604eb2deaf223549213c6c7c9456d972ae1badf800220348bf4918b01bea8ee69d104a60254ad23159654a8f8b31e43da1fc53603c2390147522102d63fec4b8a6705a34b32e5476a5a07c20774fe2abcf88eeea320c9714bc3010e21038b22637639db00f3dd5d8c46644b61954a01362bc92a87eb136bd6ba80c1f12852ae64000000", Network.Main);

            RemoveSigs(cet);
            RemoveSigs(funding);


            foreach (var isInitiator in new[] { true, false })
            {
                var b         = new DLCTransactionBuilder(isInitiator, offer, accept, sign, funding, Network.RegTest);
                var actualCet = b.BuildCET(offer.ContractInfo[1].Outcome);
                Assert.Equal(cet.ToString(), actualCet.ToString());
                Assert.True(b.VerifyRemoteCetSigs(isInitiator ? accept.CetSigs.OutcomeSigs : sign.CetSigs.OutcomeSigs));
                Assert.True(b.VerifyRemoteRefundSignature());

                b = new DLCTransactionBuilder(isInitiator, offer, accept, sign, Network.RegTest);
                var actualFunding = b.GetFundingTransaction();
                RemoveSigs(actualFunding);
                Assert.Equal(funding.ToString(), actualFunding.ToString());
            }
        }
Beispiel #2
0
        public void CanCreateFunding()
        {
            var offer  = Parse <Messages.Offer>("Data/Offer2.json");
            var accept = Parse <Messages.Accept>("Data/Accept2.json");

            var b = new DLCTransactionBuilder(true, offer, accept, null, Network.RegTest);

            var expected = Transaction.Parse("0200000000010213a31be98d8e29a08cbb3b64de59727b0a6285e2f34338a3ca576ae5250692fc0100000000ffffffffff9208b14cf747f04a7b653debb4dfedc9b4a3291985b91c872dda54b420e7110200000000ffffffff037418f605000000002200205f4c70ba1400d2efc70a2ff32f07a2d79c26d9f6c49a65bde68efc53cf12f0d5dcfb0d700000000016001486039150dbcfd4a136243a7cb4ea2b3dad24e606bbbfd17400000000160014775277f3b5fc4c32bed192ac6b3fd90b4403f1c902483045022100a9518901afbe84053644c0f08e50d45eb373616f7aa729677ecf5718f2fac8b2022032949f92beaf8d4ee8b944fa9452f1feabbc863e59b943fd5acce1aedc89feef012102a3795336df054e4fe408e0d453ff48fc8a0ec4ba7ef9234391babfa4247aef0a024730440220320baf857ca4ff420613e89930dcaa6ee423251fdcfc2108c7a029263cab5c2302201237c4b978c65ed3ed00bb763ff1d1b6f61bdaff8a29e5ca537761e089ce5265012103af3fb0e5788dbfdc478dd5e58d27001177a9a3fb19b990c883dd3ea75aec424c00000000", Network.Main);

            foreach (var input in expected.Inputs)
            {
                input.WitScript = WitScript.Empty;
            }
            // We cheat for now...
            b.FundingOverride = expected;
            var actual = b.BuildFunding();

            Assert.Equal(expected.ToString(), actual.ToString());

            expected = Transaction.Parse("02000000000101ef43cda2f4e8fb1e254864a00b3b81f4915b9c8d060e41fe37523001ac03a08a0000000000feffffff0200879303000000001600145e8bf52af230ae61e9637ecb498a5bed8d09a59b005a62020000000016001411eb59e70fa4abe8c02699b54fb71ed3d76574fc04004830450221009942f177f872e786f6d1edd98abc78e4114bcbd886a72170190e39515841da85022001908f53671480bd2eb869dc7c34d188921a72243dce1e3108646f83948e516401483045022100e988416aba7518317c7a3e1acf051e0ef28ba7cb0473039115072a4073a2a650022028ee55f5d202f9facc230aa7f7c9fd18a75228fc80493bea9d1ae36085cec8830147522103fbae911c3acb17f06a9a544f068d925715900c69d0c58408543c09e75ba249332103f81ee1a50b2b01ce32982b2900c34f0c3a3745e98458e67ecf239086f4e8908852aec8000000", Network.Main);
            foreach (var input in expected.Inputs)
            {
                input.WitScript = WitScript.Empty;
            }

            actual = b.BuildRefund();
            Assert.Equal(expected.ToString(), actual.ToString());
        }
Beispiel #3
0
        public void FullExchange2()
        {
            var initiatorInputKey = new Key();
            var acceptorInputKey  = new Key();

            var offerKey  = new Key();
            var acceptKey = new Key();

            var initiator = new DLCTransactionBuilder(true, null, null, null, Network.RegTest);
            var acceptor  = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);

            var oracleInfo         = OracleInfo.Parse("e4d36e995ff4bba4da2b60ad907d61d36e120d6f7314a3c2a20c6e27a5cd850ff67f8f41718c86f05eb95fab308f5ed788a2a963124299154648f97124caa579");
            var requiredCollateral = initiator.Offer(oracleInfo.PubKey, oracleInfo.RValue,
                                                     new DiscretePayoffs()
            {
                new DiscretePayoff("Republicans", Money.Coins(0.4m)),
                new DiscretePayoff("Democrats", -Money.Coins(0.6m)),
                new DiscretePayoff("Smith", Money.Zero)
            }, new Timeouts()
            {
                ContractMaturity = 100,
                ContractTimeout  = 200
            });
            var fund1 = GetFundingPSBT(acceptorInputKey, requiredCollateral);
            var offer = initiator.FundOffer(offerKey, fund1);

            var payoff = acceptor.Accept(offer);
            var fund2  = GetFundingPSBT(initiatorInputKey, payoff.CalculateMinimumCollateral());
            var accept = acceptor.FundAccept(acceptKey, fund2);

            initiator.Sign1(accept);
            var fundPSBT = initiator.GetFundingPSBT();

            fundPSBT.SignWithKeys(initiatorInputKey);
            var sign = initiator.Sign2(offerKey, fundPSBT);

            acceptor.Finalize1(sign);
            fundPSBT = acceptor.GetFundingPSBT();
            fundPSBT.SignWithKeys(acceptorInputKey);
            var fullyVerified = acceptor.Finalize(fundPSBT);

            foreach (var i in fullyVerified.Inputs)
            {
                Assert.NotNull(i.WitScript);
            }

            var cet          = initiator.BuildCET(offer.ContractInfo[0].Outcome);
            var keyBytes     = Encoders.Hex.DecodeData("d1da46f96f0be50bce2bbabe6bc8633f448ec3f1d14715a0b086b68ed34e095d");
            var oracleSecret = new Key(keyBytes);

            var sig = oracleInfo.RValue.CreateSchnorrSignature(oracleSecret.ToECPrivKey());

            Assert.True(oracleInfo.PubKey.SigVerifyBIP340(sig, new DiscreteOutcome("Republicans").Hash));

            initiator.BuildSignedCET(offerKey, oracleSecret);

            this.testOutputHelper.WriteLine("----Final state------");
            testOutputHelper.WriteLine(JObject.Parse(initiator.ExportState()).ToString(Formatting.Indented));
            this.testOutputHelper.WriteLine("---------------------");
        }
Beispiel #4
0
        protected override async Task InvokeAsyncBase(InvocationContext context)
        {
            var name = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("name")?.Trim();

            if (name is null)
            {
                throw new CommandOptionRequiredException("name");
            }
            if (await this.TryGetDLC(name) != null)
            {
                throw new CommandException("name", "This DLC already exists");
            }
            EventFullName evtName = context.GetEventName();
            var           oracle  = await GetOracle("eventfullname", evtName.OracleName);

            if (oracle?.PubKey is null)
            {
                throw new CommandException("eventfullname", "The specified oracle does not exists");
            }
            var evt = await GetEvent("eventfullname", evtName);

            var payoffsStr = context.ParseResult.CommandResult.GetArgumentValueOrDefault <List <string> >("payoff");

            if (payoffsStr is null || payoffsStr.Count == 0)
            {
                throw new CommandOptionRequiredException("payoff");
            }
            var payoffs = CreatePayoffs(payoffsStr);

            FixCasing(evt, payoffs);
            var builder = new DLCTransactionBuilder(true, null, null, null, Network);

            var timeout = new Timeouts()
            {
                ContractMaturity = 0,
                ContractTimeout  = Constants.NeverLockTime
            };

            if (context.ParseResult.HasOption("cetlocktime"))
            {
                timeout.ContractMaturity = new LockTime(context.ParseResult.ValueForOption <uint>("cetlocktime"));
            }
            if (context.ParseResult.HasOption("refundlocktime"))
            {
                timeout.ContractTimeout = new LockTime(context.ParseResult.ValueForOption <uint>("refundlocktime"));
            }
            var collateral = payoffs.CalculateMinimumCollateral();

            builder.Offer(oracle.PubKey, evt.EventId !.RValue, payoffs, timeout);
            var dlc = await Repository.NewDLC(evt.EventId, builder);

            await NameRepository.AsDLCNameRepository().SetMapping(name, dlc.LocalId);

            context.Console.Out.Write($"Offer created, you now need to setup the DLC sending {collateral} BTC to yourself. For more information, run `dlc show \"{name}\"`.");
        }
Beispiel #5
0
        public void CanCalculateEventId()
        {
            var offer  = Parse <Messages.Offer>("Data/Offer2.json");
            var accept = Parse <Messages.Accept>("Data/Accept2.json");

            var  builder  = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);
            var  payoff   = builder.Accept(offer);
            PSBT fundPSBT = GetFundingPSBT(new Key(), payoff.CalculateMinimumCollateral());
            var  accept2  = builder.FundAccept(new Key(), fundPSBT);

            Assert.Equal(accept.EventId, accept2.EventId);
        }
Beispiel #6
0
        public void CanCreateAccept()
        {
            var offer           = Parse <Messages.Offer>("Data/Offer2.json");
            var builder         = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);
            var fundingInputKey = new Key();

            var  payoffs  = builder.Accept(offer);
            PSBT fundPSBT = GetFundingPSBT(fundingInputKey, payoffs.CalculateMinimumCollateral());
            var  accept   = builder.FundAccept(new Key(), fundPSBT);

            builder = new DLCTransactionBuilder(true, offer, null, null, Network.RegTest);
            builder.Sign1(accept);
        }
Beispiel #7
0
        public async Task dlc_test()
        {
            await foreach (var vector in DLCTestVector.ReadVectors())
            {
                Assert.Equal(vector.ExpectedOfferTLV, Encoders.Hex.EncodeData(vector.Offer.ToTLV()));
                var accept = new Accept();
                accept.TemporaryContractId = vector.Offer.GetTemporaryContractId();
                vector.FillFundingInformation(accept, "acceptParams");
                accept.PubKeys.FundingKey = vector.AcceptPrivateKey.PubKey;
                var acceptor = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);
                acceptor.Accept(vector.Offer, accept.TotalCollateral);
                Assert.True(acceptor.State.TemporaryContractId);
                var builtAccept = acceptor.FundAccept(vector.AcceptPrivateKey, accept.CreateSetupPSBT(Network.RegTest));
                accept.CetSigs = builtAccept.CetSigs;
                // this signature is non deterministic...
                accept.CetSigs.RefundSig = Accept.ParseFromTLV(vector.ExpectedAcceptTLV, Network.RegTest).CetSigs.RefundSig;
                Assert.Equal(vector.ExpectedAcceptTLV, Encoders.Hex.EncodeData(accept.ToTLV()));

                var actualFundingTransaction = acceptor.GetFundingTransaction();
                var actualRefundTransaction  = acceptor.BuildRefund();
                var actualCETs = new Transaction[vector.Offer.ContractInfo.Length];
                for (int i = 0; i < vector.Offer.ContractInfo.Length; i++)
                {
                    actualCETs[i] = acceptor.BuildCET(vector.Offer.ContractInfo[i].Outcome);
                }
                var offerer = new DLCTransactionBuilder(true, vector.Offer, null, null, Network.RegTest);
                offerer.Sign1(accept);
                var fundingPSBT = offerer.GetFundingPSBT();
                FillSignatures(fundingPSBT, vector.SignedTxs.FundingTx);
                var sign = offerer.Sign2(vector.OfferPrivateKey, fundingPSBT);
                // this signature is non deterministic...
                sign.CetSigs.RefundSig = Sign.ParseFromTLV(vector.ExpectedSignTLV, Network.RegTest).CetSigs.RefundSig;
                Assert.Equal(vector.ExpectedSignTLV, Encoders.Hex.EncodeData(sign.ToTLV()));
                Assert.Equal(
                    RemoveForDebug(vector.UnsignedTxs.FundingTx.ToString()),
                    RemoveForDebug(actualFundingTransaction.ToString()));
                Assert.Equal(
                    RemoveForDebug(vector.UnsignedTxs.RefundTx.ToString()),
                    RemoveForDebug(actualRefundTransaction.ToString()));
                for (int i = 0; i < actualCETs.Length; i++)
                {
                    Assert.Equal(
                        RemoveForDebug(vector.UnsignedTxs.Cets[i].ToString()),
                        RemoveForDebug(actualCETs[i].ToString()));
                }

                Assert.NotNull(offerer.State.ContractId);
                Assert.Equal(offerer.State.ContractId, acceptor.State.ContractId);
                Assert.False(offerer.State.TemporaryContractId);
            }
        }
Beispiel #8
0
        public void CanCreateAccept()
        {
            var  offer           = Parse <Messages.Offer>("Data/Offer2.json");
            var  builder         = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);
            var  fundingInputKey = new Key();
            PSBT fundPSBT        = GetFundingPSBT(fundingInputKey, Money.Coins(0.4m));

            Assert.True(PSBTFundingTemplate.TryParse(fundPSBT.ToBase64(), fundPSBT.Network, out var template));
            var accept = builder.Accept(offer, template);

            builder = new DLCTransactionBuilder(true, offer, accept, null, Network.RegTest);
            Assert.True(builder.VerifyRemoteCetSigs());
            Assert.True(builder.VerifyRemoteRefundSignature());
        }
Beispiel #9
0
 protected override async Task InvokeAsyncBase(InvocationContext context)
 {
     ECXOnlyPubKey         oraclePubKey = GetOraclePubKey(context.ParseResult.ValueForOption <string>("oraclepubkey"));
     SchnorrNonce          schnorrNonce = GetSchnorrNonce(context.ParseResult.ValueForOption <string>("nonce"));
     DLCTransactionBuilder builder      = new DLCTransactionBuilder(true, null, null, null, Network);
     var timeouts = new Timeouts()
     {
         ContractMaturity = GetLockTime(context, "maturity") ?? new LockTime(0),
         ContractTimeout  = GetLockTime(context, "expiration") ?? throw new CommandOptionRequiredException("expiration")
     };
     var ci = GetOutcomes(context.ParseResult.ValueForOption <string[]>("outcome"));
     //var offer = builder.Offer(template, new OracleInfo(oraclePubKey, schnorrNonce), ci, timeouts);
     //WriteObject(context, offer);
 }
Beispiel #10
0
            public DLCNextStep GetNextStep(Network network)
            {
                if (BuilderState is null)
                {
                    throw new InvalidOperationException("BuilderState not set");
                }
                var         builder = new DLCTransactionBuilder(BuilderState.ToString(), network);
                DLCNextStep nextStep;

                if (builder.State.IsInitiator)
                {
                    if (FundKeyPath is null)
                    {
                        nextStep = DLCNextStep.Setup;
                    }
                    else if (Accept is null)
                    {
                        nextStep = DLCNextStep.CheckSigs;
                    }
                    else if (Sign is null)
                    {
                        nextStep = DLCNextStep.Fund;
                    }
                    else
                    {
                        nextStep = DLCNextStep.Done;
                    }
                }
                else
                {
                    if (builder.State.Funding is null)
                    {
                        nextStep = DLCNextStep.Setup;
                    }
                    else if (Sign is null)
                    {
                        nextStep = DLCNextStep.CheckSigs;
                    }
                    else if (!builder.State.Funding.PSBT.CanExtractTransaction())
                    {
                        nextStep = DLCNextStep.Fund;
                    }
                    else
                    {
                        nextStep = DLCNextStep.Done;
                    }
                }
                return(nextStep);
            }
Beispiel #11
0
        public void FullExchange2()
        {
            var initiatorInputKey = new Key();
            var acceptorInputKey  = new Key();

            var fund1     = GetFundingPSBT(initiatorInputKey, Money.Coins(0.6m));
            var fund2     = GetFundingPSBT(acceptorInputKey, Money.Coins(0.4m));
            var initiator = new DLCTransactionBuilder(true, null, null, null, Network.RegTest);
            var acceptor  = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);

            var oracleInfo = OracleInfo.Parse("156c7d1c7922f0aa1168d9e21ac77ea88bbbe05e24e70a08bbe0519778f2e5daea3a68d8749b81682513b0479418d289d17e24d4820df2ce979f1a56a63ca525");
            var offer      = initiator.Offer(PSBTFundingTemplate.Parse(fund1), oracleInfo,
                                             ContractInfo.CreateContract(
                                                 ("Republicans_win", Money.Coins(1.0m)),
                                                 ("Democrats_win", Money.Coins(0m)),
                                                 ("other", Money.Coins(0.6m))), new Timeouts()
            {
                ContractMaturity = 100,
                ContractTimeout  = 200
            });
            var accept = acceptor.Accept(offer, PSBTFundingTemplate.Parse(fund2));

            initiator.VerifySign(accept);
            var fundPSBT = initiator.BuildFundingPSBT();

            fundPSBT.SignWithKeys(initiatorInputKey);
            var sign = initiator.EndSign(fundPSBT);

            acceptor.VerifySign(sign);
            fundPSBT = acceptor.BuildFundingPSBT();
            fundPSBT.SignWithKeys(acceptorInputKey);
            var psbt = acceptor.CombineFunding(fundPSBT);

            psbt = psbt.Finalize();
            var fullyVerified = psbt.ExtractTransaction();

            foreach (var i in fullyVerified.Inputs)
            {
                Assert.NotNull(i.WitScript);
            }

            var cet      = initiator.BuildCET(offer.ContractInfo[0].Outcome);
            var keyBytes = Encoders.Hex.DecodeData("39eabd151030f4f2d518fb8a8d00f679aa9e034c66263032a1245a04cfbc592b");
            //Array.Reverse(keyBytes);
            var oracleSecret = new Key(keyBytes);

            initiator.BuildSignedCET(oracleSecret);
        }
Beispiel #12
0
        protected override async Task InvokeAsyncBase(InvocationContext context)
        {
            var name = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("name")?.Trim();

            if (name is null)
            {
                throw new CommandOptionRequiredException("name");
            }
            var dlc = await GetDLC("name", name);

            if (dlc?.BuilderState is null ||
                dlc?.OracleInfo is null)
            {
                throw new CommandException("name", "This DLC does not exist");
            }
            var builder = new DLCTransactionBuilder(dlc.BuilderState.ToString(), Network);
            var psbt    = context.ParsePSBT("signedpsbt", Network);

            if (!builder.State.IsInitiator)
            {
                context.AssertState("name", dlc, false, DLCNextStep.Fund, Network);
                var fullySigned = builder.Finalize(psbt);
                dlc.BuilderState = builder.ExportStateJObject();
                await Repository.SaveDLC(dlc);

                context.WriteTransaction(fullySigned, builder.State.Funding?.FundCoin, Network);
            }
            else
            {
                context.AssertState("name", dlc, true, DLCNextStep.Fund, Network);
                var key = await Repository.GetKey(dlc.FundKeyPath !);

                try
                {
                    var sign = builder.Sign2(key, psbt);
                    dlc.Sign         = sign;
                    dlc.BuilderState = builder.ExportStateJObject();
                    await Repository.SaveDLC(dlc);

                    context.WriteObject(sign, Repository.JsonSettings);
                }
                catch (Exception ex)
                {
                    throw new CommandException("signedpsbt", $"Invalid PSBT. ({ex.Message})");
                }
            }
        }
Beispiel #13
0
        public void CanCreateAccept()
        {
            var offer = Parse <Messages.Offer>("Data/Offer2.json");

            offer.SetContractPreimages(
                new DiscreteOutcome("Republicans_win"),
                new DiscreteOutcome("Democrats_win"),
                new DiscreteOutcome("other")
                );
            var  builder         = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);
            var  fundingInputKey = new Key();
            var  payoffs         = builder.Accept(offer);
            PSBT fundPSBT        = GetFundingPSBT(fundingInputKey, payoffs.CalculateMinimumCollateral());
            var  accept          = builder.FundAccept(new Key(), fundPSBT);

            builder = new DLCTransactionBuilder(true, offer, null, null, Network.RegTest);
            builder.Sign1(accept);
        }
Beispiel #14
0
        public async Task <DLCState> NewDLC(OracleInfo oracleInfo, DLCTransactionBuilder builder)
        {
            var dir = Path.Combine(RepositoryDirectory, "DLCs");

            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            var s = new DLCState()
            {
                OracleInfo   = oracleInfo,
                BuilderState = builder.ExportStateJObject(),
                Id           = RandomUtils.GetUInt256()
            };
            var file = GetDLCFilePath(s.Id);
            await File.WriteAllTextAsync(file, JsonConvert.SerializeObject(s, JsonSettings));

            return(s);
        }
Beispiel #15
0
        public void FullExchange()
        {
            var offerExample      = Parse <Messages.Offer>("Data/Offer2.json");
            var offerKey          = new Key();
            var acceptKey         = new Key();
            var initiatorInputKey = new Key();
            var acceptorInputKey  = new Key();
            var initiator         = new DLCTransactionBuilder(true, null, null, null, Network.RegTest);
            var requiredFund      = initiator.Offer(offerExample.OracleInfo.PubKey,
                                                    offerExample.OracleInfo.RValue,
                                                    DiscretePayoffs.CreateFromContractInfo(offerExample.ContractInfo, offerExample.TotalCollateral,
                                                                                           new[] { new DiscreteOutcome("Republicans_win"), new DiscreteOutcome("Democrats_win"), new DiscreteOutcome("other") }),
                                                    offerExample.Timeouts);
            var fund1          = GetFundingPSBT(initiatorInputKey, requiredFund);
            var offer          = initiator.FundOffer(offerKey, fund1);
            var acceptor       = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);
            var acceptorPayoff = acceptor.Accept(offer);
            var fund2          = GetFundingPSBT(acceptorInputKey, acceptorPayoff.CalculateMinimumCollateral());
            var accept         = acceptor.FundAccept(acceptKey, fund2);

            initiator.Sign1(accept);
            var fundPSBT = initiator.GetFundingPSBT();

            fundPSBT.SignWithKeys(initiatorInputKey);
            var sign = initiator.Sign2(offerKey, fundPSBT);

            acceptor.Finalize1(sign);
            fundPSBT = acceptor.GetFundingPSBT();
            fundPSBT.SignWithKeys(acceptorInputKey);
            var fullyVerified = acceptor.Finalize(fundPSBT);

            foreach (var i in fullyVerified.Inputs)
            {
                Assert.NotNull(i.WitScript);
            }
            fundPSBT = acceptor.GetFundingPSBT();
            if (fundPSBT.TryGetEstimatedFeeRate(out var estimated))
            {
                Assert.True(estimated > new FeeRate(1.0m), "Fee Rate of the funding PSBT are too low");
            }
        }
Beispiel #16
0
        protected override async Task InvokeAsyncBase(InvocationContext context)
        {
            var name = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("name")?.Trim();

            if (name is null)
            {
                throw new CommandOptionRequiredException("name");
            }
            var dlc = await GetDLC("name", name);

            if (dlc?.OracleInfo is null || dlc?.BuilderState is null)
            {
                throw new CommandException("name", "This DLC does not exist");
            }
            var str = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("transaction")?.Trim();

            if (str is null)
            {
                throw new CommandOptionRequiredException("transaction");
            }
            if (!Transaction.TryParse(str, Network, out var tx) || tx is null)
            {
                throw new CommandException("transaction", "Cannot parse the transaction");
            }
            var builder = new DLCTransactionBuilder(dlc.BuilderState.ToString(), Network);

            try
            {
                var key = builder.ExtractAttestation(tx);
                await Repository.AddAttestation(dlc.OracleInfo, key);

                context.Console.Out.Write(key.ToHex());
            }
            catch (Exception ex)
            {
                throw new CommandException("transaction", $"Impossible to extract the attestation from this transaction. {ex.Message}");
            }
        }
Beispiel #17
0
        public void CanValidateCETSigs()
        {
            var offer  = Parse <Messages.Offer>("Data/Offer2.json");
            var accept = Parse <Messages.Accept>("Data/Accept2.json");
            var sign   = Parse <Messages.Sign>("Data/Sign2.json");

            var funding = Transaction.Parse("0200000000010213a31be98d8e29a08cbb3b64de59727b0a6285e2f34338a3ca576ae5250692fc0100000000ffffffffff9208b14cf747f04a7b653debb4dfedc9b4a3291985b91c872dda54b420e7110200000000ffffffff037418f605000000002200205f4c70ba1400d2efc70a2ff32f07a2d79c26d9f6c49a65bde68efc53cf12f0d5dcfb0d700000000016001486039150dbcfd4a136243a7cb4ea2b3dad24e606bbbfd17400000000160014775277f3b5fc4c32bed192ac6b3fd90b4403f1c902483045022100a9518901afbe84053644c0f08e50d45eb373616f7aa729677ecf5718f2fac8b2022032949f92beaf8d4ee8b944fa9452f1feabbc863e59b943fd5acce1aedc89feef012102a3795336df054e4fe408e0d453ff48fc8a0ec4ba7ef9234391babfa4247aef0a024730440220320baf857ca4ff420613e89930dcaa6ee423251fdcfc2108c7a029263cab5c2302201237c4b978c65ed3ed00bb763ff1d1b6f61bdaff8a29e5ca537761e089ce5265012103af3fb0e5788dbfdc478dd5e58d27001177a9a3fb19b990c883dd3ea75aec424c00000000", Network.Main);
            var cet     = Transaction.Parse("02000000000101ef43cda2f4e8fb1e254864a00b3b81f4915b9c8d060e41fe37523001ac03a08a0000000000feffffff0100e1f505000000001600145e8bf52af230ae61e9637ecb498a5bed8d09a59b04004730440220131ff77ab066d7bfa682691ca70ec9d229dc8e595c4055ddf23468fbed981bf602205c3a0d6b1bfb46da00ed8aad93498ece2d621a0eeacf7e30654be5135d773f0001483045022100efba114529a59517e7c39f6e38fb1c44bd935a8fe6ec63ab15e7a1912bc8b7aa02204a5d54d7f15adf102bb61fc9e4cf174c5a6be212f3c64b74687c6a717ae9104b0147522103fbae911c3acb17f06a9a544f068d925715900c69d0c58408543c09e75ba249332103f81ee1a50b2b01ce32982b2900c34f0c3a3745e98458e67ecf239086f4e8908852ae64000000", Network.Main);

            foreach (var input in cet.Inputs)
            {
                input.WitScript = WitScript.Empty;
            }
            foreach (var isInitiator in new[] { true, false })
            {
                var b = new DLCTransactionBuilder(isInitiator, offer, accept, sign, Network.RegTest);
                b.FundingOverride = funding;
                var actualCet = b.BuildCET(offer.ContractInfo[0].Outcome);
                Assert.Equal(cet.ToString(), actualCet.ToString());
                Assert.True(b.VerifyRemoteCetSigs());
                Assert.True(b.VerifyRemoteRefundSignature());
            }
        }
Beispiel #18
0
        public void FullExchange()
        {
            var offerExample = Parse <Messages.Offer>("Data/Offer2.json");

            var initiatorInputKey = new Key();
            var acceptorInputKey  = new Key();

            var fund1     = GetFundingPSBT(initiatorInputKey, Money.Coins(0.6m));
            var fund2     = GetFundingPSBT(acceptorInputKey, Money.Coins(0.4m));
            var initiator = new DLCTransactionBuilder(true, null, null, null, Network.RegTest);
            var acceptor  = new DLCTransactionBuilder(false, null, null, null, Network.RegTest);

            var offer  = initiator.Offer(PSBTFundingTemplate.Parse(fund1), offerExample.OracleInfo, offerExample.ContractInfo, offerExample.Timeouts);
            var accept = acceptor.Accept(offer, PSBTFundingTemplate.Parse(fund2));

            initiator.VerifySign(accept);
            var fundPSBT = initiator.BuildFundingPSBT();

            fundPSBT.SignWithKeys(initiatorInputKey);
            var sign = initiator.EndSign(fundPSBT);

            acceptor.VerifySign(sign);
            fundPSBT = acceptor.BuildFundingPSBT();
            fundPSBT.SignWithKeys(acceptorInputKey);
            fundPSBT = acceptor.CombineFunding(fundPSBT);
            fundPSBT = fundPSBT.Finalize();
            var fullyVerified = fundPSBT.ExtractTransaction();

            foreach (var i in fullyVerified.Inputs)
            {
                Assert.NotNull(i.WitScript);
            }
            if (fundPSBT.TryGetEstimatedFeeRate(out var estimated))
            {
                Assert.True(estimated > new FeeRate(1.0m), "Fee Rate of the funding PSBT are too low");
            }
        }
Beispiel #19
0
        protected override async Task InvokeAsyncBase(InvocationContext context)
        {
            var offer = DLCHelpers.GetOffer(context, Repository.JsonSettings);
            var name  = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("name")?.Trim();

            if (name is null)
            {
                throw new CommandOptionRequiredException("name");
            }
            if (await this.TryGetDLC(name) != null)
            {
                throw new CommandException("name", "This DLC already exists");
            }
            if (offer.OracleInfo is null)
            {
                throw new CommandException("offer", "Missing oracleInfo");
            }
            if (offer.Timeouts is null)
            {
                throw new CommandException("offer", "Missing timeouts");
            }
            if (offer.ContractInfo is null)
            {
                throw new CommandException("offer", "Missing contractInfos");
            }

            var oracle = await Repository.GetOracle(offer.OracleInfo.PubKey);

            if (oracle is null)
            {
                throw new CommandException("offer", "Unknown oracle");
            }
            var evt = await Repository.GetEvent(offer.OracleInfo.PubKey, offer.OracleInfo.RValue);

            if (evt is null)
            {
                throw new CommandException("offer", "Unknown event");
            }

            var maturity = new LockTimeEstimation(offer.Timeouts.ContractMaturity, Network);
            var refund   = new LockTimeEstimation(offer.Timeouts.ContractTimeout, Network);

            if (!refund.UnknownEstimation)
            {
                if (refund.EstimatedRemainingBlocks == 0)
                {
                    throw new CommandException("offer", "The refund should not be immediately valid");
                }
                if (refund.EstimatedRemainingBlocks < maturity.EstimatedRemainingBlocks)
                {
                    throw new CommandException("offer", "The refund should not be valid faster than the contract execution transactions");
                }
            }

            offer.SetContractPreimages(evt.Outcomes);
            try
            {
                var builder = new DLCTransactionBuilder(false, null, null, null, Network);
                builder.Accept(offer);
                var dlc = await Repository.NewDLC(offer.OracleInfo, builder);

                dlc.BuilderState = builder.ExportStateJObject();
                dlc.Offer        = JObject.FromObject(offer, JsonSerializer.Create(Repository.JsonSettings));
                await NameRepository.AsDLCNameRepository().SetMapping(name, dlc.Id);

                await Repository.SaveDLC(dlc);

                context.Console.Out.Write($"Contract accepted, you now need to setup the DLC. For more information, run `dlc show \"{name}\"`.");
            }
            catch (Exception ex)
            {
                throw new CommandException("offer", $"Invalid offer or PSBT. ({ex.Message})");
            }
        }
Beispiel #20
0
        protected override async Task InvokeAsyncBase(InvocationContext context)
        {
            var name = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("name")?.Trim();

            if (name is null)
            {
                throw new CommandOptionRequiredException("name");
            }

            var dlc = await GetDLC("name", name);

            if (dlc?.BuilderState is null ||
                dlc?.OracleInfo is null)
            {
                throw new CommandException("name", "This DLC does not exist");
            }

            var oracle = await Repository.GetOracle(dlc.OracleInfo.PubKey);

            string?oracleName = null;

            EventFullName eventName = new EventFullName("???", "???");

            if (oracle != null)
            {
                oracleName = await NameRepository.GetName(Scopes.Oracles, new OracleId(dlc.OracleInfo.PubKey).ToString());

                var ev = await Repository.GetEvent(dlc.OracleInfo);

                eventName = await NameRepository.AsEventRepository().ResolveName(dlc.OracleInfo) ?? eventName;
            }
            var shown = ParseShownItem(context);

            if (shown == ShowOption.DLC)
            {
                context.Console.Out.WriteLine($"Name: {name}");
                context.Console.Out.WriteLine($"Local Id: {dlc.Id}");
                var builder = new DLCTransactionBuilder(dlc.BuilderState.ToString(), Network);
                var role    = builder.State.IsInitiator ? "Offerer" : "Acceptor";
                context.Console.Out.WriteLine($"Event: {eventName}");
                context.Console.Out.WriteLine($"Role: {role}");
                var nextStep = dlc.GetNextStep(Network);
                context.Console.Out.WriteLine($"Next step: {nextStep}");
                context.Console.Out.WriteLine($"Next step explanation:");
                context.Console.Out.Write($"{Explain(nextStep, name, builder.State)}");
            }
            else if (shown == ShowOption.Offer)
            {
                if (dlc.Offer is null)
                {
                    throw new CommandException("offer", "No offer available for this DLC");
                }
                context.WriteObject(dlc.Offer, Repository.JsonSettings);
            }
            else if (shown == ShowOption.Accept)
            {
                if (dlc.Accept is null)
                {
                    throw new CommandException("offer", "No accept message available for this DLC");
                }
                context.WriteObject(dlc.Accept, Repository.JsonSettings);
            }
            else if (shown == ShowOption.Funding)
            {
                try
                {
                    var builder = new DLCTransactionBuilder(dlc.BuilderState.ToString(), Network);
                    context.WritePSBT(builder.GetFundingPSBT());
                }
                catch
                {
                    throw new CommandException("funding", "No funding PSBT ready for this DLC");
                }
            }
            else if (shown == ShowOption.Abort)
            {
                if (dlc.Abort is null)
                {
                    throw new CommandException("abort", "No abort PSBT for this DLC");
                }
                context.WritePSBT(dlc.Abort);
            }
            else if (shown == ShowOption.Refund)
            {
                try
                {
                    var builder = new DLCTransactionBuilder(dlc.BuilderState.ToString(), Network);
                    context.WriteTransaction(builder.BuildRefund(), Network);
                }
                catch
                {
                    throw new CommandException("refund", "No refund PSBT ready for this DLC");
                }
            }
            else
            {
                throw new NotSupportedException();
            }
        }
Beispiel #21
0
 public static JObject ExportStateJObject(this DLCTransactionBuilder builder)
 {
     return(JObject.Parse(builder.ExportState()));
 }
Beispiel #22
0
        protected override async Task InvokeAsyncBase(InvocationContext context)
        {
            var name = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("name")?.Trim();

            if (name is null)
            {
                throw new CommandOptionRequiredException("name");
            }
            var dlc = await GetDLC("name", name);

            if (dlc?.OracleInfo is null)
            {
                throw new CommandException("name", "This DLC does not exist");
            }
            if (dlc.BuilderState is null || dlc.FundKeyPath is null)
            {
                throw new CommandException("name", "This DLC is not in the right state to get executed");
            }

            var evt = await Repository.GetEvent(dlc.OracleInfo.PubKey, dlc.OracleInfo.RValue);

            var attestation = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("attestation")?.Trim();
            Key oracleKey;

            try
            {
                if (attestation is null)
                {
                    var k = evt?.Attestations?.Select(o => o.Value).FirstOrDefault();
                    if (k is null)
                    {
                        throw new CommandOptionRequiredException("attestation");
                    }
                    oracleKey = k;
                }
                else
                {
                    oracleKey = new Key(Encoders.Hex.DecodeData(attestation));
                }
            }
            catch (CommandOptionRequiredException)
            {
                throw;
            }
            catch
            {
                throw new CommandException("attestation", "Cannot parse the attestation");
            }

            var builder = new DLCTransactionBuilder(dlc.BuilderState.ToString(), Network);
            var key     = await Repository.GetKey(dlc.FundKeyPath);

            try
            {
                var execution = builder.Execute(key, oracleKey);
                if (evt is Repository.Event)
                {
                    await Repository.AddAttestation(dlc.OracleInfo, oracleKey);
                }
                context.WriteTransaction(execution.CET, Network);
            }
            catch (Exception ex)
            {
                throw new CommandException("attestation", $"Error while building the CET. ({ex.Message})");
            }
        }