public override ContractInfo ReadJson(JsonReader reader, Type objectType, [AllowNull] ContractInfo existingValue, bool hasExistingValue, JsonSerializer serializer) { if (reader.TokenType != JsonToken.StartObject) { throw new JsonObjectException("ContractInfo is expecting a json object", reader); } var cd = serializer.Deserialize <ContractData>(reader); if (cd?.Sats is null || (cd?.Outcome is null && cd?.SHA256 is null)) { throw new JsonObjectException("Invalid contract info (missing fields)", reader); } DiscreteOutcome outcome = cd.Outcome is string?new DiscreteOutcome(cd.Outcome) : new DiscreteOutcome(Encoders.Hex.DecodeData(cd.SHA256)); if (cd.Outcome is string && cd.SHA256 is string) { if (outcome.Hash.AsSpan().SequenceCompareTo(Encoders.Hex.DecodeData(cd.SHA256)) != 0) { throw new JsonObjectException("Invalid contract info (invalid sha256 for the outcome)", reader); } } return(new ContractInfo(outcome, cd.Sats)); }
protected override async Task InvokeAsyncBase(InvocationContext context) { var outcome = context.ParseResult.CommandResult.GetArgumentValueOrDefault <string>("outcome")?.Trim(); var force = context.ParseResult.ValueForOption <bool>("force"); if (outcome is null) { throw new CommandOptionRequiredException("outcome"); } EventFullName evt = context.GetEventName(); var oracle = await Repository.GetOracle(evt.OracleName); if (oracle is null) { throw new CommandException("name", "This oracle does not exists"); } var discreteOutcome = new DiscreteOutcome(outcome); var evtObj = await Repository.GetEvent(evt); if (evtObj?.Nonce is null) { throw new CommandException("name", "This event does not exists"); } if (evtObj?.NonceKeyPath is null) { throw new CommandException("name", "You did not generated this event"); } outcome = evtObj.Outcomes.FirstOrDefault(o => o.Equals(outcome, StringComparison.OrdinalIgnoreCase)); if (outcome is null) { throw new CommandException("outcome", "This outcome does not exists in this event"); } var key = oracle.RootedKeyPath is RootedKeyPath ? await Repository.GetKey(oracle.RootedKeyPath) : null; if (key is null) { throw new CommandException("name", "You do not own the keys of this oracle"); } if (evtObj.Attestations?.ContainsKey(outcome) is true) { throw new CommandException("outcome", "This outcome has already been attested"); } if (evtObj.Attestations != null && evtObj.Attestations.Count > 0 && !force) { throw new CommandException("outcome", "An outcome has already been attested, attesting another one could leak the private key of your oracle. Use -f to force your action."); } var kValue = await Repository.GetKey(evtObj.NonceKeyPath); key.ToECPrivKey().TrySignBIP140(discreteOutcome.Hash, new PrecomputedNonceFunctionHardened(kValue.ToECPrivKey().ToBytes()), out var sig); var oracleAttestation = new Key(sig !.s.ToBytes()); if (await Repository.AddAttestation(evt, oracleAttestation) != outcome) { throw new InvalidOperationException("Error while validating reveal"); } context.Console.Out.Write(oracleAttestation.ToHex()); }
public override ContractInfo ReadJson(JsonReader reader, Type objectType, [AllowNull] ContractInfo existingValue, bool hasExistingValue, JsonSerializer serializer) { if (reader.TokenType != JsonToken.StartObject) { throw new JsonObjectException("ContractInfo is expecting a json object", reader); } var cd = serializer.Deserialize <ContractData>(reader); if (cd?.Sats is null || cd?.SHA256 is null) { throw new JsonObjectException("Invalid contract info (missing fields)", reader); } DiscreteOutcome outcome = new DiscreteOutcome(Encoders.Hex.DecodeData(cd.SHA256)); return(new ContractInfo(outcome, cd.Sats)); }
public bool TryComputeSigpoint(DiscreteOutcome outcome, out ECPubKey?sigpoint) { return(PubKey.TryComputeSigPoint(outcome.Hash, RValue, out sigpoint)); }
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); }
public ContractInfo(DiscreteOutcome outcome, Money payout) { Payout = payout; Outcome = outcome; }