コード例 #1
0
ファイル: AliceClient.cs プロジェクト: soosr/WalletWasabi
    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;
    }
コード例 #2
0
        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));
        }
コード例 #3
0
 public Alice(Coin coin, OwnershipProof ownershipProof)
 {
     // TODO init syntax?
     Coin           = coin;
     OwnershipProof = ownershipProof;
     Id             = CalculateHash();
 }
コード例 #4
0
 public Alice(Coin coin, OwnershipProof ownershipProof, Round round)
 {
     // TODO init syntax?
     Round          = round;
     Coin           = coin;
     OwnershipProof = ownershipProof;
     Id             = CalculateHash();
 }
コード例 #5
0
        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);
        }
コード例 #6
0
ファイル: Alice.cs プロジェクト: benthecarman/WalletWasabi
 public Alice(Coin coin, OwnershipProof ownershipProof, Round round, Guid id)
 {
     // TODO init syntax?
     Round          = round;
     Coin           = coin;
     OwnershipProof = ownershipProof;
     Id             = id;
 }
コード例 #7
0
 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;
 }
コード例 #8
0
        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));
        }
コード例 #9
0
        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()));
        }
コード例 #10
0
    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);
    }
コード例 #11
0
        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()));
        }
コード例 #12
0
    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);
    }
コード例 #13
0
    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()));
    }
コード例 #14
0
    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);
    }
コード例 #15
0
    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);
    }
コード例 #16
0
        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()));
        }
コード例 #17
0
        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));
        }
コード例 #18
0
        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));
            }
        }
コード例 #19
0
        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()));
        }
コード例 #20
0
 /// <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));
コード例 #21
0
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var value = (string)reader.Value;

        return(OwnershipProof.FromBytes(ByteHelpers.FromHex(value)));
    }
コード例 #22
0
 static byte[] GenerateOwnershipProof(Key key, uint256 roundHash) => OwnershipProof.GenerateCoinJoinInputProof(
     key,
     new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", roundHash)).ToBytes();
コード例 #23
0
 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();