public CollateralCheckerTests()
        {
            var loggerFactory = new LoggerFactory();
            IHttpClientFactory clientFactory = new Bitcoin.Controllers.HttpClientFactory();

            Network network = CirrusNetwork.NetworksSelector.Regtest();

            this.collateralFederationMembers = new List <CollateralFederationMember>()
            {
                new CollateralFederationMember(new PubKey("036317d97f911ce899fd0a360866d19f2dca5252c7960d4652d814ab155a8342de"), new Money(100), "addr1"),
                new CollateralFederationMember(new PubKey("02a08d72d47b3103261163c15aa2f6b0d007e1872ad9f5fddbfbd27bdb738156e9"), new Money(500), "addr2"),
                new CollateralFederationMember(new PubKey("03634c79d4e8e915cfb9f7bbef57bed32d715150836b7845b1a14c93670d816ab6"), new Money(100_000), "addr3")
            };

            List <IFederationMember> federationMembers = (network.Consensus.Options as PoAConsensusOptions).GenesisFederationMembers;

            federationMembers.Clear();
            federationMembers.AddRange(this.collateralFederationMembers);

            FederatedPegSettings fedPegSettings = FedPegTestsHelper.CreateSettings(network, out NodeSettings nodeSettings);

            CounterChainSettings settings = new CounterChainSettings(nodeSettings, Networks.Stratis.Regtest());
            var asyncMock = new Mock <IAsyncProvider>();

            asyncMock.Setup(a => a.RegisterTask(It.IsAny <string>(), It.IsAny <Task>()));

            ISignals           signals    = new Signals(loggerFactory, new DefaultSubscriptionErrorHandler(loggerFactory));
            IFederationManager fedManager = new CollateralFederationManager(nodeSettings, network, loggerFactory, new Mock <IKeyValueRepository>().Object, signals);

            fedManager.Initialize();

            this.collateralChecker = new CollateralChecker(loggerFactory, clientFactory, settings, fedManager, signals, network, asyncMock.Object);
        }
Пример #2
0
        public void Call_Mainchain_Gateway_Get_Info()
        {
            var federation = new Federation(new[]
            {
                new PubKey("02fad5f3c4fdf4c22e8be4cfda47882fff89aaa0a48c1ccad7fa80dc5fee9ccec3"),
                new PubKey("02503f03243d41c141172465caca2f5cef7524f149e965483be7ce4e44107d7d35"),
                new PubKey("03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c")
            });

            Network sideChainNetwork = new TestNetwork(federation);

            string redeemScript = PayToFederationTemplate.Instance.GenerateScriptPubKey(federation.Id).ToString();

            string federationIps  = "127.0.0.1:36201,127.0.0.1:36202,127.0.0.1:36203";
            string multisigPubKey = federation.GetFederationDetails().transactionSigningKeys.TakeLast(1).First().ToHex();

            string[] args         = new[] { "-mainchain", "-testnet", $"-federationips={federationIps}", $"-redeemscript={redeemScript}", $"-publickey={multisigPubKey}", "-mincoinmaturity=1", "-mindepositconfirmations=1" };
            var      nodeSettings = new NodeSettings(sideChainNetwork, ProtocolVersion.ALT_PROTOCOL_VERSION, args: args);

            this.federationWalletManager.IsFederationWalletActive().Returns(true);

            var settings = new FederatedPegSettings(nodeSettings, new CounterChainNetworkWrapper(KnownNetworks.StraxRegTest));

            var controller = new FederationGatewayController(
                Substitute.For <IAsyncProvider>(),
                new ChainIndexer(),
                Substitute.For <IConnectionManager>(),
                this.crossChainTransferStore,
                this.GetMaturedBlocksProvider(settings),
                this.network,
                settings,
                this.federationWalletManager,
                Substitute.For <IFullNode>(),
                Substitute.For <IPeerBanning>(),
                this.federationManager);

            IActionResult result = controller.GetInfo();

            result.Should().BeOfType <JsonResult>();
            ((JsonResult)result).Value.Should().BeOfType <FederationGatewayInfoModel>();

            var model = ((JsonResult)result).Value as FederationGatewayInfoModel;

            model.IsMainChain.Should().BeTrue();
            model.FederationMiningPubKeys.Should().BeNull();
            model.MiningPublicKey.Should().BeNull();
            model.MultiSigRedeemScript.Should().Be(redeemScript);
            string.Join(",", model.FederationNodeIpEndPoints).Should().Be(federationIps);
            model.IsActive.Should().BeTrue();
            model.MinimumDepositConfirmationsSmallDeposits.Should().Be(25);
            model.MinimumDepositConfirmationsNormalDeposits.Should().Be(80);
            model.MultisigPublicKey.Should().Be(multisigPubKey);
        }
Пример #3
0
        private void InitializeCollateralChecker([CallerMemberName] string callingMethod = "")
        {
            var loggerFactory = new LoggerFactory();
            IHttpClientFactory clientFactory = new Bitcoin.Controllers.HttpClientFactory();

            Network network = CirrusNetwork.NetworksSelector.Regtest();

            this.collateralFederationMembers = new List <CollateralFederationMember>()
            {
                new CollateralFederationMember(new PubKey("036317d97f911ce899fd0a360866d19f2dca5252c7960d4652d814ab155a8342de"), false, new Money(100), "addr1"),
                new CollateralFederationMember(new PubKey("02a08d72d47b3103261163c15aa2f6b0d007e1872ad9f5fddbfbd27bdb738156e9"), false, new Money(500), "addr2"),
                new CollateralFederationMember(new PubKey("03634c79d4e8e915cfb9f7bbef57bed32d715150836b7845b1a14c93670d816ab6"), false, new Money(100_000), "addr3")
            };

            List <IFederationMember> federationMembers = (network.Consensus.Options as PoAConsensusOptions).GenesisFederationMembers;

            federationMembers.Clear();
            federationMembers.AddRange(this.collateralFederationMembers);

            var dataFolder = TestBase.CreateTestDir(callingMethod);
            FederatedPegSettings fedPegSettings = FedPegTestsHelper.CreateSettings(network, KnownNetworks.StraxRegTest, dataFolder, out NodeSettings nodeSettings);

            var counterChainSettings = new CounterChainSettings(nodeSettings, new CounterChainNetworkWrapper(Networks.Strax.Regtest()));
            var asyncMock            = new Mock <IAsyncProvider>();

            asyncMock.Setup(a => a.RegisterTask(It.IsAny <string>(), It.IsAny <Task>()));

            ISignals signals            = new Signals(loggerFactory, new DefaultSubscriptionErrorHandler(loggerFactory));
            var      dbreezeSerializer  = new DBreezeSerializer(network.Consensus.ConsensusFactory);
            var      asyncProvider      = new AsyncProvider(loggerFactory, signals);
            var      finalizedBlockRepo = new FinalizedBlockInfoRepository(new LevelDbKeyValueRepository(nodeSettings.DataFolder, dbreezeSerializer), asyncProvider);

            finalizedBlockRepo.LoadFinalizedBlockInfoAsync(network).GetAwaiter().GetResult();

            var chainIndexerMock = new Mock <ChainIndexer>();
            var header           = new BlockHeader();

            chainIndexerMock.Setup(x => x.Tip).Returns(new ChainedHeader(header, header.GetHash(), 0));
            var fullNode = new Mock <IFullNode>();

            IFederationManager federationManager = new FederationManager(fullNode.Object, network, nodeSettings, signals, counterChainSettings);
            var votingManager     = new VotingManager(federationManager, new Mock <IPollResultExecutor>().Object, new Mock <INodeStats>().Object, nodeSettings.DataFolder, dbreezeSerializer, signals, finalizedBlockRepo, network);
            var federationHistory = new FederationHistory(federationManager, votingManager);

            votingManager.Initialize(federationHistory);

            fullNode.Setup(x => x.NodeService <VotingManager>(It.IsAny <bool>())).Returns(votingManager);

            federationManager.Initialize();

            this.collateralChecker = new CollateralChecker(clientFactory, counterChainSettings, federationManager, signals, network, asyncMock.Object, (new Mock <INodeLifetime>()).Object);
        }
        public static FederatedPegSettings CreateSettings(Network network, out NodeSettings nodeSettings)
        {
            string redeemScript   = "2 02fad5f3c4fdf4c22e8be4cfda47882fff89aaa0a48c1ccad7fa80dc5fee9ccec3 02503f03243d41c141172465caca2f5cef7524f149e965483be7ce4e44107d7d35 03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c 3 OP_CHECKMULTISIG";
            string federationIps  = "127.0.0.1:36201,127.0.0.1:36202,127.0.0.1:36203";
            string multisigPubKey = "03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c";

            string[] args = new[] { "-sidechain", "-regtest", $"-federationips={federationIps}", $"-redeemscript={redeemScript}", $"-publickey={multisigPubKey}", "-mincoinmaturity=1", "-mindepositconfirmations=1" };
            nodeSettings = new NodeSettings(network, ProtocolVersion.ALT_PROTOCOL_VERSION, args: args);

            var settings = new FederatedPegSettings(nodeSettings);

            return(settings);
        }
        public static FederatedPegSettings CreateSettings(Network network, Network counterChainNetwork, out NodeSettings nodeSettings)
        {
            FederationId federationId   = network.Federations.GetOnlyFederation().Id;
            string       redeemScript   = PayToFederationTemplate.Instance.GenerateScriptPubKey(federationId).ToString();
            string       federationIps  = "127.0.0.1:36201,127.0.0.1:36202,127.0.0.1:36203";
            string       multisigPubKey = network.Federations.GetFederation(federationId).GetFederationDetails().pubKeys.TakeLast(1).First().ToHex();

            string[] args = new[] { "-sidechain", "-regtest", $"-federationips={federationIps}", $"-redeemscript={redeemScript}", $"-publickey={multisigPubKey}", "-mincoinmaturity=1", "-mindepositconfirmations=1" };
            nodeSettings = new NodeSettings(network, ProtocolVersion.ALT_PROTOCOL_VERSION, args: args);

            var settings = new FederatedPegSettings(nodeSettings, new CounterChainNetworkWrapper(counterChainNetwork));

            return(settings);
        }
        public void Call_Sidechain_Gateway_Get_Info()
        {
            string redeemScript   = "2 02fad5f3c4fdf4c22e8be4cfda47882fff89aaa0a48c1ccad7fa80dc5fee9ccec3 02503f03243d41c141172465caca2f5cef7524f149e965483be7ce4e44107d7d35 03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c 3 OP_CHECKMULTISIG";
            string federationIps  = "127.0.0.1:36201,127.0.0.1:36202,127.0.0.1:36203";
            string multisigPubKey = "03be943c3a31359cd8e67bedb7122a0898d2c204cf2d0119e923ded58c429ef97c";

            string[] args         = new[] { "-sidechain", "-regtest", $"-federationips={federationIps}", $"-redeemscript={redeemScript}", $"-publickey={multisigPubKey}", "-mincoinmaturity=1", "-mindepositconfirmations=1" };
            var      nodeSettings = new NodeSettings(CirrusNetwork.NetworksSelector.Regtest(), ProtocolVersion.ALT_PROTOCOL_VERSION, args: args);

            this.federationWalletManager.IsFederationWalletActive().Returns(true);

            this.federationManager.Initialize();

            var settings = new FederatedPegSettings(nodeSettings);

            var controller = new FederationGatewayController(
                this.loggerFactory,
                this.GetMaturedBlocksProvider(),
                settings,
                this.federationWalletManager,
                this.federationManager);

            IActionResult result = controller.GetInfo();

            result.Should().BeOfType <JsonResult>();
            ((JsonResult)result).Value.Should().BeOfType <FederationGatewayInfoModel>();

            FederationGatewayInfoModel model = ((JsonResult)result).Value as FederationGatewayInfoModel;

            model.IsMainChain.Should().BeFalse();
            model.FederationMiningPubKeys.Should().Equal(((PoAConsensusOptions)CirrusNetwork.NetworksSelector.Regtest().Consensus.Options).GenesisFederationMembers.Select(keys => keys.ToString()));
            model.MultiSigRedeemScript.Should().Be(redeemScript);
            string.Join(",", model.FederationNodeIpEndPoints).Should().Be(federationIps);
            model.IsActive.Should().BeTrue();
            model.MinimumDepositConfirmations.Should().Be(1);
            model.MultisigPublicKey.Should().Be(multisigPubKey);
        }
Пример #7
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);
        }
        public void Eight_Of_Fifteen_SufficientlyFunded()
        {
            var network = new StratisMain();

            const int n = 15;
            const int m = 8;

            Key[] keys = new Key[n];

            Key ultimateReceiver = new Key();

            for (int i = 0; i < n; i++)
            {
                keys[i] = new Key();
            }

            Script redeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(m, keys.Select(x => x.PubKey).ToArray());

            const int     inputCount         = 50;
            const decimal fundingInputAmount = 100;
            const decimal fundingAmount      = 99; // Must be less than fundingInputAmount.

            var multiSigCoins = new List <ICoin>();

            for (int i = 0; i < inputCount; i++)
            {
                var builder = new TransactionBuilder(network);

                // Build transactions to fund the multisig
                Transaction funding = builder
                                      .AddCoins(GetCoinSource(keys[0], new[] { Money.Coins(fundingInputAmount) }))
                                      .AddKeys(keys[0])
                                      .Send(redeemScript.Hash, Money.Coins(fundingAmount))
                                      .SetChange(keys[0].PubKey.Hash)
                                      .SendFees(Money.Satoshis(5000))
                                      .BuildTransaction(true);

                multiSigCoins.Add(ScriptCoin.Create(network, funding, funding.Outputs.To(redeemScript.Hash).First(), redeemScript));
            }

            var fedPegSettings = new FederatedPegSettings(new NodeSettings(network, args: new string[]
            {
                "mainchain",
                "redeemscript=" + redeemScript.ToString(),
                "publickey=" + keys[0].PubKey.ToHex(),
                "federationips=0.0.0.0"
            }));

            // Construct the withdrawal tx
            var         txBuilder = new TransactionBuilder(network);
            Transaction tx        = txBuilder
                                    .AddCoins(multiSigCoins)
                                    .AddKeys(keys.Take(m).ToArray())
                                    .Send(ultimateReceiver.PubKey.Hash, Money.Coins(inputCount * fundingAmount - 1))
                                    .SetChange(redeemScript.Hash)
                                    .SendFees(fedPegSettings.GetWithdrawalTransactionFee(inputCount))
                                    .BuildTransaction(true);

            bool verify = txBuilder.Verify(tx, out TransactionPolicyError[] errors);

            Assert.True(verify);
        }