private AliceClient( Guid aliceId, RoundState roundState, ArenaClient arenaClient, SmartCoin coin, OwnershipProof ownershipProof, IEnumerable <Credential> issuedAmountCredentials, IEnumerable <Credential> issuedVsizeCredentials, bool isPayingZeroCoordinationFee) { var roundParameters = roundState.CoinjoinState.Parameters; AliceId = aliceId; RoundId = roundState.Id; ArenaClient = arenaClient; SmartCoin = coin; OwnershipProof = ownershipProof; FeeRate = roundParameters.MiningFeeRate; CoordinationFeeRate = roundParameters.CoordinationFeeRate; IssuedAmountCredentials = issuedAmountCredentials; IssuedVsizeCredentials = issuedVsizeCredentials; MaxVsizeAllocationPerAlice = roundParameters.MaxVsizeAllocationPerAlice; ConfirmationTimeout = roundParameters.ConnectionConfirmationTimeout / 2; IsPayingZeroCoordinationFee = isPayingZeroCoordinationFee; }
public async Task <ArenaResponse <Guid> > RegisterInputAsync( uint256 roundId, OutPoint outPoint, Key key, CancellationToken cancellationToken) { var ownershipProof = OwnershipProof.GenerateCoinJoinInputProof( key, new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundId)); var zeroAmountCredentialRequestData = AmountCredentialClient.CreateRequestForZeroAmount(); var zeroVsizeCredentialRequestData = VsizeCredentialClient.CreateRequestForZeroAmount(); var inputRegistrationResponse = await RequestHandler.RegisterInputAsync( new InputRegistrationRequest( roundId, outPoint, ownershipProof, zeroAmountCredentialRequestData.CredentialsRequest, zeroVsizeCredentialRequestData.CredentialsRequest), cancellationToken).ConfigureAwait(false); var realAmountCredentials = AmountCredentialClient.HandleResponse(inputRegistrationResponse.AmountCredentials, zeroAmountCredentialRequestData.CredentialsResponseValidation); var realVsizeCredentials = VsizeCredentialClient.HandleResponse(inputRegistrationResponse.VsizeCredentials, zeroVsizeCredentialRequestData.CredentialsResponseValidation); return(new(inputRegistrationResponse.AliceId, realAmountCredentials, realVsizeCredentials)); }
public Alice(Coin coin, OwnershipProof ownershipProof) { // TODO init syntax? Coin = coin; OwnershipProof = ownershipProof; Id = CalculateHash(); }
public Alice(Coin coin, OwnershipProof ownershipProof, Round round) { // TODO init syntax? Round = round; Coin = coin; OwnershipProof = ownershipProof; Id = CalculateHash(); }
private static uint256 CalculateHash(Coin coin, BitcoinSecret bitcoinSecret, uint256 roundId) { var ownershipProof = OwnershipProof.GenerateCoinJoinInputProof( bitcoinSecret.PrivateKey, new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundId)); return(new Alice(coin, ownershipProof).Id); }
public Alice(Coin coin, OwnershipProof ownershipProof, Round round, Guid id) { // TODO init syntax? Round = round; Coin = coin; OwnershipProof = ownershipProof; Id = id; }
public Alice(Coin coin, OwnershipProof ownershipProof, Round round, Guid id, bool isPayingZeroCoordinationFee) { // TODO init syntax? Round = round; Coin = coin; OwnershipProof = ownershipProof; Id = id; IsPayingZeroCoordinationFee = isPayingZeroCoordinationFee; }
public void OwnershipProofVerification() { // [all all all all all all all all all all all all]/84'/0'/0'/1/0 using var key = new Key(Encoders.Hex.DecodeData("3460814214450E864EC722FF1F84F96C41746CD6BBE2F1C09B33972761032E9F")); var ownershipIdentifier = new OwnershipIdentifier(Encoders.Hex.DecodeData("A122407EFC198211C81AF4450F40B235D54775EFD934D16B9E31C6CE9BAD5707")); var commitmentData = Encoders.Hex.DecodeData("A42E38EF564D4B05B65575D22553BB1F264332D77F8A61159ABF3E6179B0317C"); var scriptPubKey = PayToWitPubKeyHashTemplate.Instance.GenerateScriptPubKey(key.PubKey); OwnershipProof ownershipProof, invalidOwnershipProof; // Valid proofs ownershipProof = OwnershipProof.Generate(key, ownershipIdentifier, commitmentData, true, ScriptPubKeyType.Segwit); Assert.True(ownershipProof.VerifyOwnership(scriptPubKey, commitmentData, false)); Assert.True(ownershipProof.VerifyOwnership(scriptPubKey, commitmentData, true)); ownershipProof = OwnershipProof.Generate(key, ownershipIdentifier, commitmentData, false, ScriptPubKeyType.Segwit); Assert.True(ownershipProof.VerifyOwnership(scriptPubKey, commitmentData, false)); // Valid proof, modified user confirmation flag Assert.False(ownershipProof.VerifyOwnership(scriptPubKey, commitmentData, true)); // Valid proof, modified commitment data var invalidCommitmentData = commitmentData.ToArray(); invalidCommitmentData[0] ^= 1; Assert.False(ownershipProof.VerifyOwnership(scriptPubKey, invalidCommitmentData, false)); // Valid proof, invalid scriptPubKey // [all all all all all all all all all all all all]/84'/0'/0'/1/1 var invalidKey = new Key(Encoders.Hex.DecodeData("7b041dd735e7202d3c1b9592147894ed24da6355f0cd66573c273c0df1afa78a")); var invalidScriptPubKey = PayToWitPubKeyHashTemplate.Instance.GenerateScriptPubKey(invalidKey.PubKey); Assert.False(ownershipProof.VerifyOwnership(invalidScriptPubKey, commitmentData, false)); // Invalid proof, modified ownership identifier var invalidOwnershipIdentifierBytes = ownershipIdentifier.ToBytes(); invalidOwnershipIdentifierBytes[0] ^= 1; invalidOwnershipProof = new OwnershipProof( new ProofBody(ownershipProof.ProofBody.Flags, new OwnershipIdentifier(invalidOwnershipIdentifierBytes)), ownershipProof.ProofSignature); Assert.False(invalidOwnershipProof.VerifyOwnership(scriptPubKey, commitmentData, false)); // Invalid proof, modified r part of signature in witness var invalidSignature = ownershipProof.ProofSignature.Witness[0].ToArray(); invalidSignature[4] ^= 1; invalidOwnershipProof = new OwnershipProof( ownershipProof.ProofBody, new Bip322Signature(ownershipProof.ProofSignature.ScriptSig, new WitScript(new byte[][] { invalidSignature, ownershipProof.ProofSignature.Witness[1] }))); Assert.False(invalidOwnershipProof.VerifyOwnership(scriptPubKey, commitmentData, false)); }
public static InputRoundSignaturePair CreateInputRoundSignaturePair(Key?key = null, uint256?roundHash = null) { var rh = roundHash ?? BitcoinFactory.CreateUint256(); var outpoint = BitcoinFactory.CreateOutPoint(); var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", rh); var signingKey = key ?? new(); return(new InputRoundSignaturePair( outpoint, OwnershipProof.GenerateCoinJoinInputProof(signingKey, coinJoinInputCommitmentData).ToBytes())); }
public OwnershipProof GetOwnershipProof(IDestination destination, CoinJoinInputCommitmentData commitmentData) { var secret = GetBitcoinSecret(destination.ScriptPubKey); var masterKey = KeyManager.GetMasterExtKey(Kitchen.SaltSoup()).PrivateKey; var identificationMasterKey = Slip21Node.FromSeed(masterKey.ToBytes()); var identificationKey = identificationMasterKey.DeriveChild("SLIP-0019").DeriveChild("Ownership identification key").Key; var signingKey = secret.PrivateKey; var ownershipProof = OwnershipProof.GenerateCoinJoinInputProof( signingKey, new OwnershipIdentifier(identificationKey, destination.ScriptPubKey), commitmentData); return(ownershipProof); }
public void OwnershipProofEncodingDecoding() { // Trezor test vector // [all all all all all all all all all all all all]/84'/0'/0'/1/0 using var key = new Key(Encoders.Hex.DecodeData("3460814214450E864EC722FF1F84F96C41746CD6BBE2F1C09B33972761032E9F")); var ownershipIdentifier = new OwnershipIdentifier(Encoders.Hex.DecodeData("A122407EFC198211C81AF4450F40B235D54775EFD934D16B9E31C6CE9BAD5707")); var commitmentData = Array.Empty <byte>(); var ownershipProof = OwnershipProof.Generate(key, ownershipIdentifier, commitmentData, false, ScriptPubKeyType.Segwit); var scriptPubKey = PayToWitPubKeyHashTemplate.Instance.GenerateScriptPubKey(key.PubKey); var ownershipProofBytes = Encoders.Hex.DecodeData("534c00190001a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad57070002483045022100e5eaf2cb0a473b4545115c7b85323809e75cb106175ace38129fd62323d73df30220363dbc7acb7afcda022b1f8d97acb8f47c42043cfe0595583aa26e30bc8b3bb50121032ef68318c8f6aaa0adec0199c69901f0db7d3485eb38d9ad235221dc3d61154b"); Assert.True(ownershipProofBytes.SequenceEqual(ownershipProof.ToBytes())); var deserializedOwnershipProof = OwnershipProof.FromBytes(ownershipProofBytes); Assert.True(ownershipProofBytes.SequenceEqual(deserializedOwnershipProof.ToBytes())); }
public Transaction Sign(Transaction transaction, Coin coin, OwnershipProof ownershipProof) { transaction = transaction.Clone(); if (transaction.Inputs.Count == 0) { throw new ArgumentException("No inputs to sign.", nameof(transaction)); } var txInput = transaction.Inputs.AsIndexedInputs().FirstOrDefault(input => input.PrevOut == coin.Outpoint); if (txInput is null) { throw new InvalidOperationException("Missing input."); } var secret = GetBitcoinSecret(coin.ScriptPubKey); transaction.Sign(secret, coin); return(transaction); }
public void OwnershipProofEncodingDecoding() { // Trezor test vector // [all all all all all all all all all all all all]/84'/0'/0'/1/0 var allMnemonic = new Mnemonic("all all all all all all all all all all all all"); var identificationMasterKey = Slip21Node.FromSeed(allMnemonic.DeriveSeed()); var identificationKey = identificationMasterKey.DeriveChild("SLIP-0019").DeriveChild("Ownership identification key").Key; using var key = new Key(Encoders.Hex.DecodeData("3460814214450E864EC722FF1F84F96C41746CD6BBE2F1C09B33972761032E9F")); var ownershipIdentifier = new OwnershipIdentifier(identificationKey, key.PubKey.WitHash.ScriptPubKey); var commitmentData = Array.Empty <byte>(); var ownershipProof = OwnershipProof.Generate(key, ownershipIdentifier, commitmentData, false, ScriptPubKeyType.Segwit); var scriptPubKey = PayToWitPubKeyHashTemplate.Instance.GenerateScriptPubKey(key.PubKey); var ownershipProofBytes = Encoders.Hex.DecodeData("534c00190001a122407efc198211c81af4450f40b235d54775efd934d16b9e31c6ce9bad57070002483045022100c0dc28bb563fc5fea76cacff75dba9cb4122412faae01937cdebccfb065f9a7002202e980bfbd8a434a7fc4cd2ca49da476ce98ca097437f8159b1a386b41fcdfac50121032ef68318c8f6aaa0adec0199c69901f0db7d3485eb38d9ad235221dc3d61154b"); Assert.True(ownershipProofBytes.SequenceEqual(ownershipProof.ToBytes())); var deserializedOwnershipProof = OwnershipProof.FromBytes(ownershipProofBytes); Assert.True(ownershipProofBytes.SequenceEqual(deserializedOwnershipProof.ToBytes())); }
public async Task <(ArenaResponse <Guid> ArenaResponse, bool IsPayingZeroCoordinationFee)> RegisterInputAsync( uint256 roundId, OutPoint outPoint, OwnershipProof ownershipProof, CancellationToken cancellationToken) { var zeroAmountCredentialRequestData = AmountCredentialClient.CreateRequestForZeroAmount(); var zeroVsizeCredentialRequestData = VsizeCredentialClient.CreateRequestForZeroAmount(); var inputRegistrationResponse = await RequestHandler.RegisterInputAsync( new InputRegistrationRequest( roundId, outPoint, ownershipProof, zeroAmountCredentialRequestData.CredentialsRequest, zeroVsizeCredentialRequestData.CredentialsRequest), cancellationToken).ConfigureAwait(false); var realAmountCredentials = AmountCredentialClient.HandleResponse(inputRegistrationResponse.AmountCredentials, zeroAmountCredentialRequestData.CredentialsResponseValidation); var realVsizeCredentials = VsizeCredentialClient.HandleResponse(inputRegistrationResponse.VsizeCredentials, zeroVsizeCredentialRequestData.CredentialsResponseValidation); return(new(inputRegistrationResponse.AliceId, realAmountCredentials, realVsizeCredentials), inputRegistrationResponse.IsPayingZeroCoordinationFee); }
public void EqualityTest() { using Key key = new(); uint256 roundHash = BitcoinFactory.CreateUint256(); // Request #1. OwnershipProof request1 = CreateOwnershipProof(key, roundHash); // Request #2. OwnershipProof request2 = CreateOwnershipProof(key, roundHash); Assert.Equal(request1, request2); // Request #3. using Key key2 = new(); OwnershipProof request3 = CreateOwnershipProof(key2, roundHash); // Key intentionally changed. Assert.NotEqual(request1, request3); // Request #4. OwnershipProof request4 = CreateOwnershipProof(key, roundHash: BitcoinFactory.CreateUint256()); // Round hash intentionally changed. Assert.NotEqual(request1, request4); }
public static InputsRegistrationResponse RegisterInput( WabiSabiConfig config, Guid roundId, IDictionary <Coin, byte[]> coinRoundSignaturePairs, ZeroCredentialsRequest zeroAmountCredentialRequests, ZeroCredentialsRequest zeroVsizeCredentialRequests, IDictionary <Guid, Round> rounds, Network network) { if (!rounds.TryGetValue(roundId, out var round)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound); } var coins = coinRoundSignaturePairs.Select(x => x.Key); if (round.MaxInputCountByAlice < coins.Count()) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooManyInputs); } if (round.IsBlameRound && coins.Select(x => x.Outpoint).Any(x => !round.BlameWhitelist.Contains(x))) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNotWhitelisted); } foreach (var coinRoundSignaturePair in coinRoundSignaturePairs) { var coin = coinRoundSignaturePair.Key; var signature = coinRoundSignaturePair.Value; var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", round.Hash); if (!OwnershipProof.VerifyCoinJoinInputProof(signature, coin.TxOut.ScriptPubKey, coinJoinInputCommitmentData)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongRoundSignature); } } var alice = new Alice(coinRoundSignaturePairs); if (alice.TotalInputAmount < round.MinRegistrableAmount) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds); } if (alice.TotalInputAmount > round.MaxRegistrableAmount) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds); } if (alice.TotalInputVsize > round.PerAliceVsizeAllocation) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchVsize); } if (round.IsInputRegistrationEnded(config.MaxInputCountByRound, config.GetInputRegistrationTimeout(round))) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase); } if (round.RemainingInputVsizeAllocation < round.PerAliceVsizeAllocation) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchTotalWeight); } var commitAmountCredentialResponse = round.AmountCredentialIssuer.PrepareResponse(zeroAmountCredentialRequests); var commitVsizeCredentialResponse = round.VsizeCredentialIssuer.PrepareResponse(zeroVsizeCredentialRequests); RemoveDuplicateAlices(rounds, alice); alice.SetDeadlineRelativeTo(round.ConnectionConfirmationTimeout); round.Alices.Add(alice); return(new(alice.Id, commitAmountCredentialResponse.Commit(), commitVsizeCredentialResponse.Commit())); }
public static InputsRegistrationResponse RegisterInput( WabiSabiConfig config, Guid roundId, IDictionary <Coin, byte[]> coinRoundSignaturePairs, ZeroCredentialsRequest zeroAmountCredentialRequests, ZeroCredentialsRequest zeroWeightCredentialRequests, IDictionary <Guid, Round> rounds, Network network) { if (!rounds.TryGetValue(roundId, out var round)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound); } var alice = new Alice(coinRoundSignaturePairs); var coins = alice.Coins; if (round.MaxInputCountByAlice < coins.Count()) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooManyInputs); } if (round.IsBlameRound && coins.Select(x => x.Outpoint).Any(x => !round.BlameWhitelist.Contains(x))) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNotWhitelisted); } var inputValueSum = Money.Zero; var inputWeightSum = 0; foreach (var coinRoundSignaturePair in alice.CoinRoundSignaturePairs) { var coin = coinRoundSignaturePair.Key; var signature = coinRoundSignaturePair.Value; var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", round.Hash); if (!OwnershipProof.VerifyCoinJoinInputProof(signature, coin.TxOut.ScriptPubKey, coinJoinInputCommitmentData)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongRoundSignature); } inputValueSum += coin.TxOut.Value; // Convert conservative P2WPKH size in virtual bytes to weight units. inputWeightSum += coin.TxOut.ScriptPubKey.EstimateInputVsize() * 4; } if (inputValueSum < round.MinRegistrableAmount) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds); } if (inputValueSum > round.MaxRegistrableAmount) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds); } if (inputWeightSum > round.RegistrableWeightCredentials) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchWeight); } if (round.IsInputRegistrationEnded(config.MaxInputCountByRound, config.GetInputRegistrationTimeout(round))) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase); } var amountCredentialResponse = round.AmountCredentialIssuer.HandleRequest(zeroAmountCredentialRequests); var weightCredentialResponse = round.WeightCredentialIssuer.HandleRequest(zeroWeightCredentialRequests); RemoveDuplicateAlices(rounds, alice); alice.SetDeadlineRelativeTo(round.ConnectionConfirmationTimeout); round.Alices.Add(alice); return(new(alice.Id, amountCredentialResponse, weightCredentialResponse)); }
public async Task <InputRegistrationResponse> RegisterInputAsync(InputRegistrationRequest request, CancellationToken cancellationToken) { var coin = await OutpointToCoinAsync(request, cancellationToken).ConfigureAwait(false); using (await AsyncLock.LockAsync(cancellationToken).ConfigureAwait(false)) { var round = GetRound(request.RoundId); var registeredCoins = Rounds.Where(x => !(x.Phase == Phase.Ended && !x.WasTransactionBroadcast)) .SelectMany(r => r.Alices.Select(a => a.Coin)); if (registeredCoins.Any(x => x.Outpoint == coin.Outpoint)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.AliceAlreadyRegistered); } if (round.IsInputRegistrationEnded(Config.MaxInputCountByRound)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase); } if (round is BlameRound blameRound && !blameRound.BlameWhitelist.Contains(coin.Outpoint)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNotWhitelisted); } // Compute but don't commit updated CoinJoin to round state, it will // be re-calculated on input confirmation. This is computed it here // for validation purposes. _ = round.Assert <ConstructionState>().AddInput(coin); var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", round.Id); if (!OwnershipProof.VerifyCoinJoinInputProof(request.OwnershipProof, coin.TxOut.ScriptPubKey, coinJoinInputCommitmentData)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongOwnershipProof); } // Generate a new GUID with the secure random source, to be sure // that it is not guessable (Guid.NewGuid() documentation does // not say anything about GUID version or randomness source, // only that the probability of duplicates is very low). var id = new Guid(Random.GetBytes(16)); var alice = new Alice(coin, request.OwnershipProof, round, id); if (alice.TotalInputAmount < round.MinAmountCredentialValue) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds); } if (alice.TotalInputAmount > round.MaxAmountCredentialValue) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds); } if (alice.TotalInputVsize > round.MaxVsizeAllocationPerAlice) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchVsize); } var amountCredentialTask = round.AmountCredentialIssuer.HandleRequestAsync(request.ZeroAmountCredentialRequests, cancellationToken); var vsizeCredentialTask = round.VsizeCredentialIssuer.HandleRequestAsync(request.ZeroVsizeCredentialRequests, cancellationToken); if (round.RemainingInputVsizeAllocation < round.MaxVsizeAllocationPerAlice) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.VsizeQuotaExceeded); } var commitAmountCredentialResponse = await amountCredentialTask.ConfigureAwait(false); var commitVsizeCredentialResponse = await vsizeCredentialTask.ConfigureAwait(false); alice.SetDeadlineRelativeTo(round.ConnectionConfirmationTimeFrame.Duration); round.Alices.Add(alice); return(new(alice.Id, commitAmountCredentialResponse, commitVsizeCredentialResponse)); } }
public static InputRegistrationResponse RegisterInput( WabiSabiConfig config, uint256 roundId, Coin coin, OwnershipProof ownershipProof, ZeroCredentialsRequest zeroAmountCredentialRequests, ZeroCredentialsRequest zeroVsizeCredentialRequests, HashSet <Round> rounds) { if (rounds.FirstOrDefault(x => x.Id == roundId) is not Round round) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound); } if (round.IsInputRegistrationEnded(config.MaxInputCountByRound, config.GetInputRegistrationTimeout(round))) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase); } if (round.IsBlameRound && !round.BlameWhitelist.Contains(coin.Outpoint)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNotWhitelisted); } // Compute but don't commit updated CoinJoin to round state, it will // be re-calculated on input confirmation. This is computed it here // for validation purposes. round.Assert <ConstructionState>().AddInput(coin); var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", round.Id); if (!OwnershipProof.VerifyCoinJoinInputProof(ownershipProof, coin.TxOut.ScriptPubKey, coinJoinInputCommitmentData)) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongOwnershipProof); } var alice = new Alice(coin, ownershipProof); if (alice.TotalInputAmount < round.MinRegistrableAmount) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds); } if (alice.TotalInputAmount > round.MaxRegistrableAmount) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds); } if (alice.TotalInputVsize > round.MaxVsizeAllocationPerAlice) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchVsize); } if (round.RemainingInputVsizeAllocation < round.MaxVsizeAllocationPerAlice) { throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.VsizeQuotaExceeded); } var commitAmountCredentialResponse = round.AmountCredentialIssuer.PrepareResponse(zeroAmountCredentialRequests); var commitVsizeCredentialResponse = round.VsizeCredentialIssuer.PrepareResponse(zeroVsizeCredentialRequests); alice.SetDeadlineRelativeTo(round.ConnectionConfirmationTimeout); round.Alices.Add(alice); return(new(alice.Id, commitAmountCredentialResponse.Commit(), commitVsizeCredentialResponse.Commit())); }
/// <remarks>Each instance represents the same proof but a new object instance.</remarks> public static OwnershipProof CreateOwnershipProof(Key key, uint256 roundHash) => OwnershipProof.GenerateCoinJoinInputProof( key, new OwnershipIdentifier(key, key.PubKey.WitHash.ScriptPubKey), new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundHash));
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var value = (string)reader.Value; return(OwnershipProof.FromBytes(ByteHelpers.FromHex(value))); }
static byte[] GenerateOwnershipProof(Key key, uint256 roundHash) => OwnershipProof.GenerateCoinJoinInputProof( key, new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundHash)).ToBytes();
public static uint256 CalculateHash(Coin coin, OwnershipProof ownershipProof) => StrobeHasher.Create(ProtocolConstants.AliceStrobeDomain) .Append(ProtocolConstants.AliceCoinTxOutStrobeLabel, coin.TxOut) .Append(ProtocolConstants.AliceCoinOutpointStrobeLabel, coin.Outpoint) .Append(ProtocolConstants.AliceOwnershipProofStrobeLabel, ownershipProof) .GetHash();