Commit 1be933f6 authored by Pavel Pavlov's avatar Pavel Pavlov

Bugfix

parent 3c8c9867
......@@ -38,7 +38,7 @@ namespace DeStream.DeStreamD.ForTest
{
MainAsync(args).Wait();
}
public static async Task MainAsync(string[] args)
{
......@@ -54,7 +54,7 @@ namespace DeStream.DeStreamD.ForTest
Console.WriteLine($"current network: {network.Name}");
// NOTES: running BTC and STRAT side by side is not possible yet as the flags for serialization are static
FullNode node = (FullNode)new FullNodeBuilder()
......@@ -62,25 +62,34 @@ namespace DeStream.DeStreamD.ForTest
.UseBlockStore()
.UsePosConsensus()
.UseMempool()
.UseWalletDeStream()
.UseWallet()
.AddPowPosMining()
.UseApi()
.AddRPC()
.Build();
node.Services.ServiceProvider.GetService<IPowMining>().GenerateBlocks(new ReserveScript { ReserveFullNodeScript = MinerSecret.ScriptPubKey }, 3, uint.MaxValue);
var walletManager = (DeStreamWalletManager)node.WalletManager();
(Wallet wallet, Block block, ChainedHeader chainedHeader) result = TestClassHelper.CreateFirstTransaction(nodeSettings, node.WalletManager(), node.NodeService<WalletSettings>(),
node.NodeService<IWalletFeePolicy>());
var walletManager = node.WalletManager();
walletManager.Wallets.Add(result.wallet);
//HdAddress addr = node.WalletManager().GetUnusedAddress(new WalletAccountReference("myWallet1", "account1"));
//HdAddress addr = result.wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address;
//HdAddress addr = new HdAddress();
HdAddress addr = result.wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0);
Key key = result.wallet.GetExtendedPrivateKeyForAddress("password", addr).PrivateKey;
TestClassHelper.CreateTestBlock(node, key);
//Wallet wallet = TestClassHelper.CreateFirstTransaction(nodeSettings, ref walletManager, node.NodeService<WalletSettings>(),
// node.NodeService<IWalletFeePolicy>());
//(Wallet wallet, Block block, ChainedHeader chainedHeader) test = TestClassHelper.CreateFirstTransaction(nodeSettings, ref walletManager, node.NodeService<WalletSettings>(),
// node.NodeService<IWalletFeePolicy>());
//((WalletManager)node.NodeService<IWalletManager>()).Wallets.Add(test.wallet);
//((WalletManager)node.NodeService<IWalletManager>()).LoadKeysLookupLock();
//((WalletManager)node.NodeService<IWalletManager>()).WalletTipHash = test.block.Header.GetHash();
//((WalletManager)node.NodeService<IWalletManager>()).WalletTipHash = test.block.Header.GetHash();
//((WalletManager)node.NodeService<IWalletManager>()).ProcessBlock(test.block, test.chainedHeader);
......
......@@ -33,15 +33,15 @@ namespace DeStream.DeStreamD.ForTest
}
public static (ConcurrentChain chain, uint256 blockhash, Block block) CreateChainAndCreateFirstBlockWithPaymentToAddress(DeStreamWalletManager walletManager, Network network, HdAddress address)
public static (ConcurrentChain chain, uint256 blockhash, Block block) CreateChainAndCreateFirstBlockWithPaymentToAddress(WalletManager walletManager, Network network, HdAddress address)
{
var chain = new ConcurrentChain(network);
//var chain = walletManager.Chain;
var block = new Block();
//var block = network.GetGenesis().Header;
block.Header.HashPrevBlock = chain.Tip.HashBlock;
block.Header.Bits = block.Header.GetWorkRequired(network, chain.Tip);
block.Header.UpdateTime(DateTimeOffset.UtcNow, network, chain.Tip);
......@@ -56,7 +56,7 @@ namespace DeStream.DeStreamD.ForTest
block.Header.PrecomputeHash();
chain.SetTip(block.Header);
return (chain, block.GetHash(), block);
}
......@@ -136,7 +136,7 @@ namespace DeStream.DeStreamD.ForTest
//public static Wallet CreateFirstTransaction(DeStreamNodeSettings nodeSettings, ref DeStreamWalletManager walletManager, WalletSettings walletSettings,
// IWalletFeePolicy _walletFeePolicy)
public static (Wallet wallet, Block block, ChainedHeader chainedHeader) CreateFirstTransaction(DeStreamNodeSettings nodeSettings, ref DeStreamWalletManager walletManager, WalletSettings walletSettings,
public static (Wallet wallet, Block block, ChainedHeader chainedHeader) CreateFirstTransaction(DeStreamNodeSettings nodeSettings, WalletManager walletManager, WalletSettings walletSettings,
IWalletFeePolicy _walletFeePolicy)
{
Wallet wallet = GenerateBlankWalletWithExtKey("myWallet1", "password").wallet;
......@@ -204,18 +204,18 @@ namespace DeStream.DeStreamD.ForTest
//var _walletManager = new WalletManager(nodeSettings.LoggerFactory, Network.Main, chainInfo.chain, nodeSettings, new Mock<WalletSettings>().Object,
// nodeSettings.DataFolder, walletFeePolicy.Object, new Mock<IAsyncLoopFactory>().Object, new NodeLifetime(), DateTimeProvider.Default);
walletManager = new DeStreamWalletManager(nodeSettings.LoggerFactory, nodeSettings.Network, chainInfo.chain, nodeSettings,
walletSettings,
nodeSettings.DataFolder, walletFeePolicy.Object, walletManager.AsyncLoopFactory, walletManager.NodeLifetime, walletManager.DateTimeProvider);
walletManager.Wallets.Add(wallet);
walletManager.LoadKeysLookupLock();
walletManager.WalletTipHash = block.Header.GetHash();
//walletManager = new DeStreamWalletManager(nodeSettings.LoggerFactory, nodeSettings.Network, chainInfo.chain, nodeSettings,
// walletSettings,
// nodeSettings.DataFolder, walletFeePolicy.Object, walletManager.AsyncLoopFactory, walletManager.NodeLifetime, walletManager.DateTimeProvider);
//walletManager.Wallets.Add(wallet);
//walletManager.LoadKeysLookupLock();
//walletManager.WalletTipHash = block.Header.GetHash();
ChainedHeader chainedBlock = chainInfo.chain.GetBlock(block.GetHash());
//walletManager.ProcessTransaction(transaction, null, block, false);
walletManager.ProcessBlock(block, chainedBlock);
////walletManager.ProcessTransaction(transaction, null, block, false);
//walletManager.ProcessBlock(block, chainedBlock);
return (wallet, block, chainedBlock);
//HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0);
......@@ -227,40 +227,40 @@ namespace DeStream.DeStreamD.ForTest
return new Money(9000000000);
}
public static void CreateTestBlock(FullNode fullNode, BitcoinSecret minerSecret)
public static Block CreateTestBlock(FullNode fullNode, Key key)
{
//FullNode fullNode = (this.runner as StratisBitcoinPowRunner).FullNode;
BitcoinSecret dest = minerSecret;
BitcoinSecret dest = new BitcoinSecret(key, fullNode.Network);
var blocks = new List<Block>();
List<Transaction> passedTransactions = null;
for (int i = 0; i < 3; i++)
uint nonce = 0;
var block = new Block();
block.Header.HashPrevBlock = fullNode.Chain.Tip.HashBlock;
block.Header.Bits = block.Header.GetWorkRequired(fullNode.Network, fullNode.Chain.Tip);
block.Header.UpdateTime(DateTimeOffset.UtcNow, fullNode.Network, fullNode.Chain.Tip);
var coinbase = new Transaction();
coinbase.AddInput(TxIn.CreateCoinbase(fullNode.Chain.Height + 1));
coinbase.AddOutput(new TxOut(Get9Billion(), dest.GetAddress()));
block.AddTransaction(coinbase);
if (passedTransactions?.Any() ?? false)
{
uint nonce = 0;
var block = new Block();
block.Header.HashPrevBlock = fullNode.Chain.Tip.HashBlock;
block.Header.Bits = block.Header.GetWorkRequired(fullNode.Network, fullNode.Chain.Tip);
block.Header.UpdateTime(DateTimeOffset.UtcNow, fullNode.Network, fullNode.Chain.Tip);
var coinbase = new Transaction();
coinbase.AddInput(TxIn.CreateCoinbase(fullNode.Chain.Height + 1));
coinbase.AddOutput(new TxOut(fullNode.Network.GetReward(fullNode.Chain.Height + 1), dest.GetAddress()));
block.AddTransaction(coinbase);
if (passedTransactions?.Any() ?? false)
{
passedTransactions = Reorder(passedTransactions);
block.Transactions.AddRange(passedTransactions);
}
block.UpdateMerkleRoot();
while (!block.CheckProofOfWork())
block.Header.Nonce = ++nonce;
blocks.Add(block);
uint256 blockHash = block.GetHash();
var newChain = new ChainedHeader(block.Header, blockHash, fullNode.Chain.Tip);
ChainedHeader oldTip = fullNode.Chain.SetTip(newChain);
fullNode.ConsensusLoop().Puller.InjectBlock(blockHash, new DownloadedBlock { Length = block.GetSerializedSize(), Block = block }, CancellationToken.None);
passedTransactions = Reorder(passedTransactions);
block.Transactions.AddRange(passedTransactions);
}
block.UpdateMerkleRoot();
while (!block.CheckProofOfWork())
block.Header.Nonce = ++nonce;
blocks.Add(block);
uint256 blockHash = block.GetHash();
var newChain = new ChainedHeader(block.Header, blockHash, fullNode.Chain.Tip);
ChainedHeader oldTip = fullNode.Chain.SetTip(newChain);
fullNode.ConsensusLoop().Puller.InjectBlock(blockHash, new DownloadedBlock { Length = block.GetSerializedSize(), Block = block }, CancellationToken.None);
return block;
}
private class TransactionNode
{
public uint256 Hash = null;
......@@ -315,3 +315,4 @@ namespace DeStream.DeStreamD.ForTest
}
}
}
\ No newline at end of file
......@@ -72,9 +72,12 @@ namespace NBitcoin.Networks
genesis.Header.Bits = this.Consensus.PowLimit;
this.Genesis = genesis;
this.Consensus.HashGenesisBlock = genesis.GetHash();
//f79e424c3636eb124bb6d07dcc2266e57575c68b738eb3adae364cd3a6ef2943
Assert(this.Consensus.HashGenesisBlock == uint256.Parse("0x00000e246d7b73b88c9ab55f2e5e94d9e22d471def3df5ea448f5576b1d156b9"));
//Assert(this.Consensus.HashGenesisBlock == uint256.Parse("5c69790173d48daf2a2a111e0baafb0546ebbec9a831f890ff8c7e23fe119885"));
//Assert(this.Consensus.HashGenesisBlock == uint256.Parse("f79e424c3636eb124bb6d07dcc2266e57575c68b738eb3adae364cd3a6ef2943"));
this.Checkpoints = new Dictionary<int, CheckpointInfo>
{
......
using System;
using System.Collections.Generic;
using System.Linq;
using Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers;
namespace Stratis.Bitcoin.IntegrationTests.Builders
{
public class NodeConnectionBuilder
{
private readonly NodeGroupBuilder parentNodeGroupBuilder;
private IDictionary<string, CoreNode> nodes;
private SharedSteps sharedSteps;
private NodeConnectionBuilder()
{
this.sharedSteps = new SharedSteps();
}
public NodeConnectionBuilder(NodeGroupBuilder parentNodeGroupBuilder) : this()
{
this.parentNodeGroupBuilder = parentNodeGroupBuilder;
}
public NodeConnectionBuilder(IDictionary<string, CoreNode> nodesDictionary) : this()
{
this.nodes = nodesDictionary;
}
public NodeConnectionBuilder With(IDictionary<string, CoreNode> nodes)
{
this.nodes = nodes;
return this;
}
public NodeConnectionBuilder Connect(string from, string to)
{
this.nodes[from].CreateRPCClient().AddNode(this.nodes[to].Endpoint, true);
this.sharedSteps.WaitForNodeToSync(this.nodes[from], this.nodes[to]);
return this;
}
public NodeGroupBuilder AndNoMoreConnections()
{
if (this.parentNodeGroupBuilder == null)
throw new NotSupportedException("Pass parent builder into constructor if you need to return to that builder to continue building.");
return this.parentNodeGroupBuilder;
}
public void DisconnectAll()
{
foreach (var node in this.nodes)
{
foreach (var otherNode in this.nodes.Where(x => x.Key != node.Key))
{
node.Value.CreateRPCClient().RemoveNode(otherNode.Value.Endpoint);
}
}
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers;
namespace Stratis.Bitcoin.IntegrationTests.Builders
{
public class NodeGroupBuilder : IDisposable
{
private readonly NodeBuilder nodeBuilder;
private readonly Dictionary<string, CoreNode> nodes;
public NodeGroupBuilder(string testFolder)
{
this.nodeBuilder = NodeBuilder.Create(caller: testFolder);
this.nodes = new Dictionary<string, CoreNode>();
}
public void Dispose()
{
this.nodeBuilder.Dispose();
}
public IDictionary<string, CoreNode> Build()
{
return this.nodes;
}
public NodeGroupBuilder StratisPowNode(string nodeName)
{
this.nodes.Add(nodeName, this.nodeBuilder.CreateStratisPowNode());
return this;
}
public NodeGroupBuilder CreateStratisPowMiningNode(string nodeName)
{
this.nodes.Add(nodeName, this.nodeBuilder.CreateStratisPowMiningNode());
return this;
}
public NodeGroupBuilder CreateStratisPosNode(string nodeName)
{
this.nodes.Add(nodeName, this.nodeBuilder.CreateStratisPosNode());
return this;
}
public NodeGroupBuilder CreateStratisPosApiNode(string nodeName)
{
this.nodes.Add(nodeName, this.nodeBuilder.CreateStratisPosApiNode());
return this;
}
public NodeGroupBuilder NotInIBD()
{
this.nodes.Last().Value.NotInIBD();
return this;
}
public NodeGroupBuilder WithWallet(string walletName, string walletPassword)
{
this.nodes.Last().Value.FullNode.WalletManager().CreateWallet(walletPassword, walletName);
return this;
}
public NodeGroupBuilder Start()
{
this.nodes.Last().Value.Start();
return this;
}
public NodeConnectionBuilder WithConnections()
{
return new NodeConnectionBuilder(this).With(this.nodes);
}
}
}
\ No newline at end of file
using System;
using NBitcoin;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers
{
public class InitialBlockDownloadStateMock : IInitialBlockDownloadState
{
/// <summary>Time until IBD state can be checked.</summary>
private DateTime lockIbdUntil;
/// <summary>A cached result of the IBD method.</summary>
private bool blockDownloadState;
/// <summary>A provider of the date and time.</summary>
protected readonly IDateTimeProvider dateTimeProvider;
private readonly InitialBlockDownloadState innerBlockDownloadState;
public InitialBlockDownloadStateMock(IChainState chainState, Network network, NodeSettings nodeSettings,
ICheckpoints checkpoints)
{
this.lockIbdUntil = DateTime.MinValue;
this.dateTimeProvider = DateTimeProvider.Default;
this.innerBlockDownloadState = new InitialBlockDownloadState(chainState, network, nodeSettings, checkpoints);
}
public bool IsInitialBlockDownload()
{
if (this.lockIbdUntil >= this.dateTimeProvider.GetUtcNow())
return this.blockDownloadState;
return this.innerBlockDownloadState.IsInitialBlockDownload();
}
/// <summary>
/// Sets last IBD status update time and result.
/// </summary>
/// <param name="blockDownloadState">New value for the IBD status, <c>true</c> means the node is considered in IBD.</param>
/// <param name="lockIbdUntil">Time until IBD state won't be changed.</param>
public void SetIsInitialBlockDownload(bool blockDownloadState, DateTime lockIbdUntil)
{
this.lockIbdUntil = lockIbdUntil;
this.blockDownloadState = blockDownloadState;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.Wallet;
namespace Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers
{
public class NetworkSimulator : IDisposable
{
public List<CoreNode> Nodes { get; private set; }
private NodeBuilder nodeBuilder;
public NetworkSimulator([CallerMemberName] string caller = null)
{
this.nodeBuilder = NodeBuilder.Create(caller: caller);
}
public void Initialize(int nodesCount)
{
this.Nodes = new List<CoreNode>();
for (int i = 0; i < nodesCount; ++i)
{
CoreNode node = this.nodeBuilder.CreateStratisPowNode(true);
node.NotInIBD();
node.FullNode.WalletManager().CreateWallet("dummyPassword", "dummyWallet");
var address = node.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("dummyWallet", "account 0"));
var wallet = node.FullNode.WalletManager().GetWalletByName("dummyWallet");
var key = wallet.GetExtendedPrivateKeyForAddress("dummyPassword", address).PrivateKey;
node.SetDummyMinerSecret(new BitcoinSecret(key, node.FullNode.Network));
this.Nodes.Add(node);
}
}
public bool AreAllNodesAtSameHeight()
{
return this.Nodes.Select(i => i.FullNode.Chain.Height).Distinct().Count() == 1;
}
public void Dispose()
{
this.nodeBuilder.Dispose();
}
public bool DidAllNodesReachHeight(int height)
{
return this.Nodes.All(i => i.FullNode.Chain.Height >= height);
}
public void MakeSureEachNodeCanMineAndSync()
{
foreach (var node in this.Nodes)
{
Thread.Sleep(1000);
var currentHeight = node.FullNode.Chain.Height;
node.GenerateStratisWithMiner(1);
TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(node));
TestHelper.WaitLoop(new Func<bool>(delegate { return node.FullNode.Chain.Height == currentHeight + 1; }));
TestHelper.WaitLoop(new Func<bool>(delegate { return AreAllNodesAtSameHeight(); }));
}
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.Consensus.Interfaces;
using Stratis.Bitcoin.Features.MemoryPool;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.Features.Wallet.Interfaces;
namespace Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers
{
internal static class FullNodeExt
{
public static WalletManager WalletManager(this FullNode fullNode)
{
return fullNode.NodeService<IWalletManager>() as WalletManager;
}
public static WalletTransactionHandler WalletTransactionHandler(this FullNode fullNode)
{
return fullNode.NodeService<IWalletTransactionHandler>() as WalletTransactionHandler;
}
public static ConsensusLoop ConsensusLoop(this FullNode fullNode)
{
return fullNode.NodeService<IConsensusLoop>() as ConsensusLoop;
}
public static CoinView CoinView(this FullNode fullNode)
{
return fullNode.NodeService<CoinView>();
}
public static MempoolManager MempoolManager(this FullNode fullNode)
{
return fullNode.NodeService<MempoolManager>();
}
public static BlockStoreManager BlockStoreManager(this FullNode fullNode)
{
return fullNode.NodeService<BlockStoreManager>();
}
public static ChainedHeader HighestPersistedBlock(this FullNode fullNode)
{
return fullNode.NodeService<IBlockRepository>().HighestPersistedBlock;
}
}
public enum CoreNodeState
{
Stopped,
Starting,
Running,
Killed
}
public class NodeConfigParameters : Dictionary<string, string>
{
public void Import(NodeConfigParameters configParameters)
{
foreach (var kv in configParameters)
{
if (!this.ContainsKey(kv.Key))
this.Add(kv.Key, kv.Value);
}
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
foreach (var kv in this)
builder.AppendLine(kv.Key + "=" + kv.Value);
return builder.ToString();
}
}
public class NodeBuilder : IDisposable
{
public string BitcoinD { get; }
public List<CoreNode> Nodes { get; }
public NodeConfigParameters ConfigParameters { get; }
private int lastDataFolderIndex;
private string rootFolder;
public NodeBuilder(string rootFolder, string bitcoindPath)
{
this.lastDataFolderIndex = 0;
this.Nodes = new List<CoreNode>();
this.ConfigParameters = new NodeConfigParameters();
this.rootFolder = rootFolder;
this.BitcoinD = bitcoindPath;
}
public static NodeBuilder Create([CallerMemberName] string caller = null, string version = "0.13.1")
{
KillAnyBitcoinInstances();
caller = Path.Combine("TestData", caller);
CreateTestFolder(caller);
return new NodeBuilder(caller, DownloadBitcoinCore(version));
}
private static string DownloadBitcoinCore(string version)
{
//is a file
if (version.Length >= 2 && version[1] == ':')
{
return version;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var bitcoind = string.Format("TestData/bitcoin-{0}/bin/bitcoind.exe", version);
if (File.Exists(bitcoind))
return bitcoind;
var zip = string.Format("TestData/bitcoin-{0}-win32.zip", version);
string url = string.Format("https://bitcoin.org/bin/bitcoin-core-{0}/" + Path.GetFileName(zip), version);
var client = new HttpClient
{
Timeout = TimeSpan.FromMinutes(10.0)
};
var data = client.GetByteArrayAsync(url).GetAwaiter().GetResult();
File.WriteAllBytes(zip, data);
ZipFile.ExtractToDirectory(zip, new FileInfo(zip).Directory.FullName);
return bitcoind;
}
else
{
string bitcoind = string.Format("TestData/bitcoin-{0}/bin/bitcoind", version);
if (File.Exists(bitcoind))
return bitcoind;
var zip = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
? string.Format("TestData/bitcoin-{0}-x86_64-linux-gnu.tar.gz", version)
: string.Format("TestData/bitcoin-{0}-osx64.tar.gz", version);
string url = string.Format("https://bitcoin.org/bin/bitcoin-core-{0}/" + Path.GetFileName(zip), version);
var client = new HttpClient
{
Timeout = TimeSpan.FromMinutes(10.0)
};
var data = client.GetByteArrayAsync(url).GetAwaiter().GetResult();
File.WriteAllBytes(zip, data);
Process.Start("tar", "-zxvf " + zip + " -C TestData");
return bitcoind;
}
}
private CoreNode CreateNode(NodeRunner runner, Network network, bool start, string configFile = "bitcoin.conf")
{
var node = new CoreNode(runner, this, network, configFile);
this.Nodes.Add(node);
if (start) node.Start();
return node;
}
public CoreNode CreateBitcoinCoreNode(bool start = false)
{
return CreateNode(new BitcoinCoreRunner(this.GetNextDataFolderName(), this.BitcoinD), Network.RegTest, start);
}
public CoreNode CreateStratisPowNode(bool start = false)
{
return CreateNode(new StratisBitcoinPowRunner(this.GetNextDataFolderName()), Network.RegTest, start);
}
public CoreNode CreateStratisPowMiningNode(bool start = false)
{
return CreateNode(new StratisProofOfWorkMiningNode(this.GetNextDataFolderName()), Network.RegTest, start, "stratis.conf");
}
public CoreNode CreateStratisPosNode(bool start = false)
{
return CreateNode(new StratisBitcoinPosRunner(this.GetNextDataFolderName()), Network.RegTest, start, "stratis.conf");
}
public CoreNode CreateStratisPosApiNode(bool start = false)
{
return CreateNode(new StratisPosApiRunner(this.GetNextDataFolderName()), Network.RegTest, start, "stratis.conf");
}
public CoreNode CloneStratisNode(CoreNode cloneNode)
{
var node = new CoreNode(new StratisBitcoinPowRunner(cloneNode.FullNode.Settings.DataFolder.RootPath), this, Network.RegTest, "bitcoin.conf");
this.Nodes.Add(node);
this.Nodes.Remove(cloneNode);
return node;
}
private string GetNextDataFolderName()
{
var dataFolderName = Path.Combine(this.rootFolder, this.lastDataFolderIndex.ToString());
this.lastDataFolderIndex++;
return dataFolderName;
}
public void StartAll()
{
foreach (var node in this.Nodes.Where(n => n.State == CoreNodeState.Stopped))
{
node.Start();
}
}
public void Dispose()
{
foreach (var node in this.Nodes)
node.Kill();
KillAnyBitcoinInstances();
}
internal static void KillAnyBitcoinInstances()
{
while (true)
{
var bitcoinDProcesses = Process.GetProcessesByName("bitcoind");
var applicableBitcoinDProcesses = bitcoinDProcesses.Where(b => b.MainModule.FileName.Contains("Stratis.Bitcoin.IntegrationTests"));
if (!applicableBitcoinDProcesses.Any())
break;
foreach (var process in applicableBitcoinDProcesses)
{
process.Kill();
Thread.Sleep(1000);
}
}
}
internal static void CreateTestFolder(string folderName)
{
var deleteAttempts = 0;
while (deleteAttempts < 50)
{
if (Directory.Exists(folderName))
{
try
{
Directory.Delete(folderName, true);
break;
}
catch
{
deleteAttempts++;
Thread.Sleep(200);
}
}
else
break;
}
if (deleteAttempts >= 50)
throw new Exception(string.Format("The test folder: {0} could not be created.", folderName));
Directory.CreateDirectory(folderName);
}
internal static void CreateDataFolder(string dataFolder)
{
if (!Directory.Exists(dataFolder))
Directory.CreateDirectory(dataFolder);
}
}
}
\ No newline at end of file
using System;
using System.Diagnostics;
using System.IO;
using NBitcoin;
using NBitcoin.Protocol;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Features.Api;
using Stratis.Bitcoin.Features.BlockStore;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Features.MemoryPool;
using Stratis.Bitcoin.Features.Miner;
using Stratis.Bitcoin.Features.RPC;
using Stratis.Bitcoin.Features.Wallet;
namespace Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers
{
public class BitcoinCoreRunner : NodeRunner
{
private string bitcoinD;
public BitcoinCoreRunner(string dataDir, string bitcoinD)
: base(dataDir)
{
this.bitcoinD = bitcoinD;
}
private Process process;
public new bool IsDisposed
{
get { return this.process == null && this.process.HasExited; }
}
public new void Kill()
{
if (!this.IsDisposed)
{
this.process.Kill();
this.process.WaitForExit();
}
}
public override void OnStart()
{
this.process = Process.Start(new FileInfo(this.bitcoinD).FullName, $"-conf=bitcoin.conf -datadir={this.DataFolder} -debug=net");
}
public override void BuildNode()
{
}
}
public abstract class NodeRunner
{
public readonly string DataFolder;
public bool IsDisposed => this.FullNode.State == FullNodeState.Disposed;
public FullNode FullNode { get; set; }
protected NodeRunner(string dataDir)
{
this.DataFolder = dataDir;
}
public abstract void BuildNode();
public abstract void OnStart();
public void Kill()
{
this.FullNode?.Dispose();
}
public void Start()
{
BuildNode();
OnStart();
}
}
public sealed class StratisBitcoinPosRunner : NodeRunner
{
public StratisBitcoinPosRunner(string dataDir)
: base(dataDir)
{
}
public override void BuildNode()
{
var settings = new NodeSettings(Network.StratisRegTest, ProtocolVersion.ALT_PROTOCOL_VERSION, args: new string[] { "-conf=stratis.conf", "-datadir=" + this.DataFolder }, loadConfiguration: false);
this.FullNode = (FullNode)new FullNodeBuilder()
.UseNodeSettings(settings)
.UsePosConsensus()
.UseBlockStore()
.UseMempool()
.UseWallet()
.AddPowPosMining()
.AddRPC()
.MockIBD()
.SubstituteDateTimeProviderFor<MiningFeature>()
.Build();
}
public override void OnStart()
{
this.FullNode.Start();
}
/// <summary>
/// Builds a node with POS miner and RPC enabled.
/// </summary>
/// <param name="dataDir">Data directory that the node should use.</param>
/// <returns>Interface to the newly built node.</returns>
/// <remarks>Currently the node built here does not actually stake as it has no coins in the wallet,
/// but all the features required for it are enabled.</remarks>
public static IFullNode BuildStakingNode(string dataDir, bool staking = true)
{
var nodeSettings = new NodeSettings(args: new string[] { $"-datadir={dataDir}", $"-stake={(staking ? 1 : 0)}", "-walletname=dummy", "-walletpassword=dummy" }, loadConfiguration: false);
var fullNodeBuilder = new FullNodeBuilder(nodeSettings);
IFullNode fullNode = fullNodeBuilder
.UsePosConsensus()
.UseBlockStore()
.UseMempool()
.UseWallet()
.AddPowPosMining()
.AddRPC()
.MockIBD()
.Build();
return fullNode;
}
}
public sealed class StratisPosApiRunner : NodeRunner
{
public StratisPosApiRunner(string dataDir)
: base(dataDir)
{
}
public override void BuildNode()
{
var settings = new NodeSettings(Network.StratisRegTest, ProtocolVersion.ALT_PROTOCOL_VERSION, args: new string[] { "-conf=stratis.conf", "-datadir=" + this.DataFolder }, loadConfiguration: false);
this.FullNode = (FullNode)new FullNodeBuilder()
.UseNodeSettings(settings)
.UsePosConsensus()
.UseBlockStore()
.UseMempool()
.AddPowPosMining()
.UseWallet()
.UseApi()
.AddRPC()
.Build();
}
public override void OnStart()
{
this.FullNode.Start();
}
}
public sealed class StratisBitcoinPowRunner : NodeRunner
{
public StratisBitcoinPowRunner(string dataDir)
: base(dataDir)
{
}
public override void BuildNode()
{
var settings = new NodeSettings(args: new string[] { "-conf=bitcoin.conf", "-datadir=" + this.DataFolder }, loadConfiguration: false);
this.FullNode = (FullNode)new FullNodeBuilder()
.UseNodeSettings(settings)
.UsePowConsensus()
.UseBlockStore()
.UseMempool()
.AddMining()
.UseWallet()
.AddRPC()
.MockIBD()
.Build();
}
public override void OnStart()
{
this.FullNode.Start();
}
}
public sealed class StratisProofOfWorkMiningNode : NodeRunner
{
public StratisProofOfWorkMiningNode(string dataDir)
: base(dataDir)
{
}
public override void BuildNode()
{
var settings = new NodeSettings(Network.StratisRegTest, ProtocolVersion.ALT_PROTOCOL_VERSION, args: new string[] { "-conf=stratis.conf", "-datadir=" + this.DataFolder }, loadConfiguration: false);
this.FullNode = (FullNode)new FullNodeBuilder()
.UseNodeSettings(settings)
.UsePosConsensus()
.UseBlockStore()
.UseMempool()
.AddMining()
.UseWallet()
.AddRPC()
.MockIBD()
.SubstituteDateTimeProviderFor<MiningFeature>()
.Build();
}
public override void OnStart()
{
this.FullNode.Start();
}
}
}
\ No newline at end of file
using System.Linq;
using NBitcoin;
using Stratis.Bitcoin.Features.Consensus.Interfaces;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers;
namespace Stratis.Bitcoin.IntegrationTests
{
public static class CoreNodeExtensions
{
public static Money GetProofOfWorkRewardForMinedBlocks(this CoreNode node, int numberOfBlocks)
{
var powValidator = node.FullNode.NodeService<IPowConsensusValidator>();
var startBlock = node.FullNode.Chain.Height - numberOfBlocks + 1;
var totalReward = Enumerable.Range(startBlock, numberOfBlocks).Sum(p => powValidator.GetProofOfWorkReward(p));
return totalReward;
}
public static Money WalletBalance(this CoreNode node, string walletName)
{
return node.FullNode.WalletManager().GetSpendableTransactionsInWallet(walletName).Sum(s => s.Transaction.Amount);
}
public static int? WalletHeight(this CoreNode node, string walletName)
{
return node.FullNode.WalletManager().GetSpendableTransactionsInWallet(walletName).First().Transaction.BlockHeight;
}
public static int WalletSpendableTransactionCount(this CoreNode node, string walletName)
{
return node.FullNode.WalletManager().GetSpendableTransactionsInWallet(walletName).Count();
}
public static Money GetFee(this CoreNode node, TransactionBuildContext transactionBuildContext)
{
return node.FullNode.WalletTransactionHandler().EstimateFee(transactionBuildContext);
}
}
}
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Builder.Feature;
using Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.IntegrationTests
{
public static class FullNodeTestBuilderExtension
{
/// <summary>
/// Substitute the <see cref="IDateTimeProvider"/> for a given feature.
/// </summary>
/// <typeparam name="T">The feature to substitute the provider for.</typeparam>
public static IFullNodeBuilder SubstituteDateTimeProviderFor<T>(this IFullNodeBuilder fullNodeBuilder)
{
fullNodeBuilder.ConfigureFeature(features =>
{
var feature = features.FeatureRegistrations.FirstOrDefault(f => f.FeatureType == typeof(T));
if (feature != null)
{
feature.FeatureServices(services =>
{
ServiceDescriptor service = services.FirstOrDefault(s => s.ServiceType == typeof(IDateTimeProvider));
if (service != null)
services.Remove(service);
services.AddSingleton<IDateTimeProvider, GenerateCoinsFastDateTimeProvider>();
});
}
});
return fullNodeBuilder;
}
public static IFullNodeBuilder MockIBD(this IFullNodeBuilder fullNodeBuilder)
{
fullNodeBuilder.ConfigureFeature(features =>
{
foreach (IFeatureRegistration feature in features.FeatureRegistrations)
{
feature.FeatureServices(services =>
{
// Get default IBD implementation and replace it with the mock.
ServiceDescriptor ibdService = services.FirstOrDefault(x => x.ServiceType == typeof(IInitialBlockDownloadState));
if (ibdService != null)
{
services.Remove(ibdService);
services.AddSingleton<IInitialBlockDownloadState, InitialBlockDownloadStateMock>();
}
});
}
});
return fullNodeBuilder;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using NBitcoin;
using Stratis.Bitcoin.Features.Consensus;
using Stratis.Bitcoin.Features.Wallet;
using Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers;
namespace Stratis.Bitcoin.IntegrationTests
{
public class SharedSteps
{
public static TransactionBuildContext CreateTransactionBuildContext(
string sendingWalletName,
string sendingAccountName,
string sendingPassword,
ICollection<Recipient> recipients,
FeeType feeType,
int minConfirmations)
{
return new TransactionBuildContext(new WalletAccountReference(sendingWalletName, sendingAccountName), recipients.ToList(), sendingPassword)
{
MinConfirmations = minConfirmations,
FeeType = feeType
};
}
public void MineBlocks(int blockCount, CoreNode node, string accountName, string toWalletName, string withPassword, long expectedFees = 0)
{
this.WaitForNodeToSync(node);
var address = node.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(toWalletName, accountName));
var balanceBeforeMining = node.FullNode.WalletManager()
.GetSpendableTransactionsInWallet(toWalletName)
.Where(x => x.Address == address)
.Sum(s => s.Transaction.Amount);
var wallet = node.FullNode.WalletManager().GetWalletByName(toWalletName);
var extendedPrivateKey = wallet.GetExtendedPrivateKeyForAddress(withPassword, address).PrivateKey;
node.SetDummyMinerSecret(new BitcoinSecret(extendedPrivateKey, node.FullNode.Network));
node.GenerateStratisWithMiner(blockCount);
var balanceAfterMining = node.FullNode.WalletManager()
.GetSpendableTransactionsInWallet(toWalletName)
.Where(x => x.Address == address)
.Sum(s => s.Transaction.Amount);
var balanceIncrease = balanceAfterMining - balanceBeforeMining;
this.WaitForNodeToSync(node);
balanceIncrease.Should().Be(node.GetProofOfWorkRewardForMinedBlocks(blockCount) + expectedFees);
}
public void MinePremineBlocks(CoreNode node, string walletName, string walletAccount, string walletPassword)
{
this.WaitForNodeToSync(node);
var unusedAddress = node.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(walletName, walletAccount));
var wallet = node.FullNode.WalletManager().GetWalletByName(walletName);
var extendedPrivateKey = wallet.GetExtendedPrivateKeyForAddress(walletPassword, unusedAddress).PrivateKey;
node.SetDummyMinerSecret(new BitcoinSecret(extendedPrivateKey, node.FullNode.Network));
node.GenerateStratisWithMiner(2);
this.WaitForNodeToSync(node);
var spendable = node.FullNode.WalletManager().GetSpendableTransactionsInWallet(walletName);
var amountShouldBe = node.FullNode.Network.Consensus.Option<PosConsensusOptions>().PremineReward + node.FullNode.Network.Consensus.Option<PosConsensusOptions>().ProofOfWorkReward;
spendable.Sum(s => s.Transaction.Amount).Should().Be(amountShouldBe);
}
public void WaitForNodeToSync(params CoreNode[] nodes)
{
nodes.ToList().ForEach(n =>
TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(n)));
nodes.Skip(1).ToList().ForEach(
n => TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(nodes.First(), n)));
}
}
}
\ No newline at end of file
......@@ -67,4 +67,9 @@
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Builders\" />
<Folder Include="Extensions\" />
</ItemGroup>
</Project>
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers;
namespace Stratis.Bitcoin.IntegrationTests
{
public class TestHelper
{
public static void WaitLoop(Func<bool> act)
{
var cancel = new CancellationTokenSource(Debugger.IsAttached ? 15 * 60 * 1000 : 30 * 1000);
while (!act())
{
cancel.Token.ThrowIfCancellationRequested();
//Thread.Sleep(50);
}
}
public static bool AreNodesSyncedTemp(CoreNode node1, FullNode node2)
{
if (node1.FullNode.Chain.Tip.HashBlock != node2.Chain.Tip.HashBlock) return false;
if (node1.FullNode.ChainBehaviorState.ConsensusTip.HashBlock != node2.ChainBehaviorState.ConsensusTip.HashBlock) return false;
if (node1.FullNode.GetBlockStoreTip().HashBlock != node2.GetBlockStoreTip().HashBlock) return false;
if (node1.FullNode.MempoolManager().InfoAll().Count != node2.MempoolManager().InfoAll().Count) return false;
if (node1.FullNode.WalletManager().WalletTipHash != node2.WalletManager().WalletTipHash) return false;
//if (node1.CreateRPCClient().GetBestBlockHash() != node2.CreateRPCClient().GetBestBlockHash()) return false;
return true;
}
public static bool AreNodesSynced(CoreNode node1, CoreNode node2)
{
if (node1.FullNode.Chain.Tip.HashBlock != node2.FullNode.Chain.Tip.HashBlock) return false;
if (node1.FullNode.ChainBehaviorState.ConsensusTip.HashBlock != node2.FullNode.ChainBehaviorState.ConsensusTip.HashBlock) return false;
if (node1.FullNode.HighestPersistedBlock().HashBlock != node2.FullNode.HighestPersistedBlock().HashBlock) return false;
if (node1.FullNode.MempoolManager().InfoAll().Count != node2.FullNode.MempoolManager().InfoAll().Count) return false;
if (node1.FullNode.WalletManager().WalletTipHash != node2.FullNode.WalletManager().WalletTipHash) return false;
if (node1.CreateRPCClient().GetBestBlockHash() != node2.CreateRPCClient().GetBestBlockHash()) return false;
return true;
}
public static bool IsNodeSynced(CoreNode node)
{
if (node.FullNode.Chain.Tip.HashBlock != node.FullNode.ChainBehaviorState.ConsensusTip.HashBlock) return false;
if (node.FullNode.Chain.Tip.HashBlock != node.FullNode.HighestPersistedBlock().HashBlock) return false;
if (node.FullNode.Chain.Tip.HashBlock != node.FullNode.WalletManager().WalletTipHash) return false;
return true;
}
public static void TriggerSync(CoreNode node)
{
foreach (var connectedPeer in node.FullNode.ConnectionManager.ConnectedPeers)
connectedPeer.Behavior<ChainHeadersBehavior>().TrySyncAsync().GetAwaiter().GetResult();
}
public static bool IsNodeConnected(CoreNode node)
{
if (node.FullNode.ConnectionManager.ConnectedPeers.Any()) return true;
return false;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment