public void SetProofBlob(PayoutData data, PayoutTransactionOnChainBlob blob)
    {
        var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(blob, _jsonSerializerSettings.GetSerializer(data.GetPaymentMethodId().CryptoCode)));

        // We only update the property if the bytes actually changed, this prevent from hammering the DB too much
        if (data.Proof is null || bytes.Length != data.Proof.Length || !bytes.SequenceEqual(data.Proof))
        {
            data.Proof = bytes;
        }
    }
Ejemplo n.º 2
0
    private async Task UpdatePayoutsAwaitingForPayment(NewOnChainTransactionEvent newTransaction)
    {
        try
        {
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(newTransaction.CryptoCode);
            Dictionary <string, decimal> destinations;
            if (newTransaction.NewTransactionEvent.TrackedSource is AddressTrackedSource addressTrackedSource)
            {
                destinations = new Dictionary <string, decimal>()
                {
                    {
                        addressTrackedSource.Address.ToString(),
                            newTransaction.NewTransactionEvent.Outputs.Sum(output => output.Value.GetValue(network))
                    }
                };
            }
            else
            {
                destinations = newTransaction.NewTransactionEvent.TransactionData.Transaction.Outputs
                               .GroupBy(txout => txout.ScriptPubKey)
                               .ToDictionary(
                    txoutSet => txoutSet.Key.GetDestinationAddress(network.NBitcoinNetwork).ToString(),
                    txoutSet => txoutSet.Sum(txout => txout.Value.ToDecimal(MoneyUnit.BTC)));
            }

            var paymentMethodId = new PaymentMethodId(newTransaction.CryptoCode, BitcoinPaymentType.Instance);

            using var ctx = _dbContextFactory.CreateContext();
            var payouts = await ctx.Payouts
                          .Include(o => o.PullPaymentData)
                          .Where(p => p.State == PayoutState.AwaitingPayment)
                          .Where(p => p.PaymentMethodId == paymentMethodId.ToString())
                          .Where(p => destinations.Keys.Contains(p.Destination))
                          .ToListAsync();

            var payoutByDestination = payouts.ToDictionary(p => p.Destination);
            foreach (var destination in destinations)
            {
                if (!payoutByDestination.TryGetValue(destination.Key, out var payout))
                {
                    continue;
                }
                var payoutBlob = payout.GetBlob(_jsonSerializerSettings);
                if (payoutBlob.CryptoAmount is null ||
                    // The round up here is not strictly necessary, this is temporary to fix existing payout before we
                    // were properly roundup the crypto amount
                    destination.Value != BTCPayServer.Extensions.RoundUp(payoutBlob.CryptoAmount.Value, network.Divisibility))
                {
                    continue;
                }
                var proof = ParseProof(payout) as PayoutTransactionOnChainBlob;
                if (proof is null)
                {
                    proof = new PayoutTransactionOnChainBlob()
                    {
                        Accounted = !(newTransaction.NewTransactionEvent.TrackedSource is AddressTrackedSource),
                    };
                }
                var txId = newTransaction.NewTransactionEvent.TransactionData.TransactionHash;
                if (proof.Candidates.Add(txId))
                {
                    if (proof.Accounted is true)
                    {
                        payout.State = PayoutState.InProgress;
                        var walletId = new WalletId(payout.PullPaymentData.StoreId, newTransaction.CryptoCode);
                        _eventAggregator.Publish(new UpdateTransactionLabel(walletId,
                                                                            newTransaction.NewTransactionEvent.TransactionData.TransactionHash,
                                                                            UpdateTransactionLabel.PayoutTemplate(payout.Id, payout.PullPaymentDataId, walletId.ToString())));
                    }
                    if (proof.TransactionId is null)
                    {
                        proof.TransactionId = txId;
                    }
                    SetProofBlob(payout, proof);
                }
            }

            await ctx.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            Logs.PayServer.LogWarning(ex, "Error while processing a transaction in the pull payment hosted service");
        }
    }