public async Task FinalizedHeightCantBeDecreasedAsync() { string dir = CreateTestDir(this); using (var repo = new ChainRepository(dir, new LoggerFactory())) { await repo.SaveFinalizedBlockHeightAsync(777); await repo.SaveFinalizedBlockHeightAsync(555); Assert.Equal(777, repo.GetFinalizedBlockHeight()); } using (var repo = new ChainRepository(dir, new LoggerFactory())) { await repo.LoadFinalizedBlockHeightAsync(); Assert.Equal(777, repo.GetFinalizedBlockHeight()); } }
public void CanSaveChainIncrementally() { using (var repo = new ChainRepository(TestBase.CreateTestDir(this), this.loggerFactory)) { var chain = new ConcurrentChain(Network.RegTest); repo.LoadAsync(chain).GetAwaiter().GetResult(); Assert.True(chain.Tip == chain.Genesis); chain = new ConcurrentChain(Network.RegTest); ChainedHeader tip = this.AppendBlock(chain); repo.SaveAsync(chain).GetAwaiter().GetResult(); var newChain = new ConcurrentChain(Network.RegTest); repo.LoadAsync(newChain).GetAwaiter().GetResult(); Assert.Equal(tip, newChain.Tip); tip = this.AppendBlock(chain); repo.SaveAsync(chain).GetAwaiter().GetResult(); newChain = new ConcurrentChain(Network.RegTest); repo.LoadAsync(newChain).GetAwaiter().GetResult(); Assert.Equal(tip, newChain.Tip); } }
public async Task FinalizedHeightCantBeDecreasedAsync() { string dir = CreateTestDir(this); using (var repo = new ChainRepository(dir, new LoggerFactory())) { await repo.SaveFinalizedBlockHashAndHeightAsync(uint256.One, 777); await repo.SaveFinalizedBlockHashAndHeightAsync(uint256.One, 555); Assert.Equal(777, repo.GetFinalizedBlockInfo().Height); } using (var repo = new ChainRepository(dir, new LoggerFactory())) { await repo.LoadFinalizedBlockInfoAsync(new StratisMain()); Assert.Equal(777, repo.GetFinalizedBlockInfo().Height); } }
public void LoadChainFromDisk() { string dir = CreateTestDir(this); var chain = new ChainIndexer(KnownNetworks.StraxRegTest); ChainedHeader tip = this.AppendBlock(chain); using (var engine = new DB(new Options { CreateIfMissing = true }, new DataFolder(dir).ChainPath)) { using (var batch = new WriteBatch()) { ChainedHeader toSave = tip; var blocks = new List <ChainedHeader>(); while (toSave != null) { blocks.Insert(0, toSave); toSave = toSave.Previous; } foreach (ChainedHeader block in blocks) { batch.Put(1, BitConverter.GetBytes(block.Height), new ChainRepository.ChainRepositoryData() { Hash = block.HashBlock, Work = block.ChainWorkBytes } .ToBytes(this.Network.Consensus.ConsensusFactory)); } engine.Write(batch); } } using (var repo = new ChainRepository(new LevelDbChainStore(chain.Network, new DataFolder(dir), chain))) { var testChain = new ChainIndexer(KnownNetworks.StraxRegTest); testChain.SetTip(repo.LoadAsync(testChain.Genesis).GetAwaiter().GetResult()); Assert.Equal(tip, testChain.Tip); } }
public void CanSaveChainIncrementally() { using (var repo = new ChainRepository(TestBase.CreateTestDir(this), this.loggerFactory, this.dBreezeSerializer)) { var chain = new ChainIndexer(this.regTest); chain.SetTip(repo.LoadAsync(chain.Genesis).GetAwaiter().GetResult()); Assert.True(chain.Tip == chain.Genesis); chain = new ChainIndexer(this.regTest); ChainedHeader tip = this.AppendBlock(chain); repo.SaveAsync(chain).GetAwaiter().GetResult(); var newChain = new ChainIndexer(this.regTest); newChain.SetTip(repo.LoadAsync(chain.Genesis).GetAwaiter().GetResult()); Assert.Equal(tip, newChain.Tip); tip = this.AppendBlock(chain); repo.SaveAsync(chain).GetAwaiter().GetResult(); newChain = new ChainIndexer(this.regTest); newChain.SetTip(repo.LoadAsync(chain.Genesis).GetAwaiter().GetResult()); Assert.Equal(tip, newChain.Tip); } }
public void CanSaveChainIncrementally() { var chain = new ChainIndexer(this.regTest); var data = new DataFolder(TestBase.CreateTestDir(this)); using (var repo = new ChainRepository(new LevelDbChainStore(this.network, data, chain))) { chain.SetTip(repo.LoadAsync(chain.Genesis).GetAwaiter().GetResult()); Assert.True(chain.Tip == chain.Genesis); chain = new ChainIndexer(this.regTest); ChainedHeader tip = this.AppendBlock(chain); repo.SaveAsync(chain).GetAwaiter().GetResult(); var newChain = new ChainIndexer(this.regTest); newChain.SetTip(repo.LoadAsync(chain.Genesis).GetAwaiter().GetResult()); Assert.Equal(tip, newChain.Tip); tip = this.AppendBlock(chain); repo.SaveAsync(chain).GetAwaiter().GetResult(); newChain = new ChainIndexer(this.regTest); newChain.SetTip(repo.LoadAsync(chain.Genesis).GetAwaiter().GetResult()); Assert.Equal(tip, newChain.Tip); } }
public void SaveChainToDisk() { string dir = CreateTestDir(this); var chain = new ChainIndexer(this.Network); this.AppendBlock(chain); using (var repo = new ChainRepository(new RocksDbChainStore(chain.Network, new DataFolder(dir), chain))) { repo.SaveAsync(chain).GetAwaiter().GetResult(); } using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(), new DataFolder(dir).ChainPath)) { ChainedHeader tip = null; var itr = engine.NewIterator(); itr.SeekToFirst(); while (itr.Valid()) { if (itr.Key()[0] == 1) { var data = new ChainRepository.ChainRepositoryData(); data.FromBytes(itr.Value().ToArray(), this.Network.Consensus.ConsensusFactory); tip = new ChainedHeader(data.Hash, data.Work, tip); if (tip.Height == 0) { tip.SetChainStore(new ChainStore()); } } itr.Next(); } Assert.Equal(tip, chain.Tip); } }
public void CanSaveChainIncrementally() { using (var dir = TestDirectory.Create()) { using (var repo = new ChainRepository(dir.FolderName)) { var chain = new ConcurrentChain(Network.RegTest); repo.Load(chain).GetAwaiter().GetResult(); Assert.True(chain.Tip == chain.Genesis); chain = new ConcurrentChain(Network.RegTest); var tip = AppendBlock(chain); repo.Save(chain).GetAwaiter().GetResult(); var newChain = new ConcurrentChain(Network.RegTest); repo.Load(newChain).GetAwaiter().GetResult(); Assert.Equal(tip, newChain.Tip); tip = AppendBlock(chain); repo.Save(chain).GetAwaiter().GetResult(); newChain = new ConcurrentChain(Network.RegTest); repo.Load(newChain).GetAwaiter().GetResult(); Assert.Equal(tip, newChain.Tip); } } }
public void SaveChainToDisk() { string dir = CreateTestDir(this); var chain = new ChainIndexer(KnownNetworks.StraxRegTest); this.AppendBlock(chain); using (var repo = new ChainRepository(new LevelDbChainStore(chain.Network, new DataFolder(dir), chain))) { repo.SaveAsync(chain).GetAwaiter().GetResult(); } using (var engine = new DB(new Options { CreateIfMissing = true }, new DataFolder(dir).ChainPath)) { ChainedHeader tip = null; var itr = engine.GetEnumerator(); while (itr.MoveNext()) { if (itr.Current.Key[0] == 1) { var data = new ChainRepository.ChainRepositoryData(); data.FromBytes(itr.Current.Value.ToArray(), this.Network.Consensus.ConsensusFactory); tip = new ChainedHeader(data.Hash, data.Work, tip); if (tip.Height == 0) { tip.SetChainStore(new ChainStore()); } } } Assert.Equal(tip, chain.Tip); } }
/// <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 ChainBusiness(ConfigurationModel configuration) { _envioReadRepository = new ChainRepository(configuration.EnvioRead); }