public async Task <bool> AddEvent(EventFullName name, SchnorrNonce nonce, string[] outcomes, RootedKeyPath?nonceKeyPath = null) { var oracle = await GetOracle(name.OracleName); if (oracle is null) { throw new InvalidOperationException("The oracle does not exists"); } var events = await GetEvents(oracle); var evt = GetEvent(name, events); if (evt is Event) { return(false); } evt = new Event() { Name = name.Name, Nonce = nonce, Outcomes = outcomes, NonceKeyPath = nonceKeyPath }; events.Add(evt); await SaveEvents(oracle, events); return(true); }
public async Task <Event?> TryGetEvent(EventFullName eventFullName) { var id = await NameRepository.AsEventRepository().GetEventId(eventFullName); if (id is null) { return(null); } return(await Repository.GetEvent(id)); }
public async Task <Event> GetEvent(string optionName, EventFullName eventFullName) { var evt = await TryGetEvent(eventFullName); if (evt is null) { throw new CommandException(optionName, "This event's full name does not exists"); } return(evt); }
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}\"`."); }
public async Task <Event?> GetEvent(EventFullName evtName) { var oracle = await GetOracle(evtName.OracleName); if (oracle is null) { return(null); } var evts = await GetEvents(oracle); return(evts.FirstOrDefault(e => e.Name.Equals(evtName.Name, StringComparison.OrdinalIgnoreCase))); }
public async Task <OracleInfo?> GetEventId(EventFullName eventFullName) { var oracleId = await NameRepository.AsOracleNameRepository().GetId(eventFullName.OracleName); if (oracleId is null) { return(null); } var id = await NameRepository.GetId(Scopes.Events, GetEventFullName(oracleId, eventFullName.Name)); if (id is null) { return(null); } if (!SchnorrNonce.TryParse(id, out var nonce)) { return(null); } return(new OracleInfo(oracleId.PubKey, nonce)); }
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(); } }
private static Event GetEvent(EventFullName name, List <Event> events) { return(events.FirstOrDefault(ev => ev.Name?.Equals(name.Name, StringComparison.OrdinalIgnoreCase) is true)); }
public async Task <DiscreteOutcome?> AddAttestation(EventFullName name, Key oracleAttestation) { var oracles = await GetOracles(); var oracle = GetOracle(name.OracleName, oracles); if (oracle?.PubKey is null) { return(null); } var events = await GetEvents(oracle); var evt = events.FirstOrDefault(e => e.Name.Equals(name.Name, StringComparison.OrdinalIgnoreCase)); if (evt?.Nonce is null) { return(null); } var attestation = oracleAttestation.ToECPrivKey(); var sig = evt.Nonce.CreateSchnorrSignature(attestation); if (sig is null) { return(null); } foreach (var outcome in evt.Outcomes) { var discreteOutcome = new DiscreteOutcome(outcome); if (!oracle.PubKey.SigVerifyBIP340(sig, discreteOutcome.Hash)) { continue; } evt.Attestations ??= new Dictionary <string, Key>(); if (!evt.Attestations.TryAdd(outcome, oracleAttestation)) { return(null); } // If we have two attestation for the same event, we can recover the private // key of the oracle if (evt.Attestations.Count > 1 && oracle.RootedKeyPath is null) { var sigs = evt.Attestations.Select(kv => (Outcome: new DiscreteOutcome(kv.Key), Signature: evt.Nonce.CreateSchnorrSignature(kv.Value.ToECPrivKey()) ?? throw new InvalidOperationException("Invalid signature in attestations"))) .Take(2) .ToArray(); if (!oracle.PubKey.TryExtractPrivateKey( sigs[0].Outcome.Hash, sigs[0].Signature, sigs[1].Outcome.Hash, sigs[1].Signature, out var extracted) || extracted is null) { throw new InvalidOperationException("Could not recover the private key of the oracle, this should never happen"); } var k = new Key(extracted.ToBytes()); oracle.RootedKeyPath = new RootedKeyPath(k.PubKey.GetHDFingerPrint(), new KeyPath()); await SaveOracles(oracles); if (!KeySetExists(k.PubKey.GetHDFingerPrint())) { await SaveKeyset(k.PubKey.GetHDFingerPrint(), new Keyset() { SingleKey = k }); } } await SaveEvents(oracle, events); return(discreteOutcome); } return(null); }