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

Bugfix

parent 3c8c9867
......@@ -62,18 +62,27 @@ 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);
(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);
var walletManager = (DeStreamWalletManager)node.WalletManager();
//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>());
......
......@@ -33,7 +33,7 @@ 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);
......@@ -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;
......@@ -205,17 +205,17 @@ 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 = 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.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,16 +227,15 @@ 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;
......@@ -244,7 +243,7 @@ namespace DeStream.DeStreamD.ForTest
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()));
coinbase.AddOutput(new TxOut(Get9Billion(), dest.GetAddress()));
block.AddTransaction(coinbase);
if (passedTransactions?.Any() ?? false)
{
......@@ -259,8 +258,9 @@ namespace DeStream.DeStreamD.ForTest
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 System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
using NBitcoin.DataEncoders;
using NBitcoin.Protocol;
using NBitcoin.RPC;
using Stratis.Bitcoin.Configuration.Logging;
using Stratis.Bitcoin.Features.MemoryPool;
using Stratis.Bitcoin.Features.Miner;
using Stratis.Bitcoin.Features.Miner.Interfaces;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.P2P;
using Stratis.Bitcoin.P2P.Peer;
using Stratis.Bitcoin.P2P.Protocol.Payloads;
using Stratis.Bitcoin.Utilities;
using static Stratis.Bitcoin.BlockPulling.BlockPuller;
namespace Stratis.Bitcoin.IntegrationTests.EnvironmentMockUpHelpers
{
public class CoreNode
{
/// <summary>Factory for creating P2P network peers.</summary>
private readonly INetworkPeerFactory networkPeerFactory;
private int[] ports;
private NodeRunner runner;
private readonly NetworkCredential creds;
private List<Transaction> transactions = new List<Transaction>();
private HashSet<OutPoint> locked = new HashSet<OutPoint>();
private Money fee = Money.Coins(0.0001m);
private object lockObject = new object();
/// <summary>Location of the data directory for the node.</summary>
public string DataFolder
{
get { return this.runner.DataFolder; }
}
public IPEndPoint Endpoint { get { return new IPEndPoint(IPAddress.Parse("127.0.0.1"), this.ports[0]); } }
public string Config { get; }
public NodeConfigParameters ConfigParameters { get; } = new NodeConfigParameters();
public CoreNode(NodeRunner runner, NodeBuilder builder, Network network, string configfile)
{
this.runner = runner;
this.State = CoreNodeState.Stopped;
var pass = Encoders.Hex.EncodeData(RandomUtils.GetBytes(20));
this.creds = new NetworkCredential(pass, pass);
this.Config = Path.Combine(this.runner.DataFolder, configfile);
this.ConfigParameters.Import(builder.ConfigParameters);
this.ports = new int[2];
this.FindPorts(this.ports);
var loggerFactory = new ExtendedLoggerFactory();
loggerFactory.AddConsoleWithFilters();
this.networkPeerFactory = new NetworkPeerFactory(network, DateTimeProvider.Default, loggerFactory, new PayloadProvider().DiscoverPayloads(), new SelfEndpointTracker());
}
/// <summary>Get stratis full node if possible.</summary>
public FullNode FullNode
{
get
{
return this.runner.FullNode;
}
}
public void Sync(CoreNode node, bool keepConnection = false)
{
var rpc = this.CreateRPCClient();
var rpc1 = node.CreateRPCClient();
rpc.AddNode(node.Endpoint, true);
while (rpc.GetBestBlockHash() != rpc1.GetBestBlockHash())
{
Thread.Sleep(200);
}
if (!keepConnection)
rpc.RemoveNode(node.Endpoint);
}
public CoreNodeState State { get; private set; }
public int ProtocolPort
{
get { return this.ports[0]; }
}
public void NotInIBD()
{
(this.FullNode.NodeService<IInitialBlockDownloadState>() as InitialBlockDownloadStateMock).SetIsInitialBlockDownload(false, DateTime.UtcNow.AddMinutes(5));
}
public RPCClient CreateRPCClient()
{
return new RPCClient(this.creds, new Uri("http://127.0.0.1:" + this.ports[1].ToString() + "/"), Network.RegTest);
}
public RestClient CreateRESTClient()
{
return new RestClient(new Uri("http://127.0.0.1:" + this.ports[1].ToString() + "/"));
}
public INetworkPeer CreateNetworkPeerClient()
{
return this.networkPeerFactory.CreateConnectedNetworkPeerAsync("127.0.0.1:" + this.ports[0].ToString()).GetAwaiter().GetResult();
}
public void Start()
{
NodeBuilder.CreateDataFolder(this.runner.DataFolder);
var config = new NodeConfigParameters();
config.Add("regtest", "1");
config.Add("rest", "1");
config.Add("server", "1");
config.Add("txindex", "1");
config.Add("rpcuser", this.creds.UserName);
config.Add("rpcpassword", this.creds.Password);
config.Add("port", this.ports[0].ToString());
config.Add("rpcport", this.ports[1].ToString());
config.Add("printtoconsole", "1");
config.Add("keypool", "10");
config.Add("agentprefix", "node" + this.ports[0].ToString());
config.Import(this.ConfigParameters);
File.WriteAllText(this.Config, config.ToString());
lock (this.lockObject)
{
this.runner.Start();
this.State = CoreNodeState.Starting;
}
if (this.runner is BitcoinCoreRunner)
StartBitcoinCoreRunner();
else
StartStratisRunner();
this.State = CoreNodeState.Running;
}
private void StartBitcoinCoreRunner()
{
while (true)
{
try
{
CreateRPCClient().GetBlockHashAsync(0).GetAwaiter().GetResult();
this.State = CoreNodeState.Running;
break;
}
catch { }
Task.Delay(200);
}
}
private void StartStratisRunner()
{
while (true)
{
if (this.runner.FullNode == null)
{
Thread.Sleep(100);
continue;
}
if (this.runner.FullNode.State == FullNodeState.Started)
break;
else
Thread.Sleep(200);
}
}
private void FindPorts(int[] ports)
{
int i = 0;
while (i < ports.Length)
{
var port = RandomUtils.GetUInt32() % 4000;
port = port + 10000;
if (ports.Any(p => p == port))
continue;
try
{
TcpListener l = new TcpListener(IPAddress.Loopback, (int)port);
l.Start();
l.Stop();
ports[i] = (int)port;
i++;
}
catch (SocketException)
{
}
}
}
public void Broadcast(Transaction transaction)
{
using (INetworkPeer peer = this.CreateNetworkPeerClient())
{
peer.VersionHandshakeAsync().GetAwaiter().GetResult();
peer.SendMessageAsync(new InvPayload(transaction)).GetAwaiter().GetResult();
peer.SendMessageAsync(new TxPayload(transaction)).GetAwaiter().GetResult();
this.PingPongAsync(peer).GetAwaiter().GetResult();
}
}
/// <summary>
/// Emit a ping and wait the pong.
/// </summary>
/// <param name="cancellation"></param>
/// <param name="peer"></param>
/// <returns>Latency.</returns>
public async Task<TimeSpan> PingPongAsync(INetworkPeer peer, CancellationToken cancellation = default(CancellationToken))
{
using (var listener = new NetworkPeerListener(peer))
{
var ping = new PingPayload()
{
Nonce = RandomUtils.GetUInt64()
};
DateTimeOffset before = DateTimeOffset.UtcNow;
await peer.SendMessageAsync(ping);
while ((await listener.ReceivePayloadAsync<PongPayload>(cancellation).ConfigureAwait(false)).Nonce != ping.Nonce)
{
}
DateTimeOffset after = DateTimeOffset.UtcNow;
return after - before;
}
}
public void SelectMempoolTransactions()
{
var rpc = this.CreateRPCClient();
var txs = rpc.GetRawMempool();
var tasks = txs.Select(t => rpc.GetRawTransactionAsync(t)).ToArray();
Task.WaitAll(tasks);
this.transactions.AddRange(tasks.Select(t => t.Result).ToArray());
}
public void Split(Money amount, int parts)
{
var rpc = this.CreateRPCClient();
TransactionBuilder builder = new TransactionBuilder(this.FullNode.Network);
builder.AddKeys(rpc.ListSecrets().OfType<ISecret>().ToArray());
builder.AddCoins(rpc.ListUnspent().Select(c => c.AsCoin()));
var secret = this.GetFirstSecret(rpc);
foreach (var part in (amount - this.fee).Split(parts))
{
builder.Send(secret, part);
}
builder.SendFees(this.fee);
builder.SetChange(secret);
var tx = builder.BuildTransaction(true);
this.Broadcast(tx);
}
public void Kill()
{
lock (this.lockObject)
{
this.runner.Kill();
this.State = CoreNodeState.Killed;
}
}
public DateTimeOffset? MockTime { get; set; }
public void SetMinerSecret(BitcoinSecret secret)
{
this.CreateRPCClient().ImportPrivKey(secret);
this.MinerSecret = secret;
}
public void SetDummyMinerSecret(BitcoinSecret secret)
{
this.MinerSecret = secret;
}
public BitcoinSecret MinerSecret { get; private set; }
public async Task<Block[]> GenerateAsync(int blockCount, bool includeUnbroadcasted = true, bool broadcast = true)
{
var rpc = this.CreateRPCClient();
BitcoinSecret dest = this.GetFirstSecret(rpc);
var bestBlock = rpc.GetBestBlockHash();
ConcurrentChain chain = null;
List<Block> blocks = new List<Block>();
DateTimeOffset now = this.MockTime == null ? DateTimeOffset.UtcNow : this.MockTime.Value;
using (INetworkPeer peer = this.CreateNetworkPeerClient())
{
peer.VersionHandshakeAsync().GetAwaiter().GetResult();
chain = bestBlock == peer.Network.GenesisHash ? new ConcurrentChain(peer.Network) : this.GetChain(peer);
for (int i = 0; i < blockCount; i++)
{
uint nonce = 0;
Block block = new Block();
block.Header.HashPrevBlock = chain.Tip.HashBlock;
block.Header.Bits = block.Header.GetWorkRequired(rpc.Network, chain.Tip);
block.Header.UpdateTime(now, rpc.Network, chain.Tip);
var coinbase = new Transaction();
coinbase.AddInput(TxIn.CreateCoinbase(chain.Height + 1));
coinbase.AddOutput(new TxOut(rpc.Network.GetReward(chain.Height + 1), dest.GetAddress()));
block.AddTransaction(coinbase);
if (includeUnbroadcasted)
{
this.transactions = this.Reorder(this.transactions);
block.Transactions.AddRange(this.transactions);
this.transactions.Clear();
}
block.UpdateMerkleRoot();
while (!block.CheckProofOfWork(rpc.Network.Consensus))
block.Header.Nonce = ++nonce;
blocks.Add(block);
chain.SetTip(block.Header);
}
if (broadcast)
await this.BroadcastBlocksAsync(blocks.ToArray(), peer);
}
return blocks.ToArray();
}
/// <summary>
/// Get the chain of headers from the peer (thread safe).
/// </summary>
/// <param name="peer">Peer to get chain from.</param>
/// <param name="hashStop">The highest block wanted.</param>
/// <param name="cancellationToken"></param>
/// <returns>The chain of headers.</returns>
private ConcurrentChain GetChain(INetworkPeer peer, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken))
{
ConcurrentChain chain = new ConcurrentChain(peer.Network);
this.SynchronizeChain(peer, chain, hashStop, cancellationToken);
return chain;
}
/// <summary>
/// Synchronize a given Chain to the tip of the given node if its height is higher. (Thread safe).
/// </summary>
/// <param name="peer">Node to synchronize the chain for.</param>
/// <param name="chain">The chain to synchronize.</param>
/// <param name="hashStop">The location until which it synchronize.</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private IEnumerable<ChainedHeader> SynchronizeChain(INetworkPeer peer, ChainBase chain, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken))
{
ChainedHeader oldTip = chain.Tip;
List<ChainedHeader> headers = this.GetHeadersFromFork(peer, oldTip, hashStop, cancellationToken).ToList();
if (headers.Count == 0)
return new ChainedHeader[0];
ChainedHeader newTip = headers[headers.Count - 1];
if (newTip.Height <= oldTip.Height)
throw new ProtocolException("No tip should have been recieved older than the local one");
foreach (ChainedHeader header in headers)
{
if (!header.Validate(peer.Network))
{
throw new ProtocolException("A header which does not pass proof of work verification has been received");
}
}
chain.SetTip(newTip);
return headers;
}
private async Task AssertStateAsync(INetworkPeer peer, NetworkPeerState peerState, CancellationToken cancellationToken = default(CancellationToken))
{
if ((peerState == NetworkPeerState.HandShaked) && (peer.State == NetworkPeerState.Connected))
await peer.VersionHandshakeAsync(cancellationToken);
if (peerState != peer.State)
throw new InvalidOperationException("Invalid Node state, needed=" + peerState + ", current= " + this.State);
}
public IEnumerable<ChainedHeader> GetHeadersFromFork(INetworkPeer peer, ChainedHeader currentTip, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken))
{
this.AssertStateAsync(peer, NetworkPeerState.HandShaked, cancellationToken).GetAwaiter().GetResult();
using (var listener = new NetworkPeerListener(peer))
{
int acceptMaxReorgDepth = 0;
while (true)
{
// Get before last so, at the end, we should only receive 1 header equals to this one (so we will not have race problems with concurrent GetChains).
BlockLocator awaited = currentTip.Previous == null ? currentTip.GetLocator() : currentTip.Previous.GetLocator();
peer.SendMessageAsync(new GetHeadersPayload()
{
BlockLocators = awaited,
HashStop = hashStop
}).GetAwaiter().GetResult();
while (true)
{
bool isOurs = false;
HeadersPayload headers = null;
using (var headersCancel = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
{
headersCancel.CancelAfter(TimeSpan.FromMinutes(1.0));
try
{
headers = listener.ReceivePayloadAsync<HeadersPayload>(headersCancel.Token).GetAwaiter().GetResult();
}
catch (OperationCanceledException)
{
acceptMaxReorgDepth += 6;
if (cancellationToken.IsCancellationRequested)
throw;
// Send a new GetHeaders.
break;
}
}
// In the special case where the remote node is at height 0 as well as us, then the headers count will be 0.
if ((headers.Headers.Count == 0) && (peer.PeerVersion.StartHeight == 0) && (currentTip.HashBlock == peer.Network.GenesisHash))
yield break;
if ((headers.Headers.Count == 1) && (headers.Headers[0].GetHash() == currentTip.HashBlock))
yield break;
foreach (BlockHeader header in headers.Headers)
{
uint256 hash = header.GetHash();
if (hash == currentTip.HashBlock)
continue;
// The previous headers request timeout, this can arrive in case of big reorg.
if (header.HashPrevBlock != currentTip.HashBlock)
{
int reorgDepth = 0;
ChainedHeader tempCurrentTip = currentTip;
while (reorgDepth != acceptMaxReorgDepth && tempCurrentTip != null && header.HashPrevBlock != tempCurrentTip.HashBlock)
{
reorgDepth++;
tempCurrentTip = tempCurrentTip.Previous;
}
if (reorgDepth != acceptMaxReorgDepth && tempCurrentTip != null)
currentTip = tempCurrentTip;
}
if (header.HashPrevBlock == currentTip.HashBlock)
{
isOurs = true;
currentTip = new ChainedHeader(header, hash, currentTip);
yield return currentTip;
if (currentTip.HashBlock == hashStop)
yield break;
}
else break; // Not our headers, continue receive.
}
if (isOurs)
break; //Go ask for next header.
}
}
}
}
public bool AddToStratisMempool(Transaction trx)
{
var fullNode = (this.runner as StratisBitcoinPowRunner).FullNode;
var state = new MempoolValidationState(true);
return fullNode.MempoolManager().Validator.AcceptToMemoryPool(state, trx).Result;
}
public List<uint256> GenerateStratisWithMiner(int blockCount)
{
return this.FullNode.Services.ServiceProvider.GetService<IPowMining>().GenerateBlocks(new ReserveScript { ReserveFullNodeScript = this.MinerSecret.ScriptPubKey }, (ulong)blockCount, uint.MaxValue);
}
[Obsolete("Please use GenerateStratisWithMiner instead.")]
public Block[] GenerateStratis(int blockCount, List<Transaction> passedTransactions = null, bool broadcast = true)
{
var fullNode = (this.runner as StratisBitcoinPowRunner).FullNode;
BitcoinSecret dest = this.MinerSecret;
List<Block> blocks = new List<Block>();
DateTimeOffset now = this.MockTime == null ? DateTimeOffset.UtcNow : this.MockTime.Value;
#if !NOSOCKET
//for (int i = 0; i < blockCount; i++)
for (int i = 0; i < 3; i++)
{
uint nonce = 0;
Block block = new Block();
block.Header.HashPrevBlock = fullNode.Chain.Tip.HashBlock;
block.Header.Bits = block.Header.GetWorkRequired(fullNode.Network, fullNode.Chain.Tip);
block.Header.UpdateTime(now, 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 = this.Reorder(passedTransactions);
block.Transactions.AddRange(passedTransactions);
}
block.UpdateMerkleRoot();
while (!block.CheckProofOfWork(fullNode.Network.Consensus))
block.Header.Nonce = ++nonce;
blocks.Add(block);
if (broadcast)
{
uint256 blockHash = block.GetHash();
var newChain = new ChainedHeader(block.Header, blockHash, fullNode.Chain.Tip);
var oldTip = fullNode.Chain.SetTip(newChain);
fullNode.ConsensusLoop().Puller.InjectBlock(blockHash, new DownloadedBlock { Length = block.GetSerializedSize(), Block = block }, CancellationToken.None);
//try
//{
// var blockResult = new BlockResult { Block = block };
// fullNode.ConsensusLoop.AcceptBlock(blockResult);
// // similar logic to what's in the full node code
// if (blockResult.Error == null)
// {
// fullNode.ChainBehaviorState.ConsensusTip = fullNode.ConsensusLoop.Tip;
// //if (fullNode.Chain.Tip.HashBlock == blockResult.ChainedHeader.HashBlock)
// //{
// // var unused = cache.FlushAsync();
// //}
// fullNode.Signals.Blocks.Broadcast(block);
// }
//}
//catch (ConsensusErrorException)
//{
// // set back the old tip
// fullNode.Chain.SetTip(oldTip);
//}
}
}
return blocks.ToArray();
#endif
}
public async Task BroadcastBlocksAsync(Block[] blocks)
{
using (INetworkPeer peer = this.CreateNetworkPeerClient())
{
await peer.VersionHandshakeAsync();
await this.BroadcastBlocksAsync(blocks, peer);
}
}
public async Task BroadcastBlocksAsync(Block[] blocks, INetworkPeer peer)
{
foreach (var block in blocks)
{
await peer.SendMessageAsync(new InvPayload(block));
await peer.SendMessageAsync(new BlockPayload(block));
}
await this.PingPongAsync(peer);
}
public Block[] FindBlock(int blockCount = 1, bool includeMempool = true)
{
this.SelectMempoolTransactions();
return this.GenerateAsync(blockCount, includeMempool).GetAwaiter().GetResult();
}
private class TransactionNode
{
public uint256 Hash = null;
public Transaction Transaction = null;
public List<TransactionNode> DependsOn = new List<TransactionNode>();
public TransactionNode(Transaction tx)
{
this.Transaction = tx;
this.Hash = tx.GetHash();
}
}
private List<Transaction> Reorder(List<Transaction> transactions)
{
if (transactions.Count == 0)
return transactions;
var result = new List<Transaction>();
var dictionary = transactions.ToDictionary(t => t.GetHash(), t => new TransactionNode(t));
foreach (var transaction in dictionary.Select(d => d.Value))
{
foreach (var input in transaction.Transaction.Inputs)
{
var node = dictionary.TryGet(input.PrevOut.Hash);
if (node != null)
{
transaction.DependsOn.Add(node);
}
}
}
while (dictionary.Count != 0)
{
foreach (var node in dictionary.Select(d => d.Value).ToList())
{
foreach (var parent in node.DependsOn.ToList())
{
if (!dictionary.ContainsKey(parent.Hash))
node.DependsOn.Remove(parent);
}
if (node.DependsOn.Count == 0)
{
result.Add(node.Transaction);
dictionary.Remove(node.Hash);
}
}
}
return result;
}
private BitcoinSecret GetFirstSecret(RPCClient rpc)
{
if (this.MinerSecret != null)
return this.MinerSecret;
var dest = rpc.ListSecrets().FirstOrDefault();
if (dest == null)
{
var address = rpc.GetNewAddress();
dest = rpc.DumpPrivKey(address);
}
return dest;
}
}
}
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