public IActionResult GetGeneralInfo()
        {
            try
            {
                FederationWallet wallet = this.walletManager.GetWallet();

                if (wallet == null)
                {
                    return(this.NotFound("No federation wallet found."));
                }

                var model = new WalletGeneralInfoModel
                {
                    Network               = wallet.Network,
                    CreationTime          = wallet.CreationTime,
                    LastBlockSyncedHeight = wallet.LastBlockSyncedHeight,
                    ConnectedNodes        = this.connectionManager.ConnectedPeers.Count(),
                    ChainTip              = this.chainIndexer.Tip.Height,
                    IsChainSynced         = this.chainIndexer.IsDownloaded(),
                    IsDecrypted           = true
                };

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
        private void AddInlineStats(StringBuilder benchLogs)
        {
            if (this.federationWalletManager == null)
            {
                return;
            }
            int           height    = this.federationWalletManager.LastBlockHeight();
            ChainedHeader block     = this.chain.GetBlock(height);
            uint256       hashBlock = block == null ? 0 : block.HashBlock;

            FederationWallet federationWallet = this.federationWalletManager.GetWallet();

            benchLogs.AppendLine("Fed. Wallet.Height: ".PadRight(LoggingConfiguration.ColumnLength + 1) +
                                 (federationWallet != null ? height.ToString().PadRight(8) : "No Wallet".PadRight(8)) +
                                 (federationWallet != null ? (" Fed. Wallet.Hash: ".PadRight(LoggingConfiguration.ColumnLength - 1) + hashBlock) : string.Empty));

            benchLogs.AppendLine(
                "NodeStore.Height: ".PadRight(LoggingConfiguration.ColumnLength + 1) +
                this.crossChainTransferStore.TipHashAndHeight.Height.ToString().PadRight(9) +
                "NodeStore.Hash: ".PadRight(LoggingConfiguration.ColumnLength - 2) +
                this.crossChainTransferStore.TipHashAndHeight.HashBlock.ToString() + "  " +
                "NodeStore.NextDepositHeight: ".PadRight(LoggingConfiguration.ColumnLength + 1) +
                this.crossChainTransferStore.NextMatureDepositHeight.ToString().PadRight(8) +
                "NodeStore.HasSuspended: ".PadRight(LoggingConfiguration.ColumnLength + 1) +
                this.crossChainTransferStore.HasSuspended().ToString().PadRight(8)
                );
        }
        public IActionResult GetBalance()
        {
            try
            {
                FederationWallet wallet = this.walletManager.GetWallet();
                if (wallet == null)
                {
                    return(this.NotFound("No federation wallet found."));
                }

                (Money ConfirmedAmount, Money UnConfirmedAmount)result = this.walletManager.GetSpendableAmount();

                var balance = new AccountBalanceModel
                {
                    CoinType          = this.coinType,
                    AmountConfirmed   = result.ConfirmedAmount,
                    AmountUnconfirmed = result.UnConfirmedAmount,
                };

                var model = new WalletBalanceModel();
                model.AccountsBalances.Add(balance);

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
Esempio n. 4
0
        public FederationWalletControllerTests()
        {
            this.loggerFactory     = Substitute.For <ILoggerFactory>();
            this.walletManager     = Substitute.For <IFederationWalletManager>();
            this.walletSyncManager = Substitute.For <IFederationWalletSyncManager>();
            this.connectionManager = Substitute.For <IConnectionManager>();
            this.network           = new StratisTest();

            this.chainIndexer = new ChainIndexer(this.network);

            ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(100, ChainedHeadersHelper.CreateGenesisChainedHeader(this.network), true, null, this.network).Last();

            this.chainIndexer.SetTip(tip);


            this.dateTimeProvider          = Substitute.For <IDateTimeProvider>();
            this.withdrawalHistoryProvider = Substitute.For <IWithdrawalHistoryProvider>();

            this.controller = new FederationWalletController(this.loggerFactory, this.walletManager, this.walletSyncManager,
                                                             this.connectionManager, this.network, this.chainIndexer, this.dateTimeProvider, this.withdrawalHistoryProvider);

            this.fedWallet         = new FederationWallet();
            this.fedWallet.Network = this.network;
            this.fedWallet.LastBlockSyncedHeight = 999;
            this.fedWallet.CreationTime          = DateTimeOffset.Now;

            this.walletManager.GetWallet().Returns(this.fedWallet);
        }
        /// <summary>
        /// Initializes the cross-chain transfer tests.
        /// </summary>
        /// <param name="network">The network to run the tests for.</param>
        public CrossChainTestBase(Network network = null, Network counterChainNetwork = null)
        {
            this.network             = network ?? FederatedPegNetwork.NetworksSelector.Regtest();
            this.counterChainNetwork = counterChainNetwork ?? Networks.Stratis.Regtest();
            this.federatedPegOptions = new FederatedPegOptions(counterChainNetwork);

            NetworkRegistration.Register(this.network);

            this.loggerFactory = Substitute.For <ILoggerFactory>();
            this.nodeLifetime  = new NodeLifetime();
            this.logger        = Substitute.For <ILogger>();
            this.signals       = Substitute.For <ISignals>();
            this.asyncProvider = new AsyncProvider(this.loggerFactory, this.signals, this.nodeLifetime);
            this.loggerFactory.CreateLogger(null).ReturnsForAnyArgs(this.logger);
            this.dateTimeProvider                   = DateTimeProvider.Default;
            this.opReturnDataReader                 = new OpReturnDataReader(this.loggerFactory, this.federatedPegOptions);
            this.blockRepository                    = Substitute.For <IBlockRepository>();
            this.fullNode                           = Substitute.For <IFullNode>();
            this.withdrawalTransactionBuilder       = Substitute.For <IWithdrawalTransactionBuilder>();
            this.federationWalletManager            = Substitute.For <IFederationWalletManager>();
            this.federationWalletSyncManager        = Substitute.For <IFederationWalletSyncManager>();
            this.FederationWalletTransactionHandler = Substitute.For <IFederationWalletTransactionHandler>();
            this.walletFeePolicy                    = Substitute.For <IWalletFeePolicy>();
            this.connectionManager                  = Substitute.For <IConnectionManager>();
            this.dBreezeSerializer                  = new DBreezeSerializer(this.network.Consensus.ConsensusFactory);
            this.ibdState                           = Substitute.For <IInitialBlockDownloadState>();
            this.wallet = null;
            this.federationGatewaySettings = Substitute.For <IFederationGatewaySettings>();
            this.ChainIndexer = new ChainIndexer(this.network);

            this.federationGatewaySettings.TransactionFee.Returns(new Money(0.01m, MoneyUnit.BTC));

            // Generate the keys used by the federation members for our tests.
            this.federationKeys = new[]
            {
                "ensure feel swift crucial bridge charge cloud tell hobby twenty people mandate",
                "quiz sunset vote alley draw turkey hill scrap lumber game differ fiction",
                "exchange rent bronze pole post hurry oppose drama eternal voice client state"
            }.Select(m => HdOperations.GetExtendedKey(m)).ToArray();

            SetExtendedKey(0);

            this.fundingTransactions = new List <Transaction>();

            this.blockDict = new Dictionary <uint256, Block>();
            this.blockDict[this.network.GenesisHash] = this.network.GetGenesis();

            this.blockRepository.GetBlocks(Arg.Any <List <uint256> >()).ReturnsForAnyArgs((x) =>
            {
                var hashes = x.ArgAt <List <uint256> >(0);
                var blocks = new List <Block>();
                for (int i = 0; i < hashes.Count; i++)
                {
                    blocks.Add(this.blockDict.TryGetValue(hashes[i], out Block block) ? block : null);
                }

                return(blocks);
            });
        }
Esempio n. 6
0
        private IEnumerable <Transaction> CompletedTransactions(IEnumerable <Transaction> transactionsToCheck)
        {
            FederationWallet     wallet    = this.federationWalletManager.GetWallet();
            MultiSigTransactions walletTxs = wallet.MultiSigAddress.Transactions;

            HashSet <uint256> spendingTransactions = walletTxs
                                                     .Where(t => t.SpendingDetails?.BlockHeight > 0)
                                                     .Select(t => t.SpendingDetails?.TransactionId)
                                                     .ToHashSet();

            foreach (Transaction tx in transactionsToCheck)
            {
                uint256 hash = tx.GetHash();

                // If there is a spendable output for this tx and it has a block height then the tx is in completed state.
                if (walletTxs.TryGetTransaction(hash, 0, out TransactionData tData) && tData.BlockHeight > 0)
                {
                    yield return(tx);
                }
                // If this is a confirmed spending transaction then it is in completed state.
                else if (spendingTransactions.Contains(hash))
                {
                    yield return(tx);
                }
                // If the tx has an input that is consumed by another confirmed transaction then it should be removed.
                else
                {
                    bool bDone = false;

                    foreach (TxIn txIn in tx.Inputs)
                    {
                        // Find the input's UTXO.
                        if (walletTxs.TryGetTransaction(txIn.PrevOut.Hash, (int)txIn.PrevOut.N, out TransactionData tData2))
                        {
                            // Check if the input's UTXO is being spent by another confirmed transaction.
                            if (tData2.SpendingDetails?.BlockHeight > 0 && tData2.SpendingDetails?.TransactionId != hash)
                            {
                                bDone = true;
                                break;
                            }
                        }
                    }

                    if (bDone)
                    {
                        yield return(tx);
                    }
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Checks that the redeem script and the multisig address in the multisig wallet match the values provided in the federated peg settings.
        /// </summary>
        private void CheckConfiguration()
        {
            FederationWallet wallet = this.federationWalletManager.GetWallet();

            if (wallet.MultiSigAddress.RedeemScript != this.federatedPegSettings.MultiSigRedeemScript)
            {
                throw new ConfigurationException("Wallet redeem script does not match redeem script provided in settings. Please check that your wallet JSON file is correct and that the settings are configured correctly.");
            }

            if (wallet.MultiSigAddress.Address != this.federatedPegSettings.MultiSigAddress.ToString())
            {
                throw new ConfigurationException(
                          "Wallet multisig address does not match multisig address of the redeem script provided in settings.  Please check that your wallet JSON file is correct and that the settings are configured correctly.");
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Create the wallet manager and wallet transaction handler.
        /// </summary>
        /// <param name="dataFolder">The data folder.</param>
        protected void Init(DataFolder dataFolder)
        {
            this.dataFolder = dataFolder;

            // Create the wallet manager.
            this.federationWalletManager = new FederationWalletManager(
                this.loggerFactory,
                this.network,
                this.ChainIndexer,
                dataFolder,
                this.walletFeePolicy,
                this.asyncProvider,
                new NodeLifetime(),
                this.dateTimeProvider,
                this.federatedPegSettings,
                this.withdrawalExtractor,
                this.blockRepository);

            // Starts and creates the wallet.
            this.federationWalletManager.Start();
            this.wallet = this.federationWalletManager.GetWallet();

            // TODO: The transaction builder, cross-chain store and fed wallet tx handler should be tested individually.
            this.FederationWalletTransactionHandler = new FederationWalletTransactionHandler(this.loggerFactory, this.federationWalletManager, this.walletFeePolicy, this.network, this.federatedPegSettings);
            this.stateRepositoryRoot          = Substitute.For <IStateRepositoryRoot>();
            this.withdrawalTransactionBuilder = new WithdrawalTransactionBuilder(this.loggerFactory, this.network, this.federationWalletManager, this.FederationWalletTransactionHandler, this.federatedPegSettings, this.signals);

            var storeSettings = (StoreSettings)FormatterServices.GetUninitializedObject(typeof(StoreSettings));

            this.federationWalletSyncManager = new FederationWalletSyncManager(this.loggerFactory, this.federationWalletManager, this.ChainIndexer, this.network,
                                                                               this.blockRepository, storeSettings, Substitute.For <INodeLifetime>(), this.asyncProvider);

            this.federationWalletSyncManager.Initialize();

            // Set up the encrypted seed on the wallet.
            string encryptedSeed = this.extendedKey.PrivateKey.GetEncryptedBitcoinSecret(walletPassword, this.network).ToWif();

            this.wallet.EncryptedSeed = encryptedSeed;

            this.federationWalletManager.Secret = new WalletSecret()
            {
                WalletPassword = walletPassword
            };

            FieldInfo isFederationActiveField = this.federationWalletManager.GetType().GetField("isFederationActive", BindingFlags.NonPublic | BindingFlags.Instance);

            isFederationActiveField.SetValue(this.federationWalletManager, true);
        }
        public IActionResult RemoveTransactions([FromQuery] RemoveFederationTransactionsModel request)
        {
            Guard.NotNull(request, nameof(request));

            // Checks the request is valid.
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                HashSet <(uint256 transactionId, DateTimeOffset creationTime)> result;

                result = this.walletManager.RemoveAllTransactions();

                // If the user chose to resync the wallet after removing transactions.
                if (result.Any() && request.ReSync)
                {
                    // From the list of removed transactions, check which one is the oldest and retrieve the block right before that time.
                    DateTimeOffset earliestDate  = result.Min(r => r.creationTime);
                    ChainedHeader  chainedHeader = this.chainIndexer.GetHeader(this.chainIndexer.GetHeightAtTime(earliestDate.DateTime));

                    // Update the wallet and save it to the file system.
                    FederationWallet wallet = this.walletManager.GetWallet();
                    wallet.LastBlockSyncedHeight = chainedHeader.Height;
                    wallet.LastBlockSyncedHash   = chainedHeader.HashBlock;
                    this.walletManager.SaveWallet();

                    // Initialize the syncing process from the block before the earliest transaction was seen.
                    this.walletSyncManager.SyncFromHeight(chainedHeader.Height - 1);
                }

                IEnumerable <RemovedTransactionModel> model = result.Select(r => new RemovedTransactionModel
                {
                    TransactionId = r.transactionId,
                    CreationTime  = r.creationTime
                });

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
Esempio n. 10
0
        private void AddInlineStats(StringBuilder benchLogs)
        {
            if (this.federationWalletManager == null)
            {
                return;
            }

            int           height    = this.federationWalletManager.LastBlockHeight();
            ChainedHeader block     = this.chainIndexer.GetHeader(height);
            uint256       hashBlock = block == null ? 0 : block.HashBlock;

            FederationWallet federationWallet = this.federationWalletManager.GetWallet();

            benchLogs.AppendLine("Fed.Wallet.Height: ".PadRight(LoggingConfiguration.ColumnLength + 1) +
                                 (federationWallet != null ? height.ToString().PadRight(8) : "No Wallet".PadRight(8)) +
                                 (federationWallet != null ? (" Fed.Wallet.Hash: ".PadRight(LoggingConfiguration.ColumnLength - 1) + hashBlock) : string.Empty));
        }
        /// <summary>
        /// Create the wallet manager and wallet transaction handler.
        /// </summary>
        /// <param name="dataFolder">The data folder.</param>
        protected void Init(DataFolder dataFolder)
        {
            this.dataFolder = dataFolder;

            // Create the wallet manager.
            this.federationWalletManager = new FederationWalletManager(
                this.loggerFactory,
                this.network,
                this.chain,
                dataFolder,
                this.walletFeePolicy,
                this.asyncLoopFactory,
                new NodeLifetime(),
                this.dateTimeProvider,
                this.federationGatewaySettings,
                this.withdrawalExtractor);

            // Starts and creates the wallet.
            this.federationWalletManager.Start();
            this.wallet = this.federationWalletManager.GetWallet();
            this.federationWalletTransactionHandler = new FederationWalletTransactionHandler(this.loggerFactory, this.federationWalletManager, this.walletFeePolicy, this.network);

            var storeSettings = (StoreSettings)FormatterServices.GetUninitializedObject(typeof(StoreSettings));

            this.federationWalletSyncManager = new FederationWalletSyncManager(this.loggerFactory, this.federationWalletManager, this.chain, this.network,
                                                                               this.blockRepository, storeSettings, Substitute.For <INodeLifetime>());

            this.federationWalletSyncManager.Start();

            // Set up the encrypted seed on the wallet.
            string encryptedSeed = this.extendedKey.PrivateKey.GetEncryptedBitcoinSecret(walletPassword, this.network).ToWif();

            this.wallet.EncryptedSeed = encryptedSeed;

            this.federationWalletManager.Secret = new WalletSecret()
            {
                WalletPassword = walletPassword
            };

            System.Reflection.FieldInfo prop = this.federationWalletManager.GetType().GetField("isFederationActive",
                                                                                               System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            prop.SetValue(this.federationWalletManager, true);
        }
Esempio n. 12
0
        public FederationWalletControllerTests()
        {
            this.loggerFactory     = Substitute.For <ILoggerFactory>();
            this.walletManager     = Substitute.For <IFederationWalletManager>();
            this.walletSyncManager = Substitute.For <IFederationWalletSyncManager>();
            this.connectionManager = Substitute.For <IConnectionManager>();
            this.network           = new StraxTest();

            this.chainIndexer = new ChainIndexer(this.network);

            ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(100, ChainedHeadersHelper.CreateGenesisChainedHeader(this.network), true, null, this.network).Last();

            this.chainIndexer.SetTip(tip);

            ICrossChainTransferStore crossChainTransferStore = Substitute.For <ICrossChainTransferStore>();

            crossChainTransferStore.GetCompletedWithdrawals(5).ReturnsForAnyArgs(new List <WithdrawalModel>()
            {
                new WithdrawalModel()
            });

            this.controller = new FederationWalletController(this.walletManager, this.walletSyncManager, this.connectionManager, this.network, this.chainIndexer, crossChainTransferStore);

            this.fedWallet = new FederationWallet
            {
                Network = this.network,
                LastBlockSyncedHeight = 999,
                CreationTime          = DateTimeOffset.Now
            };

            this.walletManager.GetWallet().Returns(this.fedWallet);

            var          federationWalletManager = (FederationWalletManager)FormatterServices.GetUninitializedObject(typeof(FederationWalletManager));
            PropertyInfo lockProp = typeof(LockProtected).GetProperty("lockObject", BindingFlags.NonPublic | BindingFlags.Instance);

            lockProp.SetValue(federationWalletManager, new object());
            federationWalletManager.Wallet = this.fedWallet;
            this.walletManager.GetSpendableAmount().Returns((x) =>
            {
                return(federationWalletManager.GetSpendableAmount());
            });
        }
        public IActionResult GetHistory([FromQuery] int maxEntriesToReturn)
        {
            try
            {
                FederationWallet wallet = this.walletManager.GetWallet();
                if (wallet == null)
                {
                    return(this.NotFound("No federation wallet found."));
                }

                List <WithdrawalModel> result = this.withdrawalHistoryProvider.GetHistory(maxEntriesToReturn);

                return(this.Json(result));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
Esempio n. 14
0
        public FederationWalletControllerTests()
        {
            this.loggerFactory     = Substitute.For <ILoggerFactory>();
            this.walletManager     = Substitute.For <IFederationWalletManager>();
            this.walletSyncManager = Substitute.For <IFederationWalletSyncManager>();
            this.connectionManager = Substitute.For <IConnectionManager>();
            this.network           = new StratisTest();

            this.chainIndexer = new ChainIndexer(this.network);

            ChainedHeader tip = ChainedHeadersHelper.CreateConsecutiveHeaders(100, ChainedHeadersHelper.CreateGenesisChainedHeader(this.network), true, null, this.network).Last();

            this.chainIndexer.SetTip(tip);


            this.dateTimeProvider          = Substitute.For <IDateTimeProvider>();
            this.withdrawalHistoryProvider = Substitute.For <IWithdrawalHistoryProvider>();

            this.controller = new FederationWalletController(this.loggerFactory, this.walletManager, this.walletSyncManager,
                                                             this.connectionManager, this.network, this.chainIndexer, this.dateTimeProvider, this.withdrawalHistoryProvider);

            this.fedWallet         = new FederationWallet();
            this.fedWallet.Network = this.network;
            this.fedWallet.LastBlockSyncedHeight = 999;
            this.fedWallet.CreationTime          = DateTimeOffset.Now;

            this.walletManager.GetWallet().Returns(this.fedWallet);

            var          federationWalletManager = (FederationWalletManager)FormatterServices.GetUninitializedObject(typeof(FederationWalletManager));
            PropertyInfo lockProp = typeof(LockProtected).GetProperty("lockObject", BindingFlags.NonPublic | BindingFlags.Instance);

            lockProp.SetValue(federationWalletManager, new object());
            federationWalletManager.Wallet = this.fedWallet;
            this.walletManager.GetSpendableAmount().Returns((x) => {
                return(federationWalletManager.GetSpendableAmount());
            });
        }
Esempio n. 15
0
        public IActionResult EnableFederation([FromBody] EnableFederationRequest request)
        {
            Guard.NotNull(request, nameof(request));

            try
            {
                if (!this.ModelState.IsValid)
                {
                    IEnumerable <string> errors = this.ModelState.Values.SelectMany(e => e.Errors.Select(m => m.ErrorMessage));
                    return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Formatting error", string.Join(Environment.NewLine, errors)));
                }

                FederationWallet wallet = this.walletManager.GetWallet();

                // Check the password
                try
                {
                    Key.Parse(wallet.EncryptedSeed, request.Password, wallet.Network);
                }
                catch (Exception ex)
                {
                    throw new SecurityException(ex.Message);
                }

                this.walletManager.Secret = new WalletSecret()
                {
                    WalletPassword = request.Password
                };
                return(this.Ok());
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
Esempio n. 16
0
        /// <summary>
        /// Creates a transaction to transfers funds from an old federation to a new federation.
        /// </summary>
        /// <param name="isSideChain">Indicates whether the <paramref name="network"/> is the sidechain.</param>
        /// <param name="network">The network that we are creating the recovery transaction for.</param>
        /// <param name="counterChainNetwork">The counterchain network.</param>
        /// <param name="dataDirPath">The root folder containing the old federation.</param>
        /// <param name="redeemScript">The new redeem script.</param>
        /// <param name="password">The password required to generate transactions using the federation wallet.</param>
        /// <param name="txTime">Any deposits beyond this UTC date will be ignored when selecting coin inputs.</param>
        /// <returns>A funds recovery transaction that moves funds to the new redeem script.</returns>
        public FundsRecoveryTransactionModel CreateFundsRecoveryTransaction(bool isSideChain, Network network, Network counterChainNetwork, string dataDirPath, Script redeemScript, string password, DateTime txTime)
        {
            var model = new FundsRecoveryTransactionModel()
            {
                Network = network, IsSideChain = isSideChain, RedeemScript = redeemScript
            };

            // Get the old redeem script from the wallet file.
            PayToMultiSigTemplateParameters multisigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(redeemScript);
            string           theChain          = isSideChain ? "sidechain" : "mainchain";
            var              nodeSettings      = new NodeSettings(network, args: new string[] { $"datadir={dataDirPath}", $"redeemscript={redeemScript}", $"-{theChain}" });
            var              walletFileStorage = new FileStorage <FederationWallet>(nodeSettings.DataFolder.WalletPath);
            FederationWallet wallet            = walletFileStorage.LoadByFileName("multisig_wallet.json");
            Script           oldRedeemScript   = wallet.MultiSigAddress.RedeemScript;
            PayToMultiSigTemplateParameters oldMultisigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(oldRedeemScript);

            model.oldMultisigAddress = oldRedeemScript.Hash.GetAddress(network);
            model.newMultisigAddress = redeemScript.Hash.GetAddress(network);

            // Create dummy inputs to avoid errors when constructing FederatedPegSettings.
            var extraArgs = new Dictionary <string, string>();

            extraArgs[FederatedPegSettings.FederationIpsParam] = oldMultisigParams.PubKeys.Select(p => "0.0.0.0".ToIPEndPoint(nodeSettings.Network.DefaultPort)).Join(",");
            var privateKey = Key.Parse(wallet.EncryptedSeed, password, network);

            extraArgs[FederatedPegSettings.PublicKeyParam] = privateKey.PubKey.ToHex(network);
            (new TextFileConfiguration(extraArgs.Select(i => $"{i.Key}={i.Value}").ToArray())).MergeInto(nodeSettings.ConfigReader);

            model.PubKey = privateKey.PubKey;

            var dBreezeSerializer = new DBreezeSerializer(network.Consensus.ConsensusFactory);
            var blockStore        = new BlockRepository(network, nodeSettings.DataFolder, nodeSettings.LoggerFactory, dBreezeSerializer);

            blockStore.Initialize();

            var           chain        = new ChainRepository(nodeSettings.DataFolder, nodeSettings.LoggerFactory, dBreezeSerializer);
            Block         genesisBlock = network.GetGenesis();
            ChainedHeader tip          = chain.LoadAsync(new ChainedHeader(genesisBlock.Header, genesisBlock.GetHash(), 0)).GetAwaiter().GetResult();
            var           chainIndexer = new ChainIndexer(network, tip);

            var nodeLifetime = new NodeLifetime();
            IDateTimeProvider dateTimeProvider = DateTimeProvider.Default;
            var federatedPegSettings           = new FederatedPegSettings(nodeSettings);
            var opReturnDataReader             = new OpReturnDataReader(nodeSettings.LoggerFactory, new CounterChainNetworkWrapper(counterChainNetwork));
            var walletFeePolicy = new WalletFeePolicy(nodeSettings);

            var walletManager = new FederationWalletManager(nodeSettings.LoggerFactory, network, chainIndexer, nodeSettings.DataFolder, walletFeePolicy,
                                                            new AsyncProvider(nodeSettings.LoggerFactory, new Signals(nodeSettings.LoggerFactory, new DefaultSubscriptionErrorHandler(nodeSettings.LoggerFactory)), nodeLifetime), nodeLifetime,
                                                            dateTimeProvider, federatedPegSettings, new WithdrawalExtractor(nodeSettings.LoggerFactory, federatedPegSettings, opReturnDataReader, network), blockStore);

            walletManager.Start();
            walletManager.EnableFederationWallet(password);

            if (!walletManager.IsFederationWalletActive())
            {
                throw new ArgumentException($"Could not activate the federation wallet on {network}.");
            }

            // Retrieves the unspent outputs in deterministic order.
            List <Stratis.Features.FederatedPeg.Wallet.UnspentOutputReference> coinRefs = walletManager.GetSpendableTransactionsInWallet().ToList();

            // Exclude coins (deposits) beyond the transaction (switch-over) time!
            coinRefs = coinRefs.Where(r => r.Transaction.CreationTime < txTime).ToList();
            if (!coinRefs.Any())
            {
                throw new ArgumentException($"There are no coins to recover from the federation wallet on {network}.");
            }

            Money fee = federatedPegSettings.GetWithdrawalTransactionFee(coinRefs.Count());

            var builder = new TransactionBuilder(network);

            builder.AddKeys(privateKey);
            builder.AddCoins(coinRefs.Select(c => ScriptCoin.Create(network, c.Transaction.Id, (uint)c.Transaction.Index, c.Transaction.Amount, c.Transaction.ScriptPubKey, oldRedeemScript)));

            // Split the coins into multiple outputs.
            Money     amount         = coinRefs.Sum(r => r.Transaction.Amount) - fee;
            const int numberOfSplits = 10;
            Money     splitAmount    = new Money((long)amount / numberOfSplits);
            var       recipients     = new List <Stratis.Features.FederatedPeg.Wallet.Recipient>();

            for (int i = 0; i < numberOfSplits; i++)
            {
                Money sendAmount = (i != (numberOfSplits - 1)) ? splitAmount : amount - splitAmount * (numberOfSplits - 1);

                builder.Send(redeemScript.PaymentScript, sendAmount);
            }

            builder.SetTimeStamp((uint)(new DateTimeOffset(txTime)).ToUnixTimeSeconds());
            builder.CoinSelector = new DeterministicCoinSelector();
            builder.SendFees(fee);

            model.tx = builder.BuildTransaction(true);

            File.WriteAllText(Path.Combine(dataDirPath, $"{network.Name}_{model.PubKey.ToHex(network).Substring(0, 8)}.hex"), model.tx.ToHex(network));

            // Merge our transaction with other transactions which have been placed in the data folder.
            Transaction oldTransaction = model.tx;
            string      namePattern    = $"{network.Name}_*.hex";
            int         sigCount       = 1;

            foreach (string fileName in Directory.EnumerateFiles(dataDirPath, namePattern))
            {
                Transaction incomingPartialTransaction = network.CreateTransaction(File.ReadAllText(fileName));

                // Don't merge with self.
                if (incomingPartialTransaction.GetHash() == oldTransaction.GetHash())
                {
                    continue;
                }

                // Transaction times must match.
                if (incomingPartialTransaction is PosTransaction && incomingPartialTransaction.Time != model.tx.Time)
                {
                    Console.WriteLine($"The locally generated transaction is time-stamped differently from the transaction contained in '{fileName}'. The imported signature can't be used.");
                    continue;
                }

                // Combine signatures.
                Transaction newTransaction = SigningUtils.CheckTemplateAndCombineSignatures(builder, model.tx, new[] { incomingPartialTransaction });

                if (oldTransaction.GetHash() == newTransaction.GetHash())
                {
                    Console.WriteLine($"The locally generated transaction is not similar to '{fileName}'. The imported signature can't be used.");
                    continue;
                }

                model.tx = newTransaction;
                sigCount++;
            }

            Console.WriteLine($"{sigCount} of {multisigParams.SignatureCount} signatures collected for {network.Name}.");

            if (sigCount >= multisigParams.SignatureCount)
            {
                if (builder.Verify(model.tx))
                {
                    // Write the transaction to file.
                    File.WriteAllText(Path.Combine(dataDirPath, $"{(txTime > DateTime.Now ? "Preliminary " : "")}{network.Name}Recovery.txt"), model.tx.ToHex(network));
                }
                else
                {
                    Console.WriteLine("Could not verify the transaction.");
                }
            }

            // Stop the wallet manager to release the database folder.
            nodeLifetime.StopApplication();
            walletManager.Stop();

            return(model);
        }