예제 #1
0
        /// <inheritdoc />
        public Transaction BuildWithdrawalTransaction(uint256 depositId, uint blockTime, Recipient recipient)
        {
            try
            {
                this.logger.LogDebug("BuildDeterministicTransaction depositId(opReturnData)={0} recipient.ScriptPubKey={1} recipient.Amount={2}", depositId, recipient.ScriptPubKey, recipient.Amount);

                // Build the multisig transaction template.
                uint256 opReturnData   = depositId;
                string  walletPassword = this.federationWalletManager.Secret.WalletPassword;
                bool    sign           = (walletPassword ?? "") != "";

                var multiSigContext = new TransactionBuildContext(new List <Recipient>(), opReturnData: opReturnData.ToBytes())
                {
                    MinConfirmations = MinConfirmations,
                    Shuffle          = false,
                    IgnoreVerify     = true,
                    WalletPassword   = walletPassword,
                    Sign             = sign,
                    Time             = this.network.Consensus.IsProofOfStake ? blockTime : (uint?)null
                };

                multiSigContext.Recipients = new List <Recipient> {
                    recipient.WithPaymentReducedByFee(FederatedPegSettings.CrossChainTransferFee)
                };                                                                                                                                  // The fee known to the user is taken.

                // TODO: Amend this so we're not picking coins twice.
                (List <Coin> coins, List <Wallet.UnspentOutputReference> unspentOutputs) = FederationWalletTransactionHandler.DetermineCoins(this.federationWalletManager, this.network, multiSigContext, this.federatedPegSettings);

                if (coins.Count > FederatedPegSettings.MaxInputs)
                {
                    this.logger.LogDebug("Too many inputs. Triggering the consolidation process.");
                    this.signals.Publish(new WalletNeedsConsolidation(recipient.Amount));
                    this.logger.LogTrace("(-)[CONSOLIDATING_INPUTS]");
                    return(null);
                }

                multiSigContext.TransactionFee   = this.federatedPegSettings.GetWithdrawalTransactionFee(coins.Count); // The "actual fee". Everything else goes to the fed.
                multiSigContext.SelectedInputs   = unspentOutputs.Select(u => u.ToOutPoint()).ToList();
                multiSigContext.AllowOtherInputs = false;

                // Build the transaction.
                Transaction transaction = this.federationWalletTransactionHandler.BuildTransaction(multiSigContext);

                this.logger.LogDebug("transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer));

                return(transaction);
            }
            catch (Exception error)
            {
                if (error is WalletException walletException &&
                    (walletException.Message == FederationWalletTransactionHandler.NoSpendableTransactionsMessage ||
                     walletException.Message == FederationWalletTransactionHandler.NotEnoughFundsMessage))
                {
                    this.logger.LogWarning("Not enough spendable transactions in the wallet. Should be resolved when a pending transaction is included in a block.");
                }
예제 #2
0
        /// <inheritdoc />
        public Transaction BuildWithdrawalTransaction(uint256 depositId, uint blockTime, Recipient recipient)
        {
            try
            {
                this.logger.LogInformation("BuildDeterministicTransaction depositId(opReturnData)={0} recipient.ScriptPubKey={1} recipient.Amount={2}", depositId, recipient.ScriptPubKey, recipient.Amount);

                // Build the multisig transaction template.
                uint256 opReturnData    = depositId;
                string  walletPassword  = this.federationWalletManager.Secret.WalletPassword;
                bool    sign            = (walletPassword ?? "") != "";
                var     multiSigContext = new TransactionBuildContext(new[]
                {
                    recipient.WithPaymentReducedByFee(this.federationGatewaySettings.TransactionFee)
                }.ToList(), opReturnData: opReturnData.ToBytes())
                {
                    TransactionFee   = this.federationGatewaySettings.TransactionFee,
                    MinConfirmations = MinConfirmations,
                    Shuffle          = false,
                    IgnoreVerify     = true,
                    WalletPassword   = walletPassword,
                    Sign             = sign,
                    Time             = this.network.Consensus.IsProofOfStake ? blockTime : (uint?)null
                };

                // Build the transaction.
                Transaction transaction = this.federationWalletTransactionHandler.BuildTransaction(multiSigContext);

                this.logger.LogInformation("transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer));

                return(transaction);
            }
            catch (Exception error)
            {
                if (error is WalletException walletException &&
                    (walletException.Message == FederationWalletTransactionHandler.NoSpendableTransactionsMessage ||
                     walletException.Message == FederationWalletTransactionHandler.NotEnoughFundsMessage))
                {
                    this.logger.LogWarning("Not enough spendable transactions in the wallet. Should be resolved when a pending transaction is included in a block.");
                }
        /// <inheritdoc />
        public Transaction BuildWithdrawalTransaction(int blockHeight, uint256 depositId, uint blockTime, Recipient recipient)
        {
            try
            {
                this.logger.LogDebug("BuildDeterministicTransaction depositId(opReturnData)={0}; recipient.ScriptPubKey={1}; recipient.Amount={2}; height={3}", depositId, recipient.ScriptPubKey, recipient.Amount, blockHeight);

                // Build the multisig transaction template.
                uint256 opReturnData   = depositId;
                string  walletPassword = this.federationWalletManager.Secret.WalletPassword;
                bool    sign           = (walletPassword ?? "") != "";

                var multiSigContext = new TransactionBuildContext(new List <Recipient>(), opReturnData: opReturnData.ToBytes())
                {
                    MinConfirmations = MinConfirmations,
                    Shuffle          = false,
                    IgnoreVerify     = true,
                    WalletPassword   = walletPassword,
                    Sign             = sign,
                    Time             = this.network.Consensus.IsProofOfStake ? blockTime : (uint?)null
                };

                // Withdrawals from the sidechain won't have the OP_RETURN transaction tag, so we need to check against the ScriptPubKey of the Cirrus Dummy address.
                if (!this.federatedPegSettings.IsMainChain && recipient.ScriptPubKey.Length > 0 && recipient.ScriptPubKey == BitcoinAddress.Create(this.network.CirrusRewardDummyAddress).ScriptPubKey)
                {
                    // Use the distribution manager to determine the actual list of recipients.
                    // TODO: This would probably be neater if it was moved to the CCTS with the current method accepting a list of recipients instead
                    multiSigContext.Recipients = this.distributionManager.Distribute(blockHeight, recipient.WithPaymentReducedByFee(FederatedPegSettings.CrossChainTransferFee).Amount); // Reduce the overall amount by the fee first before splitting it up.

                    // This should never happen as we should always have at least one federation member with a configured wallet.
                    if (multiSigContext.Recipients.Count == 0)
                    {
                        this.logger.LogWarning("Could not identify recipients for the distribution transaction, adding dummy recipient to avoid the CCTS suspending irrecoverably.");
                        multiSigContext.Recipients = new List <Recipient> {
                            recipient.WithPaymentReducedByFee(FederatedPegSettings.CrossChainTransferFee)
                        };
                    }
                }
                else
                {
                    multiSigContext.Recipients = new List <Recipient> {
                        recipient.WithPaymentReducedByFee(FederatedPegSettings.CrossChainTransferFee)
                    };                                                                                                                                  // The fee known to the user is taken.
                }

                // TODO: Amend this so we're not picking coins twice.
                (List <Coin> coins, List <Wallet.UnspentOutputReference> unspentOutputs) = FederationWalletTransactionHandler.DetermineCoins(this.federationWalletManager, this.network, multiSigContext, this.federatedPegSettings);

                if (coins.Count > FederatedPegSettings.MaxInputs)
                {
                    this.logger.LogDebug("Too many inputs. Triggering the consolidation process.");
                    this.signals.Publish(new WalletNeedsConsolidation(recipient.Amount));
                    this.logger.LogTrace("(-)[CONSOLIDATING_INPUTS]");
                    return(null);
                }

                multiSigContext.TransactionFee   = this.federatedPegSettings.GetWithdrawalTransactionFee(coins.Count); // The "actual fee". Everything else goes to the fed.
                multiSigContext.SelectedInputs   = unspentOutputs.Select(u => u.ToOutPoint()).ToList();
                multiSigContext.AllowOtherInputs = false;

                // Build the transaction.
                Transaction transaction = this.federationWalletTransactionHandler.BuildTransaction(multiSigContext);

                this.logger.LogDebug("transaction = {0}", transaction.ToString(this.network, RawFormat.BlockExplorer));

                return(transaction);
            }
            catch (Exception error)
            {
                if (error is WalletException walletException &&
                    (walletException.Message == FederationWalletTransactionHandler.NoSpendableTransactionsMessage ||
                     walletException.Message == FederationWalletTransactionHandler.NotEnoughFundsMessage))
                {
                    this.logger.LogWarning("Not enough spendable transactions in the wallet. Should be resolved when a pending transaction is included in a block.");
                }