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); }
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); }
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); }
/// <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); }