From 674cf2ce7ab1d02179cccbb91618eecbe43d7d7f Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Sep 2017 12:37:03 -0700 Subject: [PATCH 01/66] sync --- NBlockchain/Interfaces/IBlockRepository.cs | 2 +- NBlockchain/Models/BlockchainOptions.cs | 11 ++--- NBlockchain/Models/StaticNetworkParameters.cs | 3 +- NBlockchain/Services/BlockBuilder.cs | 3 +- .../Database/DefaultBlockRepository.cs | 2 +- NBlockchain/Services/DifficultyCalculator.cs | 7 +-- .../Services/InMemoryBlockRepository.cs | 11 +++-- .../Services/ProofOfWorkBlockNotary.cs | 1 + .../Models/BlockStatistics.cs | 11 +++++ .../Models/PersistedBlock.cs | 2 + .../Services/MongoBlockRepository.cs | 43 +++++++++++++------ Samples/DigitalCurrency/Program.cs | 3 +- ScratchPad/Program.cs | 22 +++++----- 13 files changed, 77 insertions(+), 44 deletions(-) create mode 100644 Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index 476fa74..7c883bd 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -12,6 +12,6 @@ public interface IBlockRepository Task GetNewestBlockHeader(); Task GetNextBlock(byte[] prevBlockId); - Task GetAverageBlockTime(DateTime startUtc, DateTime endUtc); + Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc); } } \ No newline at end of file diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 608b0ce..b7b0b9b 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -123,8 +123,7 @@ internal void FillDefaults() { AddDefault(ServiceLifetime.Singleton, x => new StaticNetworkParameters() { - BlockTime = TimeSpan.FromMinutes(1), - Difficulty = 250, + BlockTime = TimeSpan.FromMinutes(1), HeaderVersion = 1, ExpectedContentThreshold = 0.8m }); @@ -140,9 +139,11 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); + //AddDefault(ServiceLifetime.Singleton); + //AddDefault(ServiceLifetime.Singleton); + //AddDefault(ServiceLifetime.Singleton); + + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton, sp => sp.GetService()); diff --git a/NBlockchain/Models/StaticNetworkParameters.cs b/NBlockchain/Models/StaticNetworkParameters.cs index 89d4e88..72131b8 100644 --- a/NBlockchain/Models/StaticNetworkParameters.cs +++ b/NBlockchain/Models/StaticNetworkParameters.cs @@ -7,8 +7,7 @@ namespace NBlockchain.Models { public class StaticNetworkParameters : INetworkParameters { - public TimeSpan BlockTime { get; set; } - public uint Difficulty { get; set; } + public TimeSpan BlockTime { get; set; } public uint HeaderVersion { get; set; } public decimal ExpectedContentThreshold { get; set; } } diff --git a/NBlockchain/Services/BlockBuilder.cs b/NBlockchain/Services/BlockBuilder.cs index e7beb95..619df0b 100644 --- a/NBlockchain/Services/BlockBuilder.cs +++ b/NBlockchain/Services/BlockBuilder.cs @@ -131,8 +131,7 @@ private async Task AssembleBlock(byte[] prevBlock, uint height, uint diff MerkleRootNode = merkleRoot, Header = new BlockHeader() { - MerkelRoot = merkleRoot.Value, - Timestamp = DateTime.UtcNow.Ticks, + MerkelRoot = merkleRoot.Value, Status = BlockStatus.Unconfirmed, Version = _networkParameters.HeaderVersion, Height = height, diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 4edca43..61e740c 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -70,7 +70,7 @@ public async Task GetGenesisBlockTime() return await Task.FromResult(Blocks.Min(x => x.Entity.Header.Timestamp)); } - public Task GetAverageBlockTime(DateTime startUtc, DateTime endUtc) + public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) { throw new NotImplementedException(); } diff --git a/NBlockchain/Services/DifficultyCalculator.cs b/NBlockchain/Services/DifficultyCalculator.cs index bbfe101..482de45 100644 --- a/NBlockchain/Services/DifficultyCalculator.cs +++ b/NBlockchain/Services/DifficultyCalculator.cs @@ -11,7 +11,8 @@ public class DifficultyCalculator : IDifficultyCalculator private readonly IBlockRepository _blockRepository; private readonly INetworkParameters _parameters; private readonly TimeSpan _sampleInterval = TimeSpan.FromHours(1); - private readonly uint _step = 50; + private readonly uint _step = 1; + private readonly uint _genesisValue = 700; public DifficultyCalculator(IBlockRepository blockRepository, INetworkParameters parameters) { @@ -26,9 +27,9 @@ public async Task CalculateDifficulty(long timestamp) var latestHeader = await _blockRepository.GetNewestBlockHeader(); if (latestHeader == null) - return 0; + return _genesisValue; - var avg = await _blockRepository.GetAverageBlockTime(start, end); + var avg = await _blockRepository.GetAverageBlockTimeInSecs(start, end); var avgBlockTime = TimeSpan.FromTicks(avg); if (_parameters.BlockTime > avgBlockTime) diff --git a/NBlockchain/Services/InMemoryBlockRepository.cs b/NBlockchain/Services/InMemoryBlockRepository.cs index 9c9ef17..158039b 100644 --- a/NBlockchain/Services/InMemoryBlockRepository.cs +++ b/NBlockchain/Services/InMemoryBlockRepository.cs @@ -93,17 +93,20 @@ public async Task IsEmpty() } } - public async Task GetAverageBlockTime(DateTime startUtc, DateTime endUtc) + public async Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) { _resetEvent.WaitOne(); try { var startTicks = startUtc.Ticks; var endTicks = endUtc.Ticks; - var avg = _blocks.Where(x => x.Header.Timestamp > startTicks && x.Header.Timestamp < endTicks && x.Header.Height > 1) - .Average(x => (x.Header.Timestamp - (_blocks.First(y => y.Header.BlockId.SequenceEqual(x.Header.PreviousBlock)).Header.Timestamp))); + var sample = _blocks.Where(x => x.Header.Timestamp > startTicks && x.Header.Timestamp < endTicks && x.Header.Height > 1); + if (sample.Count() == 0) + return 0; - return Convert.ToInt64(avg); + var avg = sample.Average(x => (x.Header.Timestamp - (_blocks.First(y => y.Header.BlockId.SequenceEqual(x.Header.PreviousBlock)).Header.Timestamp))); + + return Convert.ToInt32(TimeSpan.FromTicks(Convert.ToInt64(avg)).TotalSeconds); } finally { diff --git a/NBlockchain/Services/ProofOfWorkBlockNotary.cs b/NBlockchain/Services/ProofOfWorkBlockNotary.cs index 9f11875..8c379b1 100644 --- a/NBlockchain/Services/ProofOfWorkBlockNotary.cs +++ b/NBlockchain/Services/ProofOfWorkBlockNotary.cs @@ -59,6 +59,7 @@ private void VerifyForNonce(BlockHeader header, long nonce, CancellationTokenSou { if (header.Status == BlockStatus.Unconfirmed) { + header.Timestamp = DateTime.UtcNow.Ticks; header.BlockId = hash; header.Nonce = nonce; header.Status = BlockStatus.Confirmed; diff --git a/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs b/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs new file mode 100644 index 0000000..83051de --- /dev/null +++ b/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.MongoDB.Models +{ + public class BlockStatistics + { + public int BlockTime { get; set; } + } +} diff --git a/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs b/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs index 2996c4f..8d61ee5 100644 --- a/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs +++ b/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs @@ -10,6 +10,8 @@ public class PersistedBlock : Block { public ObjectId Id { get; set; } + public BlockStatistics Statistics { get; set; } = new BlockStatistics(); + public PersistedBlock() { } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index e2eb8d0..de7d427 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -42,8 +42,17 @@ static MongoBlockRepository() private IMongoCollection Blocks => _database.GetCollection("nbc.blocks"); public async Task AddBlock(Block block) - { - Blocks.InsertOne(new PersistedBlock(block)); + { + var persisted = new PersistedBlock(block); + var prevHeader = Blocks + .Find(x => x.Header.BlockId == block.Header.PreviousBlock) + .Project(x => x.Header) + .FirstOrDefault(); + + if (prevHeader != null) + persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); + + Blocks.InsertOne(persisted); } public async Task HaveBlock(byte[] blockId) @@ -73,15 +82,23 @@ public async Task GetNextBlock(byte[] prevBlockId) return query.FirstOrDefault(); } - public async Task GetAverageBlockTime(DateTime startUtc, DateTime endUtc) - { - throw new NotImplementedException(); - //var startTicks = startUtc.Ticks; - //var endTicks = endUtc.Ticks; - //var avg = Blocks.AsQueryable().Where(x => x.Header.Timestamp > startTicks && x.Header.Timestamp < endTicks && x.Header.Height > 1) - // .Average(x => (x.Header.Timestamp - (Blocks.AsQueryable().First(y => y.Header.BlockId.SequenceEqual(x.Header.PreviousBlock)).Header.Timestamp))); + public async Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) + { + var startTicks = startUtc.Ticks; + var endTicks = endUtc.Ticks; + + var avgQuery = Blocks.Aggregate() + .Match(x => x.Header.Timestamp > startTicks && x.Header.Timestamp < endTicks && x.Header.Height > 1) + .Group(new BsonDocument { { "_id", "$item" }, { "avg", new BsonDocument("$avg", "$Statistics.BlockTime") } }) + .SingleOrDefault(); + + if (avgQuery != null) + { + if (avgQuery.TryGetValue("avg", out var bOut)) + return Convert.ToInt32(bOut.AsDouble); + } - //return Convert.ToInt64(avg); + return 0; } static bool _indexesCreated = false; @@ -90,9 +107,9 @@ private void EnsureIndexes() { if (!_indexesCreated) { - Blocks.Indexes.CreateOne(Builders.IndexKeys.Hashed(x => x.Header.BlockId), new CreateIndexOptions() { Background = true, Name = "idx_blockid" }); - Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.Height), new CreateIndexOptions() { Background = true, Name = "idx_height" }); - Blocks.Indexes.CreateOne(Builders.IndexKeys.Hashed(x => x.Header.PreviousBlock), new CreateIndexOptions() { Background = true, Name = "idx_prevblock" }); + Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.BlockId), new CreateIndexOptions() { Background = true, Unique = true }); + Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.Height), new CreateIndexOptions() { Background = true }); + Blocks.Indexes.CreateOne(Builders.IndexKeys.Hashed(x => x.Header.PreviousBlock), new CreateIndexOptions() { Background = true }); //Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Transactions.Select(y => y.OriginKey)), new CreateIndexOptions() { Background = true, Name = "idx_origkey" }); //Blocks.Indexes.CreateOne(Builders.IndexKeys.Hashed(x => x.Transactions.Select(y => y.Originator)), new CreateIndexOptions() { Background = true, Name = "idx_origin" }); diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 260064a..b1b0e67 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -36,8 +36,7 @@ private static IServiceProvider ConfigureNode(string db, uint port) x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { - BlockTime = TimeSpan.FromSeconds(10), - Difficulty = 300, + BlockTime = TimeSpan.FromSeconds(10), HeaderVersion = 1, ExpectedContentThreshold = 0.8m }); diff --git a/ScratchPad/Program.cs b/ScratchPad/Program.cs index d209be8..b47eaf8 100644 --- a/ScratchPad/Program.cs +++ b/ScratchPad/Program.cs @@ -58,7 +58,7 @@ private static KeyPair RunMiner(IServiceProvider sp, bool genesis) var miner = sp.GetService(); var network = sp.GetService(); var sigService = sp.GetService(); - var addressEncoder = sp.GetService(); + //var addressEncoder = sp.GetService(); network.Open(); @@ -66,7 +66,7 @@ private static KeyPair RunMiner(IServiceProvider sp, bool genesis) var keys = sigService.GenerateKeyPair(); //var keys2 = sigService.GenerateKeyPair(); - var address = addressEncoder.EncodeAddress(keys.PublicKey, 0); + //var address = addressEncoder.EncodeAddress(keys.PublicKey, 0); miner.Start(keys, genesis); @@ -101,11 +101,12 @@ private static void SendTxn(IServiceProvider sp, KeyPair keys, string address, i private static decimal GetBalance(IServiceProvider sp, KeyPair keys) { - var repo = sp.GetService(); - var addressEncoder = sp.GetService(); - var address = addressEncoder.EncodeAddress(keys.PublicKey, 0); + //var repo = sp.GetService(); + //var addressEncoder = sp.GetService(); + //var address = addressEncoder.EncodeAddress(keys.PublicKey, 0); - return repo.GetAccountBalance(address); + //return repo.GetAccountBalance(address); + return 0; } private static async void CheckBalance(string name, IServiceProvider sp, KeyPair keys) @@ -129,7 +130,7 @@ private static KeyPair RunNode(IServiceProvider sp) var node = sp.GetService(); var network = sp.GetService(); var sigService = sp.GetService(); - var addressEncoder = sp.GetService(); + //var addressEncoder = sp.GetService(); var keys = sigService.GenerateKeyPair(); network.Open(); @@ -144,19 +145,18 @@ private static IServiceProvider ConfigureNode(string db, uint port, string[] pee services.AddBlockchain(x => { //x.UseMongoDB(@"mongodb://localhost:27017", db) - x.UseTransactionRepository(); + //x.UseTransactionRepository(); x.UseTcpPeerNetwork(port); x.AddPeerDiscovery(sp => new StaticPeerDiscovery(peers)); //x.UseMulticastDiscovery("test", "224.100.0.1", 8088); x.AddTransactionType(); x.AddTransactionType(); - x.AddTransactionRule(); + //x.AddTransactionRule(); x.AddTransactionRule(); x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { - BlockTime = TimeSpan.FromSeconds(10), - Difficulty = 200, + BlockTime = TimeSpan.FromSeconds(10), HeaderVersion = 1, ExpectedContentThreshold = 0.8m }); From bcc530b10fc90afaf6f705cf93c783aa6e81ba8a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Sep 2017 16:23:15 -0700 Subject: [PATCH 02/66] sync --- NBlockchain/Services/Net/TcpPeerNetwork.cs | 10 +++++----- NBlockchain/Services/ProofOfWorkBlockNotary.cs | 4 ++-- Samples/DigitalCurrency/Program.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index d002845..d6d90ac 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -247,7 +247,7 @@ public void Open() { _incomingSocket.Bind($"tcp://*:{_port}"); _poller.Add(_incomingSocket); - _poller.RunAsync(); + _poller.RunAsync(); _houseKeeper = new NetMQTimer(TimeSpan.FromSeconds(30)); _houseKeeper.Elapsed += HouseKeeper_Elapsed; _poller.Add(_houseKeeper); @@ -486,7 +486,7 @@ public void RequestNextBlock(byte[] blockId) { Task.Factory.StartNew(async () => { - var incoming = GetIncomingPeers(); + var incoming = GetIncomingPeers().Where(x => x != NodeId); foreach (var peerId in incoming) { _logger.LogDebug($"Requesting block from incoming peer {peerId}"); @@ -496,13 +496,13 @@ public void RequestNextBlock(byte[] blockId) .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) .SendFrame(blockId); - await Task.Delay(TimeSpan.FromSeconds(30)); + await Task.Delay(TimeSpan.FromSeconds(5)); if ((await _blockRepository.GetNextBlock(blockId)) != null) return; } - var outgoing = GetOutgoingPeers(); + var outgoing = GetOutgoingPeers().Where(x => x != NodeId); foreach (var peerId in outgoing) { _logger.LogDebug($"Requesting block from outgoing peer {peerId}"); @@ -510,7 +510,7 @@ public void RequestNextBlock(byte[] blockId) .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) .SendFrame(blockId); - await Task.Delay(TimeSpan.FromSeconds(30)); + await Task.Delay(TimeSpan.FromSeconds(5)); if ((await _blockRepository.GetNextBlock(blockId)) != null) return; diff --git a/NBlockchain/Services/ProofOfWorkBlockNotary.cs b/NBlockchain/Services/ProofOfWorkBlockNotary.cs index 8c379b1..27541cd 100644 --- a/NBlockchain/Services/ProofOfWorkBlockNotary.cs +++ b/NBlockchain/Services/ProofOfWorkBlockNotary.cs @@ -31,8 +31,8 @@ public async Task ConfirmBlock(Block block, CancellationToken cancellationToken) var opts = new ExecutionDataflowBlockOptions() { - MaxDegreeOfParallelism = Environment.ProcessorCount, - BoundedCapacity = Environment.ProcessorCount + 1 + MaxDegreeOfParallelism = 1, //Environment.ProcessorCount, + BoundedCapacity = 2 //Environment.ProcessorCount + 1 }; var actionBlock = new ActionBlock(nonce => VerifyForNonce(block.Header, nonce, cancellationTokenSource), opts); diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index b1b0e67..3c32729 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -53,7 +53,7 @@ private static IServiceProvider ConfigureNode(string db, uint port) static void Main(string[] args) { - var serviceProvider = ConfigureNode("DigitalCurrency", 10500); + var serviceProvider = ConfigureNode("DigitalCurrency2", 10500); _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); From a5a3343bc617cfdbcc27cb4b311b961985cf6a29 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Sep 2017 20:30:05 -0700 Subject: [PATCH 03/66] sync --- .../{IBlockBuilder.cs => IBlockMiner.cs} | 2 +- NBlockchain/Models/BlockchainOptions.cs | 2 +- NBlockchain/NBlockchain.csproj | 6 ++--- .../{BlockBuilder.cs => BlockMiner.cs} | 12 +++++----- NBlockchain/Services/DifficultyCalculator.cs | 2 +- NBlockchain/Services/Net/TcpPeerNetwork.cs | 23 +++++++++++-------- NBlockchain/Services/NodeHost.cs | 5 +++- .../NBlockchain.MongoDB.csproj | 6 ++--- Samples/DigitalCurrency/Program.cs | 6 ++--- ScratchPad/Program.cs | 2 +- .../NodeSync/NodeOnboardingScenarios.cs | 3 +-- 11 files changed, 37 insertions(+), 32 deletions(-) rename NBlockchain/Interfaces/{IBlockBuilder.cs => IBlockMiner.cs} (86%) rename NBlockchain/Services/{BlockBuilder.cs => BlockMiner.cs} (89%) diff --git a/NBlockchain/Interfaces/IBlockBuilder.cs b/NBlockchain/Interfaces/IBlockMiner.cs similarity index 86% rename from NBlockchain/Interfaces/IBlockBuilder.cs rename to NBlockchain/Interfaces/IBlockMiner.cs index 203c5e2..b289073 100644 --- a/NBlockchain/Interfaces/IBlockBuilder.cs +++ b/NBlockchain/Interfaces/IBlockMiner.cs @@ -5,7 +5,7 @@ namespace NBlockchain.Interfaces { - public interface IBlockBuilder + public interface IBlockMiner { void Start(KeyPair builderKeys, bool genesis); void Stop(); diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index b7b0b9b..528b3bd 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -134,7 +134,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); - AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Singleton); diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index a6e025b..b875642 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 0.1.0-alpha - 0.1.0.0 - 0.1.0.0 + 0.2.0-alpha + 0.2.0.0 + 0.2.0.0 https://github.com/danielgerlag/NBlockchain https://github.com/danielgerlag/NBlockchain.git git diff --git a/NBlockchain/Services/BlockBuilder.cs b/NBlockchain/Services/BlockMiner.cs similarity index 89% rename from NBlockchain/Services/BlockBuilder.cs rename to NBlockchain/Services/BlockMiner.cs index 619df0b..3df7a47 100644 --- a/NBlockchain/Services/BlockBuilder.cs +++ b/NBlockchain/Services/BlockMiner.cs @@ -13,7 +13,7 @@ namespace NBlockchain.Services { - public class BlockBuilder : IBlockBuilder + public class BlockMiner : IBlockMiner { private readonly INetworkParameters _networkParameters; private readonly ITransactionKeyResolver _transactionKeyResolver; @@ -36,7 +36,7 @@ public class BlockBuilder : IBlockBuilder private CancellationTokenSource _blockCancelToken; - public BlockBuilder(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IPendingTransactionList pendingTransactionList, IBlockRepository blockRepository, IBlockReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) + public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IPendingTransactionList pendingTransactionList, IBlockRepository blockRepository, IBlockReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) { _networkParameters = networkParameters; _peerNetwork= peerNetwork; @@ -46,7 +46,7 @@ public BlockBuilder(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeB _difficultyCalculator = difficultyCalculator; _transactionKeyResolver = transactionKeyResolver; _merkleTreeBuilder = merkleTreeBuilder; - _logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); _blockRepository = blockRepository; _pendingTransactionList = pendingTransactionList; _pendingTransactionList.Changed += PendingTransactionList_Changed; @@ -58,7 +58,7 @@ public void Start(KeyPair builderKeys, bool genesis) _builderKeys = builderKeys; _buildGenesis = genesis; _buildCancelToken = new CancellationTokenSource(); - _buildTask = Task.Factory.StartNew(BuildTask); + _buildTask = Task.Factory.StartNew(Mine); } public void Stop() @@ -66,7 +66,7 @@ public void Stop() _buildCancelToken.Cancel(); } - private async void BuildTask() + private async void Mine() { while (!_buildCancelToken.IsCancellationRequested) { @@ -80,7 +80,7 @@ private async void BuildTask() continue; } prevHeader = new BlockHeader() { BlockId = Block.HeadKey, Height = 0, Timestamp = DateTime.UtcNow.Ticks }; - _logger.LogInformation($"Building genesis block"); + _logger.LogInformation($"Mining genesis block"); } var difficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); var block = await AssembleBlock(prevHeader.BlockId, prevHeader.Height + 1, difficulty, _blockCancelToken.Token); diff --git a/NBlockchain/Services/DifficultyCalculator.cs b/NBlockchain/Services/DifficultyCalculator.cs index 482de45..492f402 100644 --- a/NBlockchain/Services/DifficultyCalculator.cs +++ b/NBlockchain/Services/DifficultyCalculator.cs @@ -30,7 +30,7 @@ public async Task CalculateDifficulty(long timestamp) return _genesisValue; var avg = await _blockRepository.GetAverageBlockTimeInSecs(start, end); - var avgBlockTime = TimeSpan.FromTicks(avg); + var avgBlockTime = TimeSpan.FromSeconds(avg); if (_parameters.BlockTime > avgBlockTime) return latestHeader.Difficulty + _step; diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index d6d90ac..d534fcd 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -96,7 +96,7 @@ private async void IncomingSocketReceiveReady(object sender, NetMQSocketEventArg _incomingSocket.SendMoreFrame(message[0].Buffer) .SendMoreFrame(NodeId.ToByteArray()) .SendMoreFrame(ConvertOp(MessageOp.Identify)) - .SendFrame(message[2].Buffer); + .TrySendFrame(message[2].Buffer); break; case MessageOp.Disconnect: @@ -245,7 +245,7 @@ private async Task ProcessTransaction(NetMQMessage message, Guid originId) public void Open() { - _incomingSocket.Bind($"tcp://*:{_port}"); + _incomingSocket.Bind($"tcp://*:{_port}"); _poller.Add(_incomingSocket); _poller.RunAsync(); _houseKeeper = new NetMQTimer(TimeSpan.FromSeconds(30)); @@ -266,8 +266,8 @@ private void OnboardPeer(string connStr) peer.ReceiveReady += Peer_ReceiveReady; peer.Connect(connStr); _poller.Add(peer); - peer.SendMoreFrame(ConvertOp(MessageOp.Connect)) - .SendFrame(connStr); + peer.SendMoreFrame(ConvertOp(MessageOp.Connect)) + .TrySendFrame(connStr); } catch (Exception ex) { @@ -282,13 +282,13 @@ public void Close() _incomingSocket .SendMoreFrame(peerId.ToByteArray()) .SendMoreFrame(NodeId.ToByteArray()) - .SendFrame(ConvertOp(MessageOp.Disconnect)); + .TrySendFrame(ConvertOp(MessageOp.Disconnect)); } foreach (var peerId in GetOutgoingPeers()) { _outgoingSockets[peerId] - .SendFrame(ConvertOp(MessageOp.Disconnect)); + .TrySendFrame(ConvertOp(MessageOp.Disconnect)); _poller.Remove(_outgoingSockets[peerId]); _outgoingSockets[peerId].Close(); @@ -425,7 +425,7 @@ private void SendBlock(IOutgoingSocket socket, bool tail, Guid? peerId, byte[] d msg.Append(BitConverter.GetBytes(hopCount)); msg.Append(data); - socket.SendMultipartMessage(msg); + socket.TrySendMultipartMessage(msg); } catch (Exception ex) { @@ -474,7 +474,7 @@ private void SendTxn(IOutgoingSocket socket, Guid? peerId, byte[] data, int hopC msg.Append(BitConverter.GetBytes(hopCount)); msg.Append(data); - socket.SendMultipartMessage(msg); + socket.TrySendMultipartMessage(msg); } catch (Exception ex) { @@ -494,21 +494,24 @@ public void RequestNextBlock(byte[] blockId) .SendMoreFrame(peerId.ToByteArray()) .SendMoreFrame(NodeId.ToByteArray()) .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) - .SendFrame(blockId); + .TrySendFrame(blockId); await Task.Delay(TimeSpan.FromSeconds(5)); if ((await _blockRepository.GetNextBlock(blockId)) != null) return; } + }); + Task.Factory.StartNew(async () => + { var outgoing = GetOutgoingPeers().Where(x => x != NodeId); foreach (var peerId in outgoing) { _logger.LogDebug($"Requesting block from outgoing peer {peerId}"); _outgoingSockets[peerId] .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) - .SendFrame(blockId); + .TrySendFrame(blockId); await Task.Delay(TimeSpan.FromSeconds(5)); diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 676b331..6edaec5 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -64,6 +64,9 @@ public async Task RecieveTail(Block block) if (!block.Header.PreviousBlock.SequenceEqual(prevHeader.BlockId)) return PeerDataResult.Ignore; + if (block.Header.Timestamp < prevHeader.Timestamp) + return PeerDataResult.Ignore; + var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); if (block.Header.Difficulty < expectedDifficulty) @@ -176,7 +179,7 @@ private async void GetMissingBlocks(object state) return; } - if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) + //if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) { _logger.LogDebug($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); _peerNetwork.RequestNextBlock(prevHeader.BlockId); diff --git a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj index b4ad546..bb4fa6c 100644 --- a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj +++ b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.1.0-alpha + 0.2.0-alpha MongoDB persistence store for NBlockchain https://github.com/danielgerlag/NBlockchain.git https://github.com/danielgerlag/NBlockchain git Blockchain - 0.1.0.0 - 0.1.0.0 + 0.2.0.0 + 0.2.0.0 true diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 3c32729..709b9b7 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -13,7 +13,7 @@ namespace DigitalCurrency class Program { private static INodeHost _host; - private static IBlockBuilder _miner; + private static IBlockMiner _miner; private static IPeerNetwork _network; private static ISignatureService _sigService; private static IAddressEncoder _addressEncoder; @@ -53,10 +53,10 @@ private static IServiceProvider ConfigureNode(string db, uint port) static void Main(string[] args) { - var serviceProvider = ConfigureNode("DigitalCurrency2", 10500); + var serviceProvider = ConfigureNode("DigitalCurrency", 10500); _host = serviceProvider.GetService(); - _miner = serviceProvider.GetService(); + _miner = serviceProvider.GetService(); _network = serviceProvider.GetService(); _sigService = serviceProvider.GetService(); _addressEncoder = serviceProvider.GetService(); diff --git a/ScratchPad/Program.cs b/ScratchPad/Program.cs index b47eaf8..ed4b13a 100644 --- a/ScratchPad/Program.cs +++ b/ScratchPad/Program.cs @@ -55,7 +55,7 @@ static void Main(string[] args) private static KeyPair RunMiner(IServiceProvider sp, bool genesis) { var node = sp.GetService(); - var miner = sp.GetService(); + var miner = sp.GetService(); var network = sp.GetService(); var sigService = sp.GetService(); //var addressEncoder = sp.GetService(); diff --git a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs index 1cb4a1d..c8ec8e5 100644 --- a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs +++ b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs @@ -27,8 +27,7 @@ private static IServiceProvider ConfigureNode(uint port, ICollection pee x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { - BlockTime = TimeSpan.FromSeconds(10), - Difficulty = 200, + BlockTime = TimeSpan.FromSeconds(10), HeaderVersion = 1, ExpectedContentThreshold = 0.8m }); From 417ca21b13612e784ddd09e0a49fb56c99430555 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Sep 2017 20:43:40 -0700 Subject: [PATCH 04/66] sync --- NBlockchain/Services/Net/TcpPeerNetwork.cs | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index d534fcd..df7c9f7 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -252,8 +252,8 @@ public void Open() _houseKeeper.Elapsed += HouseKeeper_Elapsed; _poller.Add(_houseKeeper); _houseKeeper.Enable = true; - DiscoverPeers(); DiscoverOwnConnectionStrings(); + DiscoverPeers(); AdvertiseToPeers(); } @@ -341,6 +341,8 @@ private async void SharePeers(object state) private void HouseKeeper_Elapsed(object sender, NetMQTimerEventArgs e) { _logger.LogDebug("Performing house keeping"); + DiscoverPeers(); + AdvertiseToPeers(); ConnectOut(); } @@ -352,22 +354,24 @@ private void ConnectOut() var actual = 0; var counter = 0; - - while ((actual < target) && (counter < _peerRoundRobin.Count)) + lock (_peerRoundRobin) { - if (_peerRoundRobin.TryDequeue(out var kp)) + while ((actual < target) && (counter < _peerRoundRobin.Count)) { - _peerRoundRobin.Enqueue(kp); - counter++; - if (_outgoingConnectionStrings.ContainsKey(kp.ConnectionString)) + if (_peerRoundRobin.TryDequeue(out var kp)) { - if (_outgoingSockets.ContainsKey(_outgoingConnectionStrings[kp.ConnectionString])) - continue; + _peerRoundRobin.Enqueue(kp); + counter++; + if (_outgoingConnectionStrings.ContainsKey(kp.ConnectionString)) + { + if (_outgoingSockets.ContainsKey(_outgoingConnectionStrings[kp.ConnectionString])) + continue; + } + _logger.LogDebug($"Connecting to {kp.ConnectionString}"); + OnboardPeer(kp.ConnectionString); + actual++; } - _logger.LogDebug($"Connecting to {kp.ConnectionString}"); - OnboardPeer(kp.ConnectionString); - actual++; - } + } } } From c41398bcaae5b2847b8846edfd324042d8feeb68 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 3 Sep 2017 21:30:21 -0700 Subject: [PATCH 05/66] Update readme.md --- Samples/DigitalCurrency/readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Samples/DigitalCurrency/readme.md b/Samples/DigitalCurrency/readme.md index 9c598e7..36cf356 100644 --- a/Samples/DigitalCurrency/readme.md +++ b/Samples/DigitalCurrency/readme.md @@ -155,9 +155,8 @@ services.AddBlockchain(x => x.UseParameters(new StaticNetworkParameters() { BlockTime = TimeSpan.FromSeconds(10), - Difficulty = 300, HeaderVersion = 1, ExpectedContentThreshold = 0.8m }); }); -``` \ No newline at end of file +``` From e6d056c99efc8518fccb273c6260ae3b00a38083 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 4 Sep 2017 07:59:46 -0700 Subject: [PATCH 06/66] sync --- NBlockchain/Services/Net/TcpPeerNetwork.cs | 131 ++++++++++++++------- NBlockchain/Services/NodeHost.cs | 2 +- 2 files changed, 89 insertions(+), 44 deletions(-) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index df7c9f7..52c809f 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -33,9 +33,11 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly ConcurrentQueue _peerRoundRobin = new ConcurrentQueue(); private readonly ConcurrentDictionary _outgoingConnectionStrings = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _outgoingSockets = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _outgoingSockets = new ConcurrentDictionary(); private readonly ConcurrentDictionary _incomingPeerLastContact = new ConcurrentDictionary(); + private readonly AutoResetEvent _incomingEvent = new AutoResetEvent(true); + private Timer _sharePeersTimer; private readonly RouterSocket _incomingSocket = new RouterSocket(); @@ -58,11 +60,28 @@ public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable message = e.Socket.ReceiveMultipartMessage()); + + if (message == null) + return; if (message.FrameCount < 2) { @@ -85,7 +104,7 @@ private async void IncomingSocketReceiveReady(object sender, NetMQSocketEventArg await ProcessTransaction(message, clientId); break; case MessageOp.BlockRequest: - await ProcessBlockRequest(message, e.Socket, clientId); + await ProcessBlockRequest(message, e.Socket, _incomingEvent, clientId); break; case MessageOp.PeerShare: if (IsSharablePeer(message[2].ConvertToString())) @@ -93,10 +112,13 @@ private async void IncomingSocketReceiveReady(object sender, NetMQSocketEventArg break; case MessageOp.Connect: _logger.LogDebug("Recv connect from {0}", clientId); - _incomingSocket.SendMoreFrame(message[0].Buffer) + RunProtected(_incomingEvent, () => + { + _incomingSocket.SendMoreFrame(message[0].Buffer) .SendMoreFrame(NodeId.ToByteArray()) .SendMoreFrame(ConvertOp(MessageOp.Identify)) .TrySendFrame(message[2].Buffer); + }); break; case MessageOp.Disconnect: @@ -136,7 +158,7 @@ private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) await ProcessTransaction(message, serverId); break; case MessageOp.BlockRequest: - await ProcessBlockRequest(message, e.Socket, null); + await ProcessBlockRequest(message, _outgoingSockets[serverId].Socket, _outgoingSockets[serverId].ResetEvent, null); break; case MessageOp.PeerShare: if (IsSharablePeer(message[2].ConvertToString())) @@ -145,7 +167,7 @@ private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) case MessageOp.Identify: _logger.LogDebug("Recv identify from {0}", serverId); - _outgoingSockets[serverId] = e.Socket; + _outgoingSockets[serverId] = new OutgoingPeer(e.Socket); var peerConStr = message[2].ConvertToString(); _outgoingConnectionStrings[peerConStr] = serverId; foreach (var prr in _peerRoundRobin.Where(x => x.ConnectionString == peerConStr).ToList()) @@ -154,9 +176,9 @@ private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) case MessageOp.Disconnect: _logger.LogDebug("Recv disconnect from {0}", serverId); - _poller.Remove(e.Socket); - e.Socket.Close(); _outgoingSockets.TryRemove(serverId, out var sock); + _poller.Remove(e.Socket); + e.Socket.Close(); break; } } @@ -166,15 +188,15 @@ private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) } } - private async Task ProcessBlockRequest(NetMQMessage message, IOutgoingSocket socket, Guid? peerId) + private async Task ProcessBlockRequest(NetMQMessage message, IOutgoingSocket socket, AutoResetEvent evt, Guid? peerId) { var prevBlockId = message[2].Buffer; var block = await _blockRepository.GetNextBlock(prevBlockId); if (block != null) { _logger.LogDebug("Responding to block request"); - var data = SerializeObject(block); - SendBlock(socket, false, peerId, data, -1); + var data = SerializeObject(block); + SendBlock(socket, evt, false, peerId, data, -1); } else { @@ -200,7 +222,7 @@ private async Task ProcessBlock(NetMQMessage message, Guid originId, bool tail) var peerList = GetIncomingPeers().Where(x => x != originId); Parallel.ForEach(peerList, peerId => { - SendBlock(_incomingSocket, tail, peerId, message[3].Buffer, hopCount + 1); + SendBlock(_incomingSocket, _incomingEvent, tail, peerId, message[3].Buffer, hopCount + 1); }); }); @@ -209,7 +231,7 @@ private async Task ProcessBlock(NetMQMessage message, Guid originId, bool tail) var peerList = GetOutgoingPeers().Where(x => x != originId); Parallel.ForEach(peerList, peerId => { - SendBlock(_outgoingSockets[peerId], tail, null, message[3].Buffer, hopCount + 1); + SendBlock(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, tail, null, message[3].Buffer, hopCount + 1); }); }); } @@ -223,21 +245,21 @@ private async Task ProcessTransaction(NetMQMessage message, Guid originId) if ((result == PeerDataResult.Relay) && (hopCount > -1)) { - var incomingTask = Task.Factory.StartNew(() => + var outgoingTask = Task.Factory.StartNew(() => { - var peerList = GetIncomingPeers().Where(x => x != originId); + var peerList = GetOutgoingPeers().Where(x => x != originId); Parallel.ForEach(peerList, peerId => { - SendTxn(_incomingSocket, peerId, message[3].Buffer, hopCount + 1); + SendTxn(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, null, message[3].Buffer, hopCount + 1); }); }); - var outgoingTask = Task.Factory.StartNew(() => + var incomingTask = Task.Factory.StartNew(() => { - var peerList = GetOutgoingPeers().Where(x => x != originId); + var peerList = GetIncomingPeers().Where(x => x != originId); Parallel.ForEach(peerList, peerId => { - SendTxn(_outgoingSockets[peerId], null, message[3].Buffer, hopCount + 1); + SendTxn(_incomingSocket, _incomingEvent, peerId, message[3].Buffer, hopCount + 1); }); }); } @@ -279,19 +301,25 @@ public void Close() { foreach (var peerId in GetIncomingPeers()) { - _incomingSocket - .SendMoreFrame(peerId.ToByteArray()) - .SendMoreFrame(NodeId.ToByteArray()) - .TrySendFrame(ConvertOp(MessageOp.Disconnect)); + RunProtected(_incomingEvent, () => + { + _incomingSocket + .SendMoreFrame(peerId.ToByteArray()) + .SendMoreFrame(NodeId.ToByteArray()) + .TrySendFrame(ConvertOp(MessageOp.Disconnect)); + }); } foreach (var peerId in GetOutgoingPeers()) { - _outgoingSockets[peerId] - .TrySendFrame(ConvertOp(MessageOp.Disconnect)); + RunProtected(_outgoingSockets[peerId].ResetEvent, () => + { + _outgoingSockets[peerId].Socket + .TrySendFrame(ConvertOp(MessageOp.Disconnect)); - _poller.Remove(_outgoingSockets[peerId]); - _outgoingSockets[peerId].Close(); + _poller.Remove(_outgoingSockets[peerId].Socket); + _outgoingSockets[peerId].Socket.Close(); + }); } _outgoingSockets.Clear(); @@ -396,7 +424,7 @@ public void BroadcastTail(Block block) { Parallel.ForEach(incoming, peerId => { - SendBlock(_incomingSocket, true, peerId, data, 0); + SendBlock(_incomingSocket, _incomingEvent, true, peerId, data, 0); }); }); @@ -404,12 +432,12 @@ public void BroadcastTail(Block block) { Parallel.ForEach(outgoing, peerId => { - SendBlock(_outgoingSockets[peerId], true, null, data, 0); + SendBlock(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, true, null, data, 0); }); }); } - private void SendBlock(IOutgoingSocket socket, bool tail, Guid? peerId, byte[] data, int hopCount) + private void SendBlock(IOutgoingSocket socket, AutoResetEvent resetEvt, bool tail, Guid? peerId, byte[] data, int hopCount) { try { @@ -429,7 +457,7 @@ private void SendBlock(IOutgoingSocket socket, bool tail, Guid? peerId, byte[] d msg.Append(BitConverter.GetBytes(hopCount)); msg.Append(data); - socket.TrySendMultipartMessage(msg); + RunProtected(resetEvt, () => socket.TrySendMultipartMessage(msg)); } catch (Exception ex) { @@ -446,7 +474,7 @@ public void BroadcastTransaction(TransactionEnvelope transaction) var incoming = GetIncomingPeers(); Parallel.ForEach(incoming, peerId => { - SendTxn(_incomingSocket, peerId, data, 0); + SendTxn(_incomingSocket, _incomingEvent, peerId, data, 0); }); }); @@ -455,12 +483,12 @@ public void BroadcastTransaction(TransactionEnvelope transaction) var outgoing = GetOutgoingPeers(); Parallel.ForEach(outgoing, peerId => { - SendTxn(_outgoingSockets[peerId], null, data, 0); + SendTxn(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, null, data, 0); }); }); } - private void SendTxn(IOutgoingSocket socket, Guid? peerId, byte[] data, int hopCount) + private void SendTxn(IOutgoingSocket socket, AutoResetEvent resetEvt, Guid? peerId, byte[] data, int hopCount) { try { @@ -478,7 +506,7 @@ private void SendTxn(IOutgoingSocket socket, Guid? peerId, byte[] data, int hopC msg.Append(BitConverter.GetBytes(hopCount)); msg.Append(data); - socket.TrySendMultipartMessage(msg); + RunProtected(resetEvt, () => socket.TrySendMultipartMessage(msg)); } catch (Exception ex) { @@ -494,11 +522,14 @@ public void RequestNextBlock(byte[] blockId) foreach (var peerId in incoming) { _logger.LogDebug($"Requesting block from incoming peer {peerId}"); - _incomingSocket - .SendMoreFrame(peerId.ToByteArray()) - .SendMoreFrame(NodeId.ToByteArray()) - .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) - .TrySendFrame(blockId); + RunProtected(_incomingEvent, () => + { + _incomingSocket + .SendMoreFrame(peerId.ToByteArray()) + .SendMoreFrame(NodeId.ToByteArray()) + .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) + .TrySendFrame(blockId); + }); await Task.Delay(TimeSpan.FromSeconds(5)); @@ -513,9 +544,12 @@ public void RequestNextBlock(byte[] blockId) foreach (var peerId in outgoing) { _logger.LogDebug($"Requesting block from outgoing peer {peerId}"); - _outgoingSockets[peerId] - .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) - .TrySendFrame(blockId); + RunProtected(_outgoingSockets[peerId].ResetEvent, () => + { + _outgoingSockets[peerId].Socket + .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) + .TrySendFrame(blockId); + }); await Task.Delay(TimeSpan.FromSeconds(5)); @@ -630,5 +664,16 @@ private byte[] ConvertOp(MessageOp op) enum MessageOp { Disconnect = 0, Tail = 1, Block = 2, Txn = 3, BlockRequest = 4, PeerShare = 5, Connect = 6, Identify = 7 } + private class OutgoingPeer + { + public NetMQSocket Socket { get; set; } + public AutoResetEvent ResetEvent { get; set; } + + public OutgoingPeer(NetMQSocket socket) + { + Socket = socket; + ResetEvent = new AutoResetEvent(true); + } + } } } diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 6edaec5..5c8793f 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -21,7 +21,7 @@ public class NodeHost : INodeHost private readonly IDateTimeProvider _dateTimeProvider; private readonly ITransactionKeyResolver _transactionKeyResolver; private readonly IPeerNetwork _peerNetwork; - private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); + private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); private readonly IPendingTransactionList _pendingTransactionList; private readonly IDifficultyCalculator _difficultyCalculator; From ce042b530b50afc484801e0c7a947ab8a4bb5d97 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 4 Sep 2017 17:15:28 -0700 Subject: [PATCH 07/66] sync --- NBlockchain/Interfaces/IPeerNetwork.cs | 17 ++++ NBlockchain/Models/BlockchainOptions.cs | 8 +- .../Database/DefaultBlockRepository.cs | 20 ++-- .../Services/Net/InProcessPeerNetwork.cs | 10 ++ NBlockchain/Services/Net/TcpPeerNetwork.cs | 94 ++++++++++++------- Samples/DigitalCurrency/Program.cs | 16 ++++ ScratchPad/Program.cs | 5 +- 7 files changed, 116 insertions(+), 54 deletions(-) diff --git a/NBlockchain/Interfaces/IPeerNetwork.cs b/NBlockchain/Interfaces/IPeerNetwork.cs index fbead44..d435349 100644 --- a/NBlockchain/Interfaces/IPeerNetwork.cs +++ b/NBlockchain/Interfaces/IPeerNetwork.cs @@ -26,5 +26,22 @@ public interface IPeerNetwork void Close(); + ICollection GetPeersIn(); + ICollection GetPeersOut(); + + } + + public class ConnectedPeer + { + public Guid NodeId { get; set; } + public string Address { get; set; } + public DateTime LastContact { get; set; } + + public ConnectedPeer(Guid nodeId, string address) + { + NodeId = nodeId; + Address = address; + LastContact = DateTime.Now; + } } } diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 528b3bd..a9a3849 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -70,6 +70,11 @@ public void UseTcpPeerNetwork(uint port) Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService())); } + public void UseDataConnection(string connectionString) + { + Services.AddSingleton(sp => new DataConnection(connectionString)); + } + public void UseParameters() where T : INetworkParameters { @@ -151,8 +156,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); - //AddDefault(ServiceLifetime.Singleton); - + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 61e740c..289d9d1 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -36,7 +36,7 @@ public Task AddBlock(Block block) public Task HaveBlock(byte[] blockId) { - var result = Blocks.Exists(x => x.Entity.Header.BlockId.SequenceEqual(blockId)); + var result = Blocks.Exists(x => x.Entity.Header.BlockId == blockId); return Task.FromResult(result); } @@ -51,28 +51,20 @@ public async Task GetNewestBlockHeader() if (await IsEmpty()) return null; - var max = Blocks.Max(x => x.Entity.Header.Height).AsInt32; - var block = Blocks.FindOne(x => x.Entity.Header.Height == max); + var max = Blocks.Max(x => x.Entity.Header.Height).AsInt64; + var block = Blocks.Find(x => x.Entity.Header.Height == max).First(); return await Task.FromResult(block?.Entity.Header); } public Task GetNextBlock(byte[] prevBlockId) { - var block = Blocks.FindOne(x => x.Entity.Header.PreviousBlock.SequenceEqual(prevBlockId)); + var block = Blocks.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); return Task.FromResult(block?.Entity); } - - public async Task GetGenesisBlockTime() - { - if (await IsEmpty()) - return DateTime.UtcNow.Ticks; - - return await Task.FromResult(Blocks.Min(x => x.Entity.Header.Timestamp)); - } - + public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) { - throw new NotImplementedException(); + return Task.FromResult(0); } } } \ No newline at end of file diff --git a/NBlockchain/Services/Net/InProcessPeerNetwork.cs b/NBlockchain/Services/Net/InProcessPeerNetwork.cs index 7a9ed71..e83fb49 100644 --- a/NBlockchain/Services/Net/InProcessPeerNetwork.cs +++ b/NBlockchain/Services/Net/InProcessPeerNetwork.cs @@ -100,5 +100,15 @@ public void Dispose() { Peers.Remove(this); } + + public ICollection GetPeersIn() + { + throw new NotImplementedException(); + } + + public ICollection GetPeersOut() + { + throw new NotImplementedException(); + } } } diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index 52c809f..b9c46a3 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -33,8 +33,8 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly ConcurrentQueue _peerRoundRobin = new ConcurrentQueue(); private readonly ConcurrentDictionary _outgoingConnectionStrings = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _outgoingSockets = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _incomingPeerLastContact = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _outgoingPeers = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _incomingPeers = new ConcurrentDictionary(); private readonly AutoResetEvent _incomingEvent = new AutoResetEvent(true); @@ -90,7 +90,7 @@ private async void IncomingSocketReceiveReady(object sender, NetMQSocketEventArg var clientId = new Guid(message[0].Buffer); var op = (MessageOp) (message[1].Buffer.First()); - _incomingPeerLastContact[clientId] = DateTime.Now; + _incomingPeers[clientId] = new ConnectedPeer(clientId, e.Socket.Options.LastEndpoint); switch (op) { @@ -112,6 +112,7 @@ private async void IncomingSocketReceiveReady(object sender, NetMQSocketEventArg break; case MessageOp.Connect: _logger.LogDebug("Recv connect from {0}", clientId); + RunProtected(_incomingEvent, () => { _incomingSocket.SendMoreFrame(message[0].Buffer) @@ -123,7 +124,7 @@ private async void IncomingSocketReceiveReady(object sender, NetMQSocketEventArg case MessageOp.Disconnect: _logger.LogDebug("Recv disconnect from {0}", clientId); - _incomingPeerLastContact.TryRemove(clientId, out var v); + _incomingPeers.TryRemove(clientId, out var v); break; } } @@ -158,7 +159,7 @@ private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) await ProcessTransaction(message, serverId); break; case MessageOp.BlockRequest: - await ProcessBlockRequest(message, _outgoingSockets[serverId].Socket, _outgoingSockets[serverId].ResetEvent, null); + await ProcessBlockRequest(message, _outgoingPeers[serverId].Socket, _outgoingPeers[serverId].ResetEvent, null); break; case MessageOp.PeerShare: if (IsSharablePeer(message[2].ConvertToString())) @@ -167,8 +168,8 @@ private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) case MessageOp.Identify: _logger.LogDebug("Recv identify from {0}", serverId); - _outgoingSockets[serverId] = new OutgoingPeer(e.Socket); var peerConStr = message[2].ConvertToString(); + _outgoingPeers[serverId] = new OutgoingPeer(e.Socket, serverId, peerConStr); _outgoingConnectionStrings[peerConStr] = serverId; foreach (var prr in _peerRoundRobin.Where(x => x.ConnectionString == peerConStr).ToList()) prr.LastContact = DateTime.Now; @@ -176,7 +177,7 @@ private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) case MessageOp.Disconnect: _logger.LogDebug("Recv disconnect from {0}", serverId); - _outgoingSockets.TryRemove(serverId, out var sock); + _outgoingPeers.TryRemove(serverId, out var sock); _poller.Remove(e.Socket); e.Socket.Close(); break; @@ -231,7 +232,7 @@ private async Task ProcessBlock(NetMQMessage message, Guid originId, bool tail) var peerList = GetOutgoingPeers().Where(x => x != originId); Parallel.ForEach(peerList, peerId => { - SendBlock(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, tail, null, message[3].Buffer, hopCount + 1); + SendBlock(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, tail, null, message[3].Buffer, hopCount + 1); }); }); } @@ -250,7 +251,7 @@ private async Task ProcessTransaction(NetMQMessage message, Guid originId) var peerList = GetOutgoingPeers().Where(x => x != originId); Parallel.ForEach(peerList, peerId => { - SendTxn(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, null, message[3].Buffer, hopCount + 1); + SendTxn(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, null, message[3].Buffer, hopCount + 1); }); }); @@ -312,17 +313,17 @@ public void Close() foreach (var peerId in GetOutgoingPeers()) { - RunProtected(_outgoingSockets[peerId].ResetEvent, () => + RunProtected(_outgoingPeers[peerId].ResetEvent, () => { - _outgoingSockets[peerId].Socket + _outgoingPeers[peerId].Socket .TrySendFrame(ConvertOp(MessageOp.Disconnect)); - _poller.Remove(_outgoingSockets[peerId].Socket); - _outgoingSockets[peerId].Socket.Close(); + _poller.Remove(_outgoingPeers[peerId].Socket); + _outgoingPeers[peerId].Socket.Close(); }); } - _outgoingSockets.Clear(); + _outgoingPeers.Clear(); _poller.Stop(); _poller.Remove(_incomingSocket); @@ -376,7 +377,7 @@ private void HouseKeeper_Elapsed(object sender, NetMQTimerEventArgs e) private void ConnectOut() { - var target = (TargetOutgoingCount - _outgoingSockets.Count); + var target = (TargetOutgoingCount - _outgoingPeers.Count); if (target <= 0) return; @@ -392,7 +393,7 @@ private void ConnectOut() counter++; if (_outgoingConnectionStrings.ContainsKey(kp.ConnectionString)) { - if (_outgoingSockets.ContainsKey(_outgoingConnectionStrings[kp.ConnectionString])) + if (_outgoingPeers.ContainsKey(_outgoingConnectionStrings[kp.ConnectionString])) continue; } _logger.LogDebug($"Connecting to {kp.ConnectionString}"); @@ -432,7 +433,7 @@ public void BroadcastTail(Block block) { Parallel.ForEach(outgoing, peerId => { - SendBlock(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, true, null, data, 0); + SendBlock(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, true, null, data, 0); }); }); } @@ -483,7 +484,7 @@ public void BroadcastTransaction(TransactionEnvelope transaction) var outgoing = GetOutgoingPeers(); Parallel.ForEach(outgoing, peerId => { - SendTxn(_outgoingSockets[peerId].Socket, _outgoingSockets[peerId].ResetEvent, null, data, 0); + SendTxn(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, null, data, 0); }); }); } @@ -544,9 +545,9 @@ public void RequestNextBlock(byte[] blockId) foreach (var peerId in outgoing) { _logger.LogDebug($"Requesting block from outgoing peer {peerId}"); - RunProtected(_outgoingSockets[peerId].ResetEvent, () => + RunProtected(_outgoingPeers[peerId].ResetEvent, () => { - _outgoingSockets[peerId].Socket + _outgoingPeers[peerId].Socket .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) .TrySendFrame(blockId); }); @@ -566,12 +567,12 @@ public void Dispose() private ICollection GetIncomingPeers() { - return _incomingPeerLastContact.Select(x => x.Key).ToList(); + return _incomingPeers.Select(x => x.Key).ToList(); } private ICollection GetOutgoingPeers() { - return _outgoingSockets.Keys; + return _outgoingPeers.Keys; } private void AdvertiseToPeers() @@ -579,11 +580,18 @@ private void AdvertiseToPeers() foreach (var ds in _discoveryServices) Task.Factory.StartNew(() => { - if (_internalConnsctionString != null) - ds.AdvertiseLocal(_internalConnsctionString); + try + { + if (_internalConnsctionString != null) + ds.AdvertiseLocal(_internalConnsctionString); - if (_externalConnsctionString != null) - ds.AdvertiseGlobal(_externalConnsctionString); + if (_externalConnsctionString != null) + ds.AdvertiseGlobal(_externalConnsctionString); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } }); } @@ -662,18 +670,32 @@ private byte[] ConvertOp(MessageOp op) return result; } - enum MessageOp { Disconnect = 0, Tail = 1, Block = 2, Txn = 3, BlockRequest = 4, PeerShare = 5, Connect = 6, Identify = 7 } - - private class OutgoingPeer + public ICollection GetPeersIn() { - public NetMQSocket Socket { get; set; } - public AutoResetEvent ResetEvent { get; set; } + return _incomingPeers.Values; + } - public OutgoingPeer(NetMQSocket socket) - { - Socket = socket; - ResetEvent = new AutoResetEvent(true); - } + public ICollection GetPeersOut() + { + return _outgoingPeers.Values.Cast().ToList(); + } + + enum MessageOp { Disconnect = 0, Tail = 1, Block = 2, Txn = 3, BlockRequest = 4, PeerShare = 5, Connect = 6, Identify = 7 } + + } + + + internal class OutgoingPeer : ConnectedPeer + { + public NetMQSocket Socket { get; set; } + public AutoResetEvent ResetEvent { get; set; } + + public OutgoingPeer(NetMQSocket socket, Guid nodeId, string address) + : base(nodeId, address) + { + Socket = socket; + ResetEvent = new AutoResetEvent(true); } } + } diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 709b9b7..be9ae05 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -7,6 +7,7 @@ using NBlockchain.Models; using NBlockchain.Services.PeerDiscovery; using System; +using System.Collections.Generic; namespace DigitalCurrency { @@ -103,6 +104,14 @@ static void RunCommand(string command, KeyPair keys) Console.WriteLine("Mining..."); _miner.Start(keys, false); break; + case "peers": + var peersIn = _network.GetPeersIn(); + var peersOut = _network.GetPeersOut(); + Console.WriteLine("Incoming peers"); + PrintPeerList(peersIn); + Console.WriteLine("Outgoing peers"); + PrintPeerList(peersOut); + break; case "balance": if (args.Length == 1) Console.WriteLine($"Balance = {_txnRepo.GetAccountBalance(ownAddress)}"); @@ -145,11 +154,18 @@ static void PrintHelp() Console.WriteLine("help - prints this message"); Console.WriteLine("mine-genesis - build the genesis block and start mining"); Console.WriteLine("mine - start mining"); + Console.WriteLine("peers - show connected peers"); Console.WriteLine("balance - prints your balance"); Console.WriteLine("balance [address] - prints balance of [address]"); Console.WriteLine("send [address] [amount] - sends [amount] to [address]"); Console.WriteLine("exit - end process"); Console.WriteLine(); } + + static void PrintPeerList(ICollection peers) + { + foreach (var item in peers) + Console.WriteLine($"{item.NodeId} - {item.Address}"); + } } } \ No newline at end of file diff --git a/ScratchPad/Program.cs b/ScratchPad/Program.cs index ed4b13a..2e91a92 100644 --- a/ScratchPad/Program.cs +++ b/ScratchPad/Program.cs @@ -145,13 +145,14 @@ private static IServiceProvider ConfigureNode(string db, uint port, string[] pee services.AddBlockchain(x => { //x.UseMongoDB(@"mongodb://localhost:27017", db) - //x.UseTransactionRepository(); + x.UseTransactionRepository(); x.UseTcpPeerNetwork(port); x.AddPeerDiscovery(sp => new StaticPeerDiscovery(peers)); //x.UseMulticastDiscovery("test", "224.100.0.1", 8088); + //x.UseDataConnection("test.db"); x.AddTransactionType(); x.AddTransactionType(); - //x.AddTransactionRule(); + x.AddTransactionRule(); x.AddTransactionRule(); x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() From 74aab8d5816e9515d044b8827e8869008e676158 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 4 Sep 2017 20:25:07 -0700 Subject: [PATCH 08/66] sync --- .../Services/Database/BlockStatistics.cs | 11 ++++++ .../Services/Database/PersistedBlock.cs | 21 ++++++++++ .../Services/Database/PersistedEntity.cs | 38 +++++++++++++++++++ NBlockchain/Models/BlockchainOptions.cs | 6 +-- NBlockchain/Models/PersistedEntity.cs | 21 ---------- .../Database/DefaultBlockRepository.cs | 25 ++++++++++-- .../Database/DefaultPeerRepository.cs | 4 +- .../Database/TransactionRepository.cs | 2 +- ScratchPad/CustomTransactionRepository.cs | 9 +++-- ScratchPad/Program.cs | 13 +++---- ScratchPad/TestBlockbaseBuilder.cs | 2 +- 11 files changed, 110 insertions(+), 42 deletions(-) create mode 100644 NBlockChain/Services/Database/BlockStatistics.cs create mode 100644 NBlockChain/Services/Database/PersistedBlock.cs create mode 100644 NBlockChain/Services/Database/PersistedEntity.cs delete mode 100644 NBlockchain/Models/PersistedEntity.cs diff --git a/NBlockChain/Services/Database/BlockStatistics.cs b/NBlockChain/Services/Database/BlockStatistics.cs new file mode 100644 index 0000000..4efd53e --- /dev/null +++ b/NBlockChain/Services/Database/BlockStatistics.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Database +{ + public class BlockStatistics + { + public int BlockTime { get; set; } + } +} diff --git a/NBlockChain/Services/Database/PersistedBlock.cs b/NBlockChain/Services/Database/PersistedBlock.cs new file mode 100644 index 0000000..01d5c34 --- /dev/null +++ b/NBlockChain/Services/Database/PersistedBlock.cs @@ -0,0 +1,21 @@ +using LiteDB; +using NBlockchain.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Database +{ + public class PersistedBlock : PersistedEntity + { + public PersistedBlock() + { + } + + public PersistedBlock(Block block) + { + Entity = block; + Statistics = new BlockStatistics(); + } + } +} diff --git a/NBlockChain/Services/Database/PersistedEntity.cs b/NBlockChain/Services/Database/PersistedEntity.cs new file mode 100644 index 0000000..174fb27 --- /dev/null +++ b/NBlockChain/Services/Database/PersistedEntity.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Database +{ + public class PersistedEntity + where TStats : new() + { + public TKey Id { get; set; } + public TEntity Entity { get; set; } + public TStats Statistics { get; set; } + + public PersistedEntity() + { + } + + public PersistedEntity(TEntity entity) + { + Entity = entity; + } + } + + public class PersistedEntity + { + public TKey Id { get; set; } + public TEntity Entity { get; set; } + + public PersistedEntity() + { + } + + public PersistedEntity(TEntity entity) + { + Entity = entity; + } + } +} diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index a9a3849..0d080af 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -144,11 +144,11 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Singleton); - //AddDefault(ServiceLifetime.Singleton); - //AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); //AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); + //AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton, sp => sp.GetService()); diff --git a/NBlockchain/Models/PersistedEntity.cs b/NBlockchain/Models/PersistedEntity.cs deleted file mode 100644 index 8206c74..0000000 --- a/NBlockchain/Models/PersistedEntity.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Models -{ - public class PersistedEntity - { - public TKey Id { get; set; } - public TEntity Entity { get; set; } - - public PersistedEntity() - { - } - - public PersistedEntity(TEntity entity) - { - Entity = entity; - } - } -} diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 289d9d1..19bea81 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -16,7 +16,7 @@ public class DefaultBlockRepository : IBlockRepository private readonly ILogger _logger; private readonly IDataConnection _connection; - protected LiteCollection> Blocks => _connection.Database.GetCollection>("Blocks"); + protected LiteCollection Blocks => _connection.Database.GetCollection("Blocks"); public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection connection) { @@ -30,7 +30,16 @@ public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection conn public Task AddBlock(Block block) { - Blocks.Insert(new PersistedEntity(block)); + var persisted = new PersistedBlock(block); + var prevHeader = Blocks + .Find(x => x.Entity.Header.BlockId == block.Header.PreviousBlock) + .Select(x => x.Entity.Header) + .FirstOrDefault(); + + if (prevHeader != null) + persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); + + Blocks.Insert(persisted); return Task.CompletedTask; } @@ -52,7 +61,7 @@ public async Task GetNewestBlockHeader() return null; var max = Blocks.Max(x => x.Entity.Header.Height).AsInt64; - var block = Blocks.Find(x => x.Entity.Header.Height == max).First(); + var block = Blocks.Find(Query.EQ("Entity.Header.Height", max)).First(); return await Task.FromResult(block?.Entity.Header); } @@ -64,7 +73,15 @@ public Task GetNextBlock(byte[] prevBlockId) public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) { - return Task.FromResult(0); + var startTicks = startUtc.Ticks; + var endTicks = endUtc.Ticks; + + var sample = Blocks.Find(Query.And(Query.LT("Entity.Header.Timestamp", endTicks), Query.GT("Entity.Header.Timestamp", startTicks))); + if (sample.Count() == 0) + return Task.FromResult(0); + + var result = Convert.ToInt32(sample.Average(x => x.Statistics.BlockTime)); + return Task.FromResult(result); } } } \ No newline at end of file diff --git a/NBlockchain/Services/Database/DefaultPeerRepository.cs b/NBlockchain/Services/Database/DefaultPeerRepository.cs index 068cd99..f6d9ded 100644 --- a/NBlockchain/Services/Database/DefaultPeerRepository.cs +++ b/NBlockchain/Services/Database/DefaultPeerRepository.cs @@ -21,7 +21,7 @@ public DefaultPeerRepository(IDataConnection connection, ILoggerFactory loggerFa _logger = loggerFactory.CreateLogger(); } - protected LiteCollection> Peers => _connection.Database.GetCollection>("Peers"); + protected LiteCollection> Peers => _connection.Database.GetCollection>("Peers"); public async Task> DiscoverPeers() { @@ -42,7 +42,7 @@ public Task SharePeers(ICollection peers) } else { - Peers.Insert(new PersistedEntity(peer)); + Peers.Insert(new PersistedEntity(peer)); } } diff --git a/NBlockchain/Services/Database/TransactionRepository.cs b/NBlockchain/Services/Database/TransactionRepository.cs index 135afd6..8a6800b 100644 --- a/NBlockchain/Services/Database/TransactionRepository.cs +++ b/NBlockchain/Services/Database/TransactionRepository.cs @@ -16,7 +16,7 @@ public abstract class TransactionRepository protected readonly ILogger Logger; protected readonly IDataConnection Connection; - protected LiteCollection> Blocks => Connection.Database.GetCollection>("Blocks"); + protected LiteCollection Blocks => Connection.Database.GetCollection("Blocks"); protected TransactionRepository(ILoggerFactory loggerFactory, IDataConnection connection) { diff --git a/ScratchPad/CustomTransactionRepository.cs b/ScratchPad/CustomTransactionRepository.cs index 798ecb4..f8d5aa1 100644 --- a/ScratchPad/CustomTransactionRepository.cs +++ b/ScratchPad/CustomTransactionRepository.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using LiteDB; +using Microsoft.Extensions.Logging; using NBlockchain.Interfaces; using NBlockchain.Models; using NBlockchain.Services.Database; @@ -18,7 +19,7 @@ public CustomTransactionRepository(ILoggerFactory loggerFactory, IDataConnection public decimal GetAccountBalance(string account) { var totalOut = Blocks - .Find(x => x.Entity.Transactions.Count(y => y.Originator == account) > 0) + .Find(Query.EQ("Entity.Transactions.Originator", account)) .SelectMany(x => x.Entity.Transactions) .Where(x => x.Originator == account) .Select(x => x.Transaction) @@ -26,7 +27,9 @@ public decimal GetAccountBalance(string account) .Sum(x => x.Amount); - var totalIn = Blocks.Find(x => x.Entity.Transactions.Select(y => y.Transaction).OfType().Count(y => y.Destination == account) > 0) + var totalIn = Blocks + //.Find(x => x.Entity.Transactions.Select(y => y.Transaction).OfType().Count(y => y.Destination == account) > 0) + .Find(Query.EQ("Entity.Transactions.Transaction.Destination", account)) .SelectMany(x => x.Entity.Transactions) .Select(x => x.Transaction) .OfType() diff --git a/ScratchPad/Program.cs b/ScratchPad/Program.cs index 2e91a92..75b96de 100644 --- a/ScratchPad/Program.cs +++ b/ScratchPad/Program.cs @@ -38,7 +38,7 @@ static void Main(string[] args) var addressEncoder = node1.GetService(); var nodeAddr = addressEncoder.EncodeAddress(node1Keys.PublicKey, 0); Console.WriteLine("sending txn..."); - SendTxn(miner1, keys1, nodeAddr, 15); + SendTxn(miner1, keys1, nodeAddr, 5); }); @@ -101,12 +101,11 @@ private static void SendTxn(IServiceProvider sp, KeyPair keys, string address, i private static decimal GetBalance(IServiceProvider sp, KeyPair keys) { - //var repo = sp.GetService(); - //var addressEncoder = sp.GetService(); - //var address = addressEncoder.EncodeAddress(keys.PublicKey, 0); + var repo = sp.GetService(); + var addressEncoder = sp.GetService(); + var address = addressEncoder.EncodeAddress(keys.PublicKey, 0); - //return repo.GetAccountBalance(address); - return 0; + return repo.GetAccountBalance(address); } private static async void CheckBalance(string name, IServiceProvider sp, KeyPair keys) @@ -149,7 +148,7 @@ private static IServiceProvider ConfigureNode(string db, uint port, string[] pee x.UseTcpPeerNetwork(port); x.AddPeerDiscovery(sp => new StaticPeerDiscovery(peers)); //x.UseMulticastDiscovery("test", "224.100.0.1", 8088); - //x.UseDataConnection("test.db"); + //x.UseDataConnection($"{db}.db"); x.AddTransactionType(); x.AddTransactionType(); x.AddTransactionRule(); diff --git a/ScratchPad/TestBlockbaseBuilder.cs b/ScratchPad/TestBlockbaseBuilder.cs index 417b1bc..ce314b6 100644 --- a/ScratchPad/TestBlockbaseBuilder.cs +++ b/ScratchPad/TestBlockbaseBuilder.cs @@ -18,7 +18,7 @@ protected override CoinbaseTransaction BuildBaseTransaction(ICollection Date: Tue, 5 Sep 2017 19:28:47 -0700 Subject: [PATCH 09/66] litedb --- .../Services/Database/PersistedBlock.cs | 36 +++++- .../Database/DefaultBlockRepository.cs | 24 +++- .../Database/TransactionRepository.cs | 1 + .../Services/InMemoryBlockRepository.cs | 117 ------------------ ScratchPad/CustomTransactionRepository.cs | 16 +-- 5 files changed, 63 insertions(+), 131 deletions(-) delete mode 100644 NBlockchain/Services/InMemoryBlockRepository.cs diff --git a/NBlockChain/Services/Database/PersistedBlock.cs b/NBlockChain/Services/Database/PersistedBlock.cs index 01d5c34..a5d21da 100644 --- a/NBlockChain/Services/Database/PersistedBlock.cs +++ b/NBlockChain/Services/Database/PersistedBlock.cs @@ -6,7 +6,7 @@ namespace NBlockchain.Services.Database { - public class PersistedBlock : PersistedEntity + public class PersistedBlock : PersistedEntity { public PersistedBlock() { @@ -14,8 +14,40 @@ public PersistedBlock() public PersistedBlock(Block block) { - Entity = block; + Entity = new BlockInfo(block); Statistics = new BlockStatistics(); } } + + public class PersistedTransaction : PersistedEntity + { + public byte[] BlockId { get; set; } + + public PersistedTransaction() + { + } + + public PersistedTransaction(byte[] blockId, TransactionEnvelope txn) + { + Entity = txn; + BlockId = blockId; + } + } + + public class BlockInfo + { + public BlockHeader Header { get; set; } + public MerkleNode MerkleRootNode { get; set; } + + public BlockInfo() + { + + } + + public BlockInfo(Block block) + { + Header = block.Header; + MerkleRootNode = block.MerkleRootNode; + } + } } diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 19bea81..418a270 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -17,6 +17,7 @@ public class DefaultBlockRepository : IBlockRepository private readonly IDataConnection _connection; protected LiteCollection Blocks => _connection.Database.GetCollection("Blocks"); + protected LiteCollection Txns => _connection.Database.GetCollection("Txns"); public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection connection) { @@ -26,6 +27,8 @@ public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection conn Blocks.EnsureIndex(x => x.Entity.Header.BlockId); Blocks.EnsureIndex(x => x.Entity.Header.PreviousBlock); Blocks.EnsureIndex(x => x.Entity.Header.Height); + Txns.EnsureIndex(x => x.BlockId); + Txns.EnsureIndex(x => x.Entity.Originator); } public Task AddBlock(Block block) @@ -40,6 +43,12 @@ public Task AddBlock(Block block) persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); Blocks.Insert(persisted); + + + var pt = block.Transactions.Select(txn => new PersistedTransaction(block.Header.BlockId, txn)).ToList(); + + Txns.InsertBulk(pt); + return Task.CompletedTask; } @@ -67,8 +76,19 @@ public async Task GetNewestBlockHeader() public Task GetNextBlock(byte[] prevBlockId) { - var block = Blocks.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); - return Task.FromResult(block?.Entity); + var blockHeader = Blocks.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); + + if (blockHeader == null) + return Task.FromResult(null); + + var result = new Block(); + result.Header = blockHeader.Entity.Header; + result.MerkleRootNode = blockHeader.Entity.MerkleRootNode; + + var txns = Txns.Find(Query.EQ("BlockId", blockHeader.Entity.Header.BlockId)); + result.Transactions = txns.Select(x => x.Entity).ToList(); + + return Task.FromResult(result); } public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) diff --git a/NBlockchain/Services/Database/TransactionRepository.cs b/NBlockchain/Services/Database/TransactionRepository.cs index 8a6800b..165f9e6 100644 --- a/NBlockchain/Services/Database/TransactionRepository.cs +++ b/NBlockchain/Services/Database/TransactionRepository.cs @@ -17,6 +17,7 @@ public abstract class TransactionRepository protected readonly IDataConnection Connection; protected LiteCollection Blocks => Connection.Database.GetCollection("Blocks"); + protected LiteCollection Transactions => Connection.Database.GetCollection("Txns"); protected TransactionRepository(ILoggerFactory loggerFactory, IDataConnection connection) { diff --git a/NBlockchain/Services/InMemoryBlockRepository.cs b/NBlockchain/Services/InMemoryBlockRepository.cs deleted file mode 100644 index 158039b..0000000 --- a/NBlockchain/Services/InMemoryBlockRepository.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using NBlockchain.Interfaces; -using NBlockchain.Models; - -namespace NBlockchain.Services -{ - /// - /// In-memory block repository for testing & demo purposes - /// - public class InMemoryBlockRepository : IBlockRepository - { - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); - private readonly ICollection _blocks = new HashSet(); - - public InMemoryBlockRepository() - { - } - - public async Task AddBlock(Block block) - { - _resetEvent.WaitOne(); - try - { - _blocks.Add(block); - await Task.Yield(); - } - finally - { - _resetEvent.Set(); - } - } - - public async Task HaveBlock(byte[] blockId) - { - _resetEvent.WaitOne(); - try - { - return await Task.FromResult(_blocks.Any(x => x.Header.BlockId.SequenceEqual(blockId))); - - } - finally - { - _resetEvent.Set(); - } - } - - public async Task GetNewestBlockHeader() - { - _resetEvent.WaitOne(); - try - { - if (!_blocks.Any()) - return null; - - var max = _blocks.Max(x => x.Header.Height); - var block = _blocks.FirstOrDefault(x => x.Header.Height == max); - return await Task.FromResult(block?.Header); - } - finally - { - _resetEvent.Set(); - } - } - - public async Task GetNextBlock(byte[] prevBlockId) - { - _resetEvent.WaitOne(); - try - { - var block = _blocks.FirstOrDefault(x => x.Header.PreviousBlock.SequenceEqual(prevBlockId)); - return await Task.FromResult(block); - } - finally - { - _resetEvent.Set(); - } - } - - public async Task IsEmpty() - { - _resetEvent.WaitOne(); - try - { - return await Task.FromResult(!_blocks.Any()); - } - finally - { - _resetEvent.Set(); - } - } - - public async Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) - { - _resetEvent.WaitOne(); - try - { - var startTicks = startUtc.Ticks; - var endTicks = endUtc.Ticks; - var sample = _blocks.Where(x => x.Header.Timestamp > startTicks && x.Header.Timestamp < endTicks && x.Header.Height > 1); - if (sample.Count() == 0) - return 0; - - var avg = sample.Average(x => (x.Header.Timestamp - (_blocks.First(y => y.Header.BlockId.SequenceEqual(x.Header.PreviousBlock)).Header.Timestamp))); - - return Convert.ToInt32(TimeSpan.FromTicks(Convert.ToInt64(avg)).TotalSeconds); - } - finally - { - _resetEvent.Set(); - } - } - } -} diff --git a/ScratchPad/CustomTransactionRepository.cs b/ScratchPad/CustomTransactionRepository.cs index f8d5aa1..908c878 100644 --- a/ScratchPad/CustomTransactionRepository.cs +++ b/ScratchPad/CustomTransactionRepository.cs @@ -18,20 +18,16 @@ public CustomTransactionRepository(ILoggerFactory loggerFactory, IDataConnection public decimal GetAccountBalance(string account) { - var totalOut = Blocks - .Find(Query.EQ("Entity.Transactions.Originator", account)) - .SelectMany(x => x.Entity.Transactions) - .Where(x => x.Originator == account) - .Select(x => x.Transaction) + var totalOut = Transactions + .Find(Query.EQ("Entity.Originator", account)) + .Select(x => x.Entity.Transaction) .OfType() .Sum(x => x.Amount); - var totalIn = Blocks - //.Find(x => x.Entity.Transactions.Select(y => y.Transaction).OfType().Count(y => y.Destination == account) > 0) - .Find(Query.EQ("Entity.Transactions.Transaction.Destination", account)) - .SelectMany(x => x.Entity.Transactions) - .Select(x => x.Transaction) + var totalIn = Transactions + .Find(Query.EQ("Entity.Transaction.Destination", account)) + .Select(x => x.Entity.Transaction) .OfType() .Where(x => x.Destination == account) .Sum(x => x.Amount); From e78ffbcd20bfd164797d0771625f8b872d4b70d0 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Sep 2017 07:41:45 -0700 Subject: [PATCH 10/66] Rip out netmq (#3) Rip out NetMq --- NBlockchain/NBlockchain.csproj | 6 +- NBlockchain/Services/Net/PeerConnection.cs | 222 ++++++++ NBlockchain/Services/Net/TcpPeerNetwork.cs | 473 ++++++------------ .../Models/BlockStatistics.cs | 2 + .../Services/MongoBlockRepository.cs | 2 + Samples/DigitalCurrency/Program.cs | 3 +- 6 files changed, 375 insertions(+), 333 deletions(-) create mode 100644 NBlockchain/Services/Net/PeerConnection.cs diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index b875642..d2012d7 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -23,7 +23,6 @@ - @@ -36,6 +35,11 @@ + + + + + \ No newline at end of file diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs new file mode 100644 index 0000000..56d335d --- /dev/null +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace NBlockchain.Services.Net +{ + public class PeerConnection + { + private const byte NetworkQualifier = 0; + private const byte MessageQualifier = 1; + + private const byte IdentifyCommand = 0; + private const byte PingCommand = 1; + + + private readonly byte[] _serviceIdentifier; + private readonly TcpClient _client; + private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); + private readonly Guid _localId; + private Guid _remoteId = Guid.NewGuid(); + private DateTime? _lastContact; + + public event ReceiveMessage OnReceiveMessage; + public event PeerEvent OnDisconnect; + public event PeerEvent OnIdentify; + + public Guid RemoteId => _remoteId; + + public EndPoint RemoteEndPoint => _client.Client.RemoteEndPoint; + public bool Outgoing { get; private set; } + public string ConnectionString { get; private set; } + public DateTime? LastContact => _lastContact; + + public PeerConnection(byte[] serviceIdentifier, Guid nodeId) + { + _client = new TcpClient(); + _localId = nodeId; + _serviceIdentifier = serviceIdentifier; + Outgoing = true; + } + + public PeerConnection(byte[] serviceIdentifier, Guid nodeId, TcpClient client) + { + _client = client; + _localId = nodeId; + _serviceIdentifier = serviceIdentifier; + Outgoing = false; + Task.Factory.StartNew(Poll); + } + + public void Connect(string connectionString) + { + ConnectionString = connectionString; + var uri = new Uri(connectionString); + if (uri.Scheme != "tcp") + throw new InvalidOperationException("Only tcp connections are possible"); + + _client.ConnectAsync(uri.Host, uri.Port).Wait(); + Task.Factory.StartNew(Poll); + Send(NetworkQualifier, IdentifyCommand, _localId.ToByteArray()); + } + + public void Send(byte command, byte[] data) + { + Send(MessageQualifier, command, data); + } + + public void Send(byte qualifier, byte command, byte[] data) + { + _resetEvent.WaitOne(); + try + { + //_client.Client.SendTimeout = 1000; + var headerLength = _serviceIdentifier.Length + 6; + var lenBuffer = BitConverter.GetBytes(data.Length); + var message = new byte[headerLength + data.Length]; + _serviceIdentifier.CopyTo(message, 0); + lenBuffer.CopyTo(message, _serviceIdentifier.Length); + message[_serviceIdentifier.Length + 4] = qualifier; + message[_serviceIdentifier.Length + 5] = command; + data.CopyTo(message, headerLength); + _client.Client.Send(message); + } + catch (SocketException ex) + { + switch (ex.SocketErrorCode) + { + case SocketError.ConnectionAborted: + case SocketError.ConnectionReset: + case SocketError.Disconnecting: + case SocketError.HostDown: + case SocketError.NetworkDown: + case SocketError.NotConnected: + case SocketError.Shutdown: + OnDisconnect?.Invoke(this); + break; + + } + } + catch (Exception ex) + { + + //($"SEND ERR: {ex.Message}"); + } + finally + { + _resetEvent.Set(); + } + } + + public void Disconnect() + { + //_client.Client.(false); + //_client.Close(); + + _client.Client.Shutdown(SocketShutdown.Both); + //_client.Dispose(); + } + + private void Maintain(object state) + { + if (_client.Connected) + { + if (_lastContact < (DateTime.Now.AddMinutes(-10))) + { + Disconnect(); + } + else + { + Send(NetworkQualifier, PingCommand, new byte[0]); + } + } + } + + private async void Poll() + { + //_client.ReceiveTimeout = 3000; + var headerLength = _serviceIdentifier.Length + 6; + var timer = new Timer(Maintain, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(120)); + Send(NetworkQualifier, IdentifyCommand, _localId.ToByteArray()); + while (_client.Connected) + { + try + { + var header = new byte[headerLength]; + + if (_client.Client.Receive(header) != headerLength) + continue; + + var servIdSegment = new ArraySegment(header, 0, _serviceIdentifier.Length).ToArray(); + var lengthSegment = new ArraySegment(header, _serviceIdentifier.Length, 4).ToArray(); + var commandSegment = new ArraySegment(header, _serviceIdentifier.Length + 4, 2).ToArray(); + + var msgLength = BitConverter.ToInt32(lengthSegment, 0); + var msgBuffer = new byte[msgLength]; + + if (!servIdSegment.SequenceEqual(_serviceIdentifier)) + continue; + + if (_client.Client.Receive(msgBuffer) != msgLength) + continue; + + _lastContact = DateTime.Now; + + switch (commandSegment[0]) + { + case NetworkQualifier: + ProcessNetworkCommand(commandSegment[1], msgBuffer); + break; + default: + var evtTask = Task.Factory.StartNew(() => OnReceiveMessage?.Invoke(this, commandSegment[1], msgBuffer)); + break; + } + } + catch (SocketException ex) + { + switch (ex.SocketErrorCode) + { + case SocketError.ConnectionAborted: + case SocketError.ConnectionReset: + case SocketError.Disconnecting: + case SocketError.HostDown: + case SocketError.NetworkDown: + case SocketError.NotConnected: + case SocketError.Shutdown: + OnDisconnect?.Invoke(this); + return; + } + } + catch (Exception ex) + { + //log + //Console.WriteLine($"EXCEPTION: {ex.Message}"); + await Task.Delay(1000); + } + } + timer.Dispose(); + } + + private void ProcessNetworkCommand(byte command, byte[] data) + { + switch (command) + { + case IdentifyCommand: + _remoteId = new Guid(data); + OnIdentify?.Invoke(this); + break; + case PingCommand: + break; + } + } + } + + public delegate void ReceiveMessage(PeerConnection sender, byte command, byte[] data); + public delegate void PeerEvent(PeerConnection sender); + +} diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index b9c46a3..fe9cc41 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -5,13 +5,12 @@ using System.Linq; using System.Net; using System.Net.Sockets; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NBlockchain.Interfaces; using NBlockchain.Models; -using NetMQ; -using NetMQ.Sockets; using Newtonsoft.Json; using Newtonsoft.Json.Bson; @@ -22,6 +21,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable //TODO: break this class up into smaller pieces private const int TargetOutgoingCount = 8; private readonly uint _port; + private byte[] _serviceId = new byte[] { 0x0, 0x1 }; private IBlockReceiver _blockReciever; private ITransactionReceiver _transactionReciever; @@ -32,17 +32,16 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly IOwnAddressResolver _ownAddressResolver; private readonly ConcurrentQueue _peerRoundRobin = new ConcurrentQueue(); - private readonly ConcurrentDictionary _outgoingConnectionStrings = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _outgoingPeers = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _incomingPeers = new ConcurrentDictionary(); + + private readonly List _peerConnections = new List(); + private CancellationTokenSource _cancelTokenSource; + private TcpListener _listener; - private readonly AutoResetEvent _incomingEvent = new AutoResetEvent(true); + private readonly AutoResetEvent _peerEvent = new AutoResetEvent(true); private Timer _sharePeersTimer; + private Timer _discoveryTimer; - private readonly RouterSocket _incomingSocket = new RouterSocket(); - private readonly NetMQPoller _poller = new NetMQPoller(); - private NetMQTimer _houseKeeper; private string _internalConnsctionString; private string _externalConnsctionString; @@ -55,149 +54,99 @@ public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable GetActivePeers() { - evt.WaitOne(); + _peerEvent.WaitOne(); try { - action(); + return new List(_peerConnections); } finally { - evt.Set(); + _peerEvent.Set(); } } - private async void IncomingSocketReceiveReady(object sender, NetMQSocketEventArgs e) + public void Open() { - try - { - NetMQMessage message = null; - RunProtected(_incomingEvent, () => message = e.Socket.ReceiveMultipartMessage()); + _cancelTokenSource = new CancellationTokenSource(); + _listener = new TcpListener(IPAddress.Any, (int)_port); + _listener.Start(); - if (message == null) - return; - - if (message.FrameCount < 2) + Task.Factory.StartNew(() => + { + while (!_cancelTokenSource.IsCancellationRequested) { - return; + var client = _listener.AcceptTcpClientAsync().Result; + _logger.LogDebug($"Client connected - {client.Client.RemoteEndPoint}"); + var peer = new PeerConnection(_serviceId, NodeId, client); + peer.OnReceiveMessage += Peer_OnReceiveMessage; + peer.OnDisconnect += Peer_OnDisconnect; + _peerEvent.WaitOne(); + try + { + _peerConnections.Add(peer); + } + finally + { + _peerEvent.Set(); + } } + }); - var clientId = new Guid(message[0].Buffer); - var op = (MessageOp) (message[1].Buffer.First()); - _incomingPeers[clientId] = new ConnectedPeer(clientId, e.Socket.Options.LastEndpoint); - switch (op) - { - case MessageOp.Block: - await ProcessBlock(message, clientId, false); - break; - case MessageOp.Tail: - await ProcessBlock(message, clientId, true); - break; - case MessageOp.Txn: - await ProcessTransaction(message, clientId); - break; - case MessageOp.BlockRequest: - await ProcessBlockRequest(message, e.Socket, _incomingEvent, clientId); - break; - case MessageOp.PeerShare: - if (IsSharablePeer(message[2].ConvertToString())) - AddPeer(new KnownPeer() { ConnectionString = message[2].ConvertToString() }); - break; - case MessageOp.Connect: - _logger.LogDebug("Recv connect from {0}", clientId); - - RunProtected(_incomingEvent, () => - { - _incomingSocket.SendMoreFrame(message[0].Buffer) - .SendMoreFrame(NodeId.ToByteArray()) - .SendMoreFrame(ConvertOp(MessageOp.Identify)) - .TrySendFrame(message[2].Buffer); - }); - break; + DiscoverOwnConnectionStrings(); - case MessageOp.Disconnect: - _logger.LogDebug("Recv disconnect from {0}", clientId); - _incomingPeers.TryRemove(clientId, out var v); - break; - } - } - catch (Exception ex) + _discoveryTimer = new Timer((state) => { - _logger.LogError($"Error processing serv message, {ex.Message}"); - } + DiscoverPeers(); + AdvertiseToPeers(); + }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30)); + + _sharePeersTimer = new Timer(SharePeers, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); } - private async void Peer_ReceiveReady(object sender, NetMQSocketEventArgs e) + private async void Peer_OnReceiveMessage(PeerConnection sender, byte command, byte[] data) { try { - var message = e.Socket.ReceiveMultipartMessage(); - if (message.FrameCount < 2) - { - return; - } - - var serverId = new Guid(message[0].Buffer); - var op = (MessageOp) (message[1].Buffer.First()); - - switch (op) + switch (command) { - case MessageOp.Block: - await ProcessBlock(message, serverId, false); + case Commands.Block: + await ProcessBlock(data, sender.RemoteId, false); break; - case MessageOp.Tail: - await ProcessBlock(message, serverId, true); + case Commands.BlockRequest: + await ProcessBlockRequest(data, sender); break; - case MessageOp.Txn: - await ProcessTransaction(message, serverId); + case Commands.PeerShare: + if (IsSharablePeer(Encoding.UTF8.GetString(data))) + AddPeer(new KnownPeer() { ConnectionString = Encoding.UTF8.GetString(data) }); break; - case MessageOp.BlockRequest: - await ProcessBlockRequest(message, _outgoingPeers[serverId].Socket, _outgoingPeers[serverId].ResetEvent, null); + case Commands.Tail: + await ProcessBlock(data, sender.RemoteId, true); break; - case MessageOp.PeerShare: - if (IsSharablePeer(message[2].ConvertToString())) - AddPeer(new KnownPeer() { ConnectionString = message[2].ConvertToString() }); - break; - - case MessageOp.Identify: - _logger.LogDebug("Recv identify from {0}", serverId); - var peerConStr = message[2].ConvertToString(); - _outgoingPeers[serverId] = new OutgoingPeer(e.Socket, serverId, peerConStr); - _outgoingConnectionStrings[peerConStr] = serverId; - foreach (var prr in _peerRoundRobin.Where(x => x.ConnectionString == peerConStr).ToList()) - prr.LastContact = DateTime.Now; - break; - - case MessageOp.Disconnect: - _logger.LogDebug("Recv disconnect from {0}", serverId); - _outgoingPeers.TryRemove(serverId, out var sock); - _poller.Remove(e.Socket); - e.Socket.Close(); + case Commands.Txn: + await ProcessTransaction(data, sender.RemoteId); break; } } catch (Exception ex) { - _logger.LogError($"Error processing peer message, {ex.Message}"); + _logger.LogError($"Error procssing command {command} - {ex.Message}"); } } - private async Task ProcessBlockRequest(NetMQMessage message, IOutgoingSocket socket, AutoResetEvent evt, Guid? peerId) + private async Task ProcessBlockRequest(byte[] prevBlockId, PeerConnection peer) { - var prevBlockId = message[2].Buffer; var block = await _blockRepository.GetNextBlock(prevBlockId); if (block != null) { _logger.LogDebug("Responding to block request"); var data = SerializeObject(block); - SendBlock(socket, evt, false, peerId, data, -1); + SendBlock(peer, data, false); } else { @@ -205,92 +154,78 @@ private async Task ProcessBlockRequest(NetMQMessage message, IOutgoingSocket soc } } - private async Task ProcessBlock(NetMQMessage message, Guid originId, bool tail) + private async Task ProcessBlock(byte[] data, Guid originId, bool tail) { _logger.LogDebug($"Processing block {tail}"); - var hopCount = message[2].ConvertToInt32(); - var block = DeserializeObject(message[3].Buffer); + var block = DeserializeObject(data); var result = PeerDataResult.Ignore; if (tail) result = await _blockReciever.RecieveTail(block); else result = await _blockReciever.RecieveBlock(block); - if ((tail) && (result == PeerDataResult.Relay) && (hopCount > -1)) + if ((tail) && (result == PeerDataResult.Relay)) { - var incomingTask = Task.Factory.StartNew(() => + var relayTask = Task.Factory.StartNew(() => { - var peerList = GetIncomingPeers().Where(x => x != originId); - Parallel.ForEach(peerList, peerId => + var peerList = GetActivePeers().Where(x => x.RemoteId != originId); + Parallel.ForEach(peerList, peer => { - SendBlock(_incomingSocket, _incomingEvent, tail, peerId, message[3].Buffer, hopCount + 1); - }); - }); - - var outgoingTask = Task.Factory.StartNew(() => - { - var peerList = GetOutgoingPeers().Where(x => x != originId); - Parallel.ForEach(peerList, peerId => - { - SendBlock(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, tail, null, message[3].Buffer, hopCount + 1); + SendBlock(peer, data, tail); }); }); } } - private async Task ProcessTransaction(NetMQMessage message, Guid originId) + private async Task ProcessTransaction(byte[] data, Guid originId) { - var hopCount = message[2].ConvertToInt32(); - var txn = DeserializeObject(message[3].Buffer); + var txn = DeserializeObject(data); var result = await _transactionReciever.RecieveTransaction(txn); - if ((result == PeerDataResult.Relay) && (hopCount > -1)) + if (result == PeerDataResult.Relay) { - var outgoingTask = Task.Factory.StartNew(() => + var relayTask = Task.Factory.StartNew(() => { - var peerList = GetOutgoingPeers().Where(x => x != originId); - Parallel.ForEach(peerList, peerId => + var peerList = GetActivePeers().Where(x => x.RemoteId != originId); + Parallel.ForEach(peerList, peer => { - SendTxn(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, null, message[3].Buffer, hopCount + 1); - }); - }); - - var incomingTask = Task.Factory.StartNew(() => - { - var peerList = GetIncomingPeers().Where(x => x != originId); - Parallel.ForEach(peerList, peerId => - { - SendTxn(_incomingSocket, _incomingEvent, peerId, message[3].Buffer, hopCount + 1); + SendTxn(peer, data); }); }); } } + - public void Open() + private void Peer_OnDisconnect(PeerConnection sender) { - _incomingSocket.Bind($"tcp://*:{_port}"); - _poller.Add(_incomingSocket); - _poller.RunAsync(); - _houseKeeper = new NetMQTimer(TimeSpan.FromSeconds(30)); - _houseKeeper.Elapsed += HouseKeeper_Elapsed; - _poller.Add(_houseKeeper); - _houseKeeper.Enable = true; - DiscoverOwnConnectionStrings(); - DiscoverPeers(); - AdvertiseToPeers(); + _peerEvent.WaitOne(); + try + { + _peerConnections.Remove(sender); + } + finally + { + _peerEvent.Set(); + } } - + private void OnboardPeer(string connStr) { try { - var peer = new DealerSocket(); - peer.Options.Identity = NodeId.ToByteArray(); - peer.ReceiveReady += Peer_ReceiveReady; + var peer = new PeerConnection(_serviceId, NodeId); peer.Connect(connStr); - _poller.Add(peer); - peer.SendMoreFrame(ConvertOp(MessageOp.Connect)) - .TrySendFrame(connStr); + peer.OnReceiveMessage += Peer_OnReceiveMessage; + peer.OnDisconnect += Peer_OnDisconnect; + _peerEvent.WaitOne(); + try + { + _peerConnections.Add(peer); + } + finally + { + _peerEvent.Set(); + } } catch (Exception ex) { @@ -300,36 +235,14 @@ private void OnboardPeer(string connStr) public void Close() { - foreach (var peerId in GetIncomingPeers()) - { - RunProtected(_incomingEvent, () => - { - _incomingSocket - .SendMoreFrame(peerId.ToByteArray()) - .SendMoreFrame(NodeId.ToByteArray()) - .TrySendFrame(ConvertOp(MessageOp.Disconnect)); - }); - } + _discoveryTimer.Dispose(); + _sharePeersTimer.Dispose(); + _listener?.Stop(); - foreach (var peerId in GetOutgoingPeers()) + foreach (var peer in GetActivePeers()) { - RunProtected(_outgoingPeers[peerId].ResetEvent, () => - { - _outgoingPeers[peerId].Socket - .TrySendFrame(ConvertOp(MessageOp.Disconnect)); - - _poller.Remove(_outgoingPeers[peerId].Socket); - _outgoingPeers[peerId].Socket.Close(); - }); + peer.Disconnect(); } - - _outgoingPeers.Clear(); - - _poller.Stop(); - _poller.Remove(_incomingSocket); - _poller.Remove(_houseKeeper); - _houseKeeper.Enable = false; - _incomingSocket.Close(); } public void DiscoverPeers() @@ -366,18 +279,11 @@ private async void SharePeers(object state) // } - - private void HouseKeeper_Elapsed(object sender, NetMQTimerEventArgs e) - { - _logger.LogDebug("Performing house keeping"); - DiscoverPeers(); - AdvertiseToPeers(); - ConnectOut(); - } - + private void ConnectOut() { - var target = (TargetOutgoingCount - _outgoingPeers.Count); + var peersOut = GetActivePeers().Where(x => x.Outgoing); + var target = (TargetOutgoingCount - peersOut.Count()); if (target <= 0) return; @@ -391,11 +297,10 @@ private void ConnectOut() { _peerRoundRobin.Enqueue(kp); counter++; - if (_outgoingConnectionStrings.ContainsKey(kp.ConnectionString)) - { - if (_outgoingPeers.ContainsKey(_outgoingConnectionStrings[kp.ConnectionString])) - continue; - } + + if (peersOut.Any(x => x.ConnectionString == kp.ConnectionString)) + continue; + _logger.LogDebug($"Connecting to {kp.ConnectionString}"); OnboardPeer(kp.ConnectionString); actual++; @@ -418,47 +323,22 @@ public void RegisterTransactionReceiver(ITransactionReceiver transactionReciever public void BroadcastTail(Block block) { var data = SerializeObject(block); - var incoming = GetIncomingPeers(); - var outgoing = GetOutgoingPeers(); - - Task.Factory.StartNew(() => - { - Parallel.ForEach(incoming, peerId => - { - SendBlock(_incomingSocket, _incomingEvent, true, peerId, data, 0); - }); - }); + var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); Task.Factory.StartNew(() => { - Parallel.ForEach(outgoing, peerId => + Parallel.ForEach(peers, peer => { - SendBlock(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, true, null, data, 0); + SendBlock(peer, data, true); }); }); } - private void SendBlock(IOutgoingSocket socket, AutoResetEvent resetEvt, bool tail, Guid? peerId, byte[] data, int hopCount) + private void SendBlock(PeerConnection peer, byte[] data, bool tail) { try { - var op = ConvertOp(MessageOp.Block); - if (tail) - op = ConvertOp(MessageOp.Tail); - - var msg = new NetMQMessage(); - - if ((peerId.HasValue)) - { - msg.Append(peerId.Value.ToByteArray()); - msg.Append(NodeId.ToByteArray()); - } - - msg.Append(op); - msg.Append(BitConverter.GetBytes(hopCount)); - msg.Append(data); - - RunProtected(resetEvt, () => socket.TrySendMultipartMessage(msg)); + peer.Send(tail ? Commands.Tail : Commands.Block, data); } catch (Exception ex) { @@ -472,42 +352,19 @@ public void BroadcastTransaction(TransactionEnvelope transaction) Task.Factory.StartNew(() => { - var incoming = GetIncomingPeers(); - Parallel.ForEach(incoming, peerId => + var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); + Parallel.ForEach(peers, peer => { - SendTxn(_incomingSocket, _incomingEvent, peerId, data, 0); - }); - }); - - Task.Factory.StartNew(() => - { - var outgoing = GetOutgoingPeers(); - Parallel.ForEach(outgoing, peerId => - { - SendTxn(_outgoingPeers[peerId].Socket, _outgoingPeers[peerId].ResetEvent, null, data, 0); + SendTxn(peer, data); }); }); } - private void SendTxn(IOutgoingSocket socket, AutoResetEvent resetEvt, Guid? peerId, byte[] data, int hopCount) + private void SendTxn(PeerConnection peer, byte[] data) { try { - var op = ConvertOp(MessageOp.Txn); - - var msg = new NetMQMessage(); - - if ((peerId.HasValue) && (socket is RouterSocket)) - { - msg.Append(peerId.Value.ToByteArray()); - msg.Append(NodeId.ToByteArray()); - } - - msg.Append(op); - msg.Append(BitConverter.GetBytes(hopCount)); - msg.Append(data); - - RunProtected(resetEvt, () => socket.TrySendMultipartMessage(msg)); + peer.Send(Commands.Txn, data); } catch (Exception ex) { @@ -519,61 +376,24 @@ public void RequestNextBlock(byte[] blockId) { Task.Factory.StartNew(async () => { - var incoming = GetIncomingPeers().Where(x => x != NodeId); - foreach (var peerId in incoming) + var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); + foreach (var peer in peers) { - _logger.LogDebug($"Requesting block from incoming peer {peerId}"); - RunProtected(_incomingEvent, () => - { - _incomingSocket - .SendMoreFrame(peerId.ToByteArray()) - .SendMoreFrame(NodeId.ToByteArray()) - .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) - .TrySendFrame(blockId); - }); + _logger.LogDebug($"Requesting block from incoming peer {peer.RemoteId}"); + peer.Send(Commands.BlockRequest, blockId); await Task.Delay(TimeSpan.FromSeconds(5)); if ((await _blockRepository.GetNextBlock(blockId)) != null) return; } - }); - - Task.Factory.StartNew(async () => - { - var outgoing = GetOutgoingPeers().Where(x => x != NodeId); - foreach (var peerId in outgoing) - { - _logger.LogDebug($"Requesting block from outgoing peer {peerId}"); - RunProtected(_outgoingPeers[peerId].ResetEvent, () => - { - _outgoingPeers[peerId].Socket - .SendMoreFrame(ConvertOp(MessageOp.BlockRequest)) - .TrySendFrame(blockId); - }); - - await Task.Delay(TimeSpan.FromSeconds(5)); - - if ((await _blockRepository.GetNextBlock(blockId)) != null) - return; - } - }); + }); } public void Dispose() { - } - - private ICollection GetIncomingPeers() - { - return _incomingPeers.Select(x => x.Key).ToList(); - } - - private ICollection GetOutgoingPeers() - { - return _outgoingPeers.Keys; - } + } private void AdvertiseToPeers() { @@ -662,40 +482,31 @@ private static bool IsSharablePeer(string connectionUri) return false; } } - - private byte[] ConvertOp(MessageOp op) - { - byte[] result = new byte[1]; - result[0] = (byte)op; - return result; - } - + public ICollection GetPeersIn() { - return _incomingPeers.Values; + return GetActivePeers() + .Where(x => !x.Outgoing) + .Select(x => new ConnectedPeer(x.RemoteId, x.RemoteEndPoint.ToString())) + .ToList(); } public ICollection GetPeersOut() { - return _outgoingPeers.Values.Cast().ToList(); + return GetActivePeers() + .Where(x => x.Outgoing) + .Select(x => new ConnectedPeer(x.RemoteId, x.RemoteEndPoint.ToString())) + .ToList(); } - - enum MessageOp { Disconnect = 0, Tail = 1, Block = 2, Txn = 3, BlockRequest = 4, PeerShare = 5, Connect = 6, Identify = 7 } - + } - - internal class OutgoingPeer : ConnectedPeer - { - public NetMQSocket Socket { get; set; } - public AutoResetEvent ResetEvent { get; set; } - - public OutgoingPeer(NetMQSocket socket, Guid nodeId, string address) - : base(nodeId, address) - { - Socket = socket; - ResetEvent = new AutoResetEvent(true); - } - } - + internal class Commands + { + public const byte Tail = 0; + public const byte Block = 1; + public const byte Txn = 2; + public const byte BlockRequest = 3; + public const byte PeerShare = 4; + } } diff --git a/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs b/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs index 83051de..330b41c 100644 --- a/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs +++ b/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs @@ -7,5 +7,7 @@ namespace NBlockchain.MongoDB.Models public class BlockStatistics { public int BlockTime { get; set; } + + public DateTime TimeStamp { get; set; } } } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index de7d427..eb4bfdd 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -49,6 +49,8 @@ public async Task AddBlock(Block block) .Project(x => x.Header) .FirstOrDefault(); + persisted.Statistics.TimeStamp = new DateTime(block.Header.Timestamp); + if (prevHeader != null) persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index be9ae05..af641be 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -47,7 +47,8 @@ private static IServiceProvider ConfigureNode(string db, uint port) var serviceProvider = services.BuildServiceProvider(); //config logging - var loggerFactory = serviceProvider.GetService(); + var loggerFactory = serviceProvider.GetService(); + //loggerFactory.AddProvider() return serviceProvider; } From c6e77c324be43851c11c0e6fee3ef1f79dae2125 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Sep 2017 08:22:15 -0700 Subject: [PATCH 11/66] logging --- NBlockchain/Services/Net/TcpPeerNetwork.cs | 29 ++++++++++++++++--- .../DigitalCurrency/DigitalCurrency.csproj | 1 + Samples/DigitalCurrency/Program.cs | 4 +-- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index fe9cc41..4094502 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -13,6 +13,7 @@ using NBlockchain.Models; using Newtonsoft.Json; using Newtonsoft.Json.Bson; +using System.Diagnostics; namespace NBlockchain.Services.Net { @@ -76,11 +77,11 @@ public void Open() _listener = new TcpListener(IPAddress.Any, (int)_port); _listener.Start(); - Task.Factory.StartNew(() => + Task.Factory.StartNew(async () => { while (!_cancelTokenSource.IsCancellationRequested) { - var client = _listener.AcceptTcpClientAsync().Result; + var client = await _listener.AcceptTcpClientAsync(); _logger.LogDebug($"Client connected - {client.Client.RemoteEndPoint}"); var peer = new PeerConnection(_serviceId, NodeId, client); peer.OnReceiveMessage += Peer_OnReceiveMessage; @@ -100,13 +101,32 @@ public void Open() DiscoverOwnConnectionStrings(); - _discoveryTimer = new Timer((state) => + _discoveryTimer = new Timer((state) => { DiscoverPeers(); AdvertiseToPeers(); }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30)); _sharePeersTimer = new Timer(SharePeers, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); + + + Task.Factory.StartNew(async () => + { + while (!_cancelTokenSource.IsCancellationRequested) + { + await Task.Delay(TimeSpan.FromMinutes(5)); + var peers = GetActivePeers(); + _logger.LogInformation($"Check in - Outgoing peers: {peers.Count(x => x.Outgoing)}"); + _logger.LogInformation($"Check in - Incoming peers: {peers.Count(x => !x.Outgoing)}"); + + var process = Process.GetCurrentProcess(); + + _logger.LogInformation($"Check in - Thread count: {process.Threads.Count}"); + _logger.LogInformation($"Check in - Working set: {process.WorkingSet64}"); + _logger.LogInformation($"Check in - PrivateMemorySize: {process.PrivateMemorySize64}"); + + } + }); } private async void Peer_OnReceiveMessage(PeerConnection sender, byte command, byte[] data) @@ -201,6 +221,7 @@ private void Peer_OnDisconnect(PeerConnection sender) _peerEvent.WaitOne(); try { + _logger.LogInformation($"Peer disconnect {sender.RemoteId} {sender.RemoteEndPoint}"); _peerConnections.Remove(sender); } finally @@ -301,7 +322,7 @@ private void ConnectOut() if (peersOut.Any(x => x.ConnectionString == kp.ConnectionString)) continue; - _logger.LogDebug($"Connecting to {kp.ConnectionString}"); + _logger.LogInformation($"Connecting to {kp.ConnectionString}"); OnboardPeer(kp.ConnectionString); actual++; } diff --git a/Samples/DigitalCurrency/DigitalCurrency.csproj b/Samples/DigitalCurrency/DigitalCurrency.csproj index 06957ba..9ccc1c4 100644 --- a/Samples/DigitalCurrency/DigitalCurrency.csproj +++ b/Samples/DigitalCurrency/DigitalCurrency.csproj @@ -9,6 +9,7 @@ + diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index af641be..b88de08 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -47,8 +47,8 @@ private static IServiceProvider ConfigureNode(string db, uint port) var serviceProvider = services.BuildServiceProvider(); //config logging - var loggerFactory = serviceProvider.GetService(); - //loggerFactory.AddProvider() + var loggerFactory = serviceProvider.GetService(); + loggerFactory.AddFile("node.log", LogLevel.Debug); return serviceProvider; } From 542d559446328258fbc5758c26fd10cc8154a6ce Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Sep 2017 11:44:53 -0700 Subject: [PATCH 12/66] fixes --- NBlockchain/Models/BlockTransaction.cs | 10 +++ NBlockchain/Models/TransactionEnvelope.cs | 4 +- .../Services/BlockbaseTransactionBuilder.cs | 1 + NBlockchain/Services/Net/PeerConnection.cs | 23 ++++-- NBlockchain/Services/Net/TcpPeerNetwork.cs | 77 ++++++++++++++----- .../PeerDiscovery/MulticastDiscovery.cs | 72 +++++++++-------- .../Services/MongoBlockRepository.cs | 17 +++- .../DigitalCurrency/DigitalCurrency.csproj | 1 + Samples/DigitalCurrency/Program.cs | 3 +- .../Transactions/ValueTransaction.cs | 5 +- ScratchPad/TestTransaction.cs | 2 +- .../Common/TestTransaction.cs | 2 +- 12 files changed, 150 insertions(+), 67 deletions(-) create mode 100644 NBlockchain/Models/BlockTransaction.cs diff --git a/NBlockchain/Models/BlockTransaction.cs b/NBlockchain/Models/BlockTransaction.cs new file mode 100644 index 0000000..9553f56 --- /dev/null +++ b/NBlockchain/Models/BlockTransaction.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Models +{ + public abstract class BlockTransaction + { + } +} diff --git a/NBlockchain/Models/TransactionEnvelope.cs b/NBlockchain/Models/TransactionEnvelope.cs index d2bc6d7..e00eeea 100644 --- a/NBlockchain/Models/TransactionEnvelope.cs +++ b/NBlockchain/Models/TransactionEnvelope.cs @@ -15,13 +15,13 @@ public class TransactionEnvelope public byte[] Signature { get; set; } - public object Transaction { get; set; } + public BlockTransaction Transaction { get; set; } public TransactionEnvelope() { } - public TransactionEnvelope(object transaction) + public TransactionEnvelope(BlockTransaction transaction) { Transaction = transaction; } diff --git a/NBlockchain/Services/BlockbaseTransactionBuilder.cs b/NBlockchain/Services/BlockbaseTransactionBuilder.cs index 670770e..5824e67 100644 --- a/NBlockchain/Services/BlockbaseTransactionBuilder.cs +++ b/NBlockchain/Services/BlockbaseTransactionBuilder.cs @@ -9,6 +9,7 @@ namespace NBlockchain.Services { public abstract class BlockbaseTransactionBuilder : IBlockbaseTransactionBuilder + where T : BlockTransaction { private readonly IAddressEncoder _addressEncoder; diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index 56d335d..c2690d1 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -24,10 +24,13 @@ public class PeerConnection private readonly Guid _localId; private Guid _remoteId = Guid.NewGuid(); private DateTime? _lastContact; + private CancellationTokenSource _cancelToken = new CancellationTokenSource(); public event ReceiveMessage OnReceiveMessage; public event PeerEvent OnDisconnect; public event PeerEvent OnIdentify; + public event PeerEvent OnUnresponsive; + public event PeerException OnPeerException; public Guid RemoteId => _remoteId; @@ -35,7 +38,7 @@ public class PeerConnection public bool Outgoing { get; private set; } public string ConnectionString { get; private set; } public DateTime? LastContact => _lastContact; - + public PeerConnection(byte[] serviceIdentifier, Guid nodeId) { _client = new TcpClient(); @@ -97,15 +100,16 @@ public void Send(byte qualifier, byte command, byte[] data) case SocketError.NetworkDown: case SocketError.NotConnected: case SocketError.Shutdown: + _cancelToken.Cancel(); OnDisconnect?.Invoke(this); + Disconnect(); break; } } catch (Exception ex) { - - //($"SEND ERR: {ex.Message}"); + OnPeerException?.Invoke(this, ex); } finally { @@ -115,6 +119,7 @@ public void Send(byte qualifier, byte command, byte[] data) public void Disconnect() { + _cancelToken.Cancel(); //_client.Client.(false); //_client.Close(); @@ -128,6 +133,7 @@ private void Maintain(object state) { if (_lastContact < (DateTime.Now.AddMinutes(-10))) { + OnUnresponsive?.Invoke(this); Disconnect(); } else @@ -140,10 +146,11 @@ private void Maintain(object state) private async void Poll() { //_client.ReceiveTimeout = 3000; + _cancelToken = new CancellationTokenSource(); var headerLength = _serviceIdentifier.Length + 6; var timer = new Timer(Maintain, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(120)); Send(NetworkQualifier, IdentifyCommand, _localId.ToByteArray()); - while (_client.Connected) + while ((_client.Connected) && (!_cancelToken.IsCancellationRequested)) { try { @@ -188,14 +195,15 @@ private async void Poll() case SocketError.NetworkDown: case SocketError.NotConnected: case SocketError.Shutdown: + _cancelToken.Cancel(); OnDisconnect?.Invoke(this); - return; + Disconnect(); + break; } } catch (Exception ex) { - //log - //Console.WriteLine($"EXCEPTION: {ex.Message}"); + OnPeerException?.Invoke(this, ex); await Task.Delay(1000); } } @@ -218,5 +226,6 @@ private void ProcessNetworkCommand(byte command, byte[] data) public delegate void ReceiveMessage(PeerConnection sender, byte command, byte[] data); public delegate void PeerEvent(PeerConnection sender); + public delegate void PeerException(PeerConnection sender, Exception exception); } diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index 4094502..cf67283 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -79,32 +79,37 @@ public void Open() Task.Factory.StartNew(async () => { - while (!_cancelTokenSource.IsCancellationRequested) + try { - var client = await _listener.AcceptTcpClientAsync(); - _logger.LogDebug($"Client connected - {client.Client.RemoteEndPoint}"); - var peer = new PeerConnection(_serviceId, NodeId, client); - peer.OnReceiveMessage += Peer_OnReceiveMessage; - peer.OnDisconnect += Peer_OnDisconnect; - _peerEvent.WaitOne(); - try + while (!_cancelTokenSource.IsCancellationRequested) { - _peerConnections.Add(peer); - } - finally - { - _peerEvent.Set(); + var client = await _listener.AcceptTcpClientAsync(); + _logger.LogDebug($"Client connected - {client.Client.RemoteEndPoint}"); + var peer = new PeerConnection(_serviceId, NodeId, client); + AttachEventHandlers(peer); + _peerEvent.WaitOne(); + try + { + _peerConnections.Add(peer); + } + finally + { + _peerEvent.Set(); + } } } + catch (Exception ex) + { + _logger.LogError($"Error listening - {ex.Message}"); + } }); - - + DiscoverOwnConnectionStrings(); + AdvertiseToPeers(); _discoveryTimer = new Timer((state) => { - DiscoverPeers(); - AdvertiseToPeers(); + DiscoverPeers(); }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30)); _sharePeersTimer = new Timer(SharePeers, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); @@ -129,6 +134,30 @@ public void Open() }); } + private void AttachEventHandlers(PeerConnection peer) + { + peer.OnReceiveMessage += Peer_OnReceiveMessage; + peer.OnDisconnect += Peer_OnDisconnect; + peer.OnPeerException += Peer_OnPeerException; + peer.OnUnresponsive += Peer_OnUnresponsive; + peer.OnIdentify += Peer_OnIdentify; + } + + private void Peer_OnIdentify(PeerConnection sender) + { + _logger.LogInformation($"Peer identify {sender.RemoteEndPoint} - {sender.RemoteId}"); + } + + private void Peer_OnUnresponsive(PeerConnection sender) + { + _logger.LogInformation($"Unresponsive peer {sender.RemoteEndPoint}"); + } + + private void Peer_OnPeerException(PeerConnection sender, Exception exception) + { + _logger.LogError($"Peer exception {sender.RemoteEndPoint} - {exception.Message}"); + } + private async void Peer_OnReceiveMessage(PeerConnection sender, byte command, byte[] data) { try @@ -176,8 +205,11 @@ private async Task ProcessBlockRequest(byte[] prevBlockId, PeerConnection peer) private async Task ProcessBlock(byte[] data, Guid originId, bool tail) { - _logger.LogDebug($"Processing block {tail}"); + _logger.LogInformation($"Processing block - tail: {tail}"); var block = DeserializeObject(data); + + _logger.LogInformation($"Rec block {BitConverter.ToString(block.Header.BlockId)} from {originId}"); + var result = PeerDataResult.Ignore; if (tail) result = await _blockReciever.RecieveTail(block); @@ -224,6 +256,10 @@ private void Peer_OnDisconnect(PeerConnection sender) _logger.LogInformation($"Peer disconnect {sender.RemoteId} {sender.RemoteEndPoint}"); _peerConnections.Remove(sender); } + catch (Exception ex) + { + _logger.LogError($"Disconnect error ({sender.RemoteId}) - {ex.Message}"); + } finally { _peerEvent.Set(); @@ -236,8 +272,7 @@ private void OnboardPeer(string connStr) { var peer = new PeerConnection(_serviceId, NodeId); peer.Connect(connStr); - peer.OnReceiveMessage += Peer_OnReceiveMessage; - peer.OnDisconnect += Peer_OnDisconnect; + AttachEventHandlers(peer); _peerEvent.WaitOne(); try { @@ -400,7 +435,7 @@ public void RequestNextBlock(byte[] blockId) var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); foreach (var peer in peers) { - _logger.LogDebug($"Requesting block from incoming peer {peer.RemoteId}"); + _logger.LogInformation($"Requesting block {BitConverter.ToString(blockId)} from incoming peer {peer.RemoteId}"); peer.Send(Commands.BlockRequest, blockId); await Task.Delay(TimeSpan.FromSeconds(5)); diff --git a/NBlockchain/Services/PeerDiscovery/MulticastDiscovery.cs b/NBlockchain/Services/PeerDiscovery/MulticastDiscovery.cs index f282ff4..bf1cd5b 100644 --- a/NBlockchain/Services/PeerDiscovery/MulticastDiscovery.cs +++ b/NBlockchain/Services/PeerDiscovery/MulticastDiscovery.cs @@ -39,6 +39,7 @@ public async Task AdvertiseGlobal(string connectionString) public async Task AdvertiseLocal(string connectionString) { + _logger.LogInformation($"Advertising to peers {connectionString}"); if (_advertiseTask != null) { _advertiseCts.Cancel(); @@ -64,56 +65,65 @@ public async Task AdvertiseLocal(string connectionString) await udpClient.SendAsync(data, data.Length, ipEndPoint); await Task.Delay(_interval); } - //udpClient.Close(); + udpClient.Dispose(); } } catch (Exception ex) { - _logger.LogError(ex.Message); + _logger.LogError($"Error advertising - {ex.Message}"); } }); } public async Task> DiscoverPeers() { - _logger.LogDebug("Discovering peers"); - var result = new HashSet(); - var udpClient = new UdpClient(_port); + _logger.LogInformation("Discovering peers"); - var ownAddress = _ownAddressResolver.ResolvePreferredLocalAddress(); - udpClient.JoinMulticastGroup(IPAddress.Parse(_multicastAddress), ownAddress); + var result = new HashSet(); - udpClient.Client.ReceiveTimeout = Convert.ToInt32(_interval.TotalMilliseconds + 1000); + try + { + var udpClient = new UdpClient(_port); - DateTime pollUntil = DateTime.Now.Add(_interval); + var ownAddress = _ownAddressResolver.ResolvePreferredLocalAddress(); + udpClient.JoinMulticastGroup(IPAddress.Parse(_multicastAddress), ownAddress); - while (pollUntil > DateTime.Now) - { - byte[] b = new byte[1024]; - try + udpClient.Client.ReceiveTimeout = Convert.ToInt32(_interval.TotalMilliseconds + 1000); + + DateTime pollUntil = DateTime.Now.Add(_interval); + + while (pollUntil > DateTime.Now) { - //var ipEndPoint = new IPEndPoint(IPAddress.Any, 0); - var data = await udpClient.ReceiveAsync(); //(ref ipEndPoint); - string message = Encoding.ASCII.GetString(data.Buffer); - _logger.LogDebug($"rx message {message}"); - if (message.StartsWith(_serviceId)) + byte[] b = new byte[1024]; + try { - var connStr = message.Remove(0, _serviceId.Length); - result.Add(new KnownPeer() + //var ipEndPoint = new IPEndPoint(IPAddress.Any, 0); + var data = await udpClient.ReceiveAsync(); //(ref ipEndPoint); + string message = Encoding.ASCII.GetString(data.Buffer); + _logger.LogDebug($"rx message {message}"); + if (message.StartsWith(_serviceId)) { - ConnectionString = connStr, - LastContact = DateTime.Now - }); + var connStr = message.Remove(0, _serviceId.Length); + result.Add(new KnownPeer() + { + ConnectionString = connStr, + LastContact = DateTime.Now + }); + } + } + catch (SocketException ex) + { + _logger.LogError($"SocketException polling for peers - {ex.Message}"); + } + catch (Exception ex) + { + _logger.LogError($"Exception polling for peers - {ex.Message}"); } } - catch (SocketException ex) - { - _logger.LogDebug(ex.Message); - } - catch (Exception ex) - { - _logger.LogError(ex.Message); - } + } + catch (Exception ex) + { + _logger.LogError($"Error joining multicast - {ex.Message}"); } return result; diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index eb4bfdd..22dd780 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -4,10 +4,12 @@ using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Bson.Serialization; +using MongoDB.Bson.IO; using MongoDB.Driver; using NBlockchain.MongoDB.Models; using NBlockchain.Interfaces; using NBlockchain.Models; +using System.IO; namespace NBlockchain.MongoDB.Services { @@ -32,10 +34,23 @@ static MongoBlockRepository() var attr = type.GetTypeInfo().GetCustomAttribute(); if (attr != null) { - BsonSerializer.RegisterDiscriminator(type, new BsonString(attr.TypeId)); + //BsonSerializer.RegisterDiscriminator(type, new BsonString(attr.TypeId)); + BsonSerializer.RegisterDiscriminator(type, TypeNameDiscriminator.GetDiscriminator(type)); + + //hack for dodgy mongo driver + IBsonWriter w = new BsonBinaryWriter(new MemoryStream()); + var constr = type.GetConstructor(new Type[0]); + BsonSerializer.Serialize(w, type, constr.Invoke(null)); } } } + + //BsonSerializer.RegisterDiscriminatorConvention(typeof(BlockTransaction), StandardDiscriminatorConvention.Scalar); + + + //TypeNameDiscriminator.GetDiscriminator + //TypeNameDiscriminator. + //BsonSerializer.RegisterDiscriminatorConvention(typeof(object), ObjectDiscriminatorConvention.Instance); //BsonSerializer.RegisterDiscriminator(t, t.FullName)); } diff --git a/Samples/DigitalCurrency/DigitalCurrency.csproj b/Samples/DigitalCurrency/DigitalCurrency.csproj index 9ccc1c4..01a527c 100644 --- a/Samples/DigitalCurrency/DigitalCurrency.csproj +++ b/Samples/DigitalCurrency/DigitalCurrency.csproj @@ -8,6 +8,7 @@ + diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index b88de08..cb9c86f 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -48,7 +48,8 @@ private static IServiceProvider ConfigureNode(string db, uint port) //config logging var loggerFactory = serviceProvider.GetService(); - loggerFactory.AddFile("node.log", LogLevel.Debug); + loggerFactory.AddDebug(); + loggerFactory.AddFile("node.log", LogLevel.Debug); return serviceProvider; } diff --git a/Samples/DigitalCurrency/Transactions/ValueTransaction.cs b/Samples/DigitalCurrency/Transactions/ValueTransaction.cs index 5a1a408..85ba780 100644 --- a/Samples/DigitalCurrency/Transactions/ValueTransaction.cs +++ b/Samples/DigitalCurrency/Transactions/ValueTransaction.cs @@ -1,10 +1,11 @@ -using System; +using NBlockchain.Models; +using System; using System.Collections.Generic; using System.Text; namespace DigitalCurrency.Transactions { - public abstract class ValueTransaction + public abstract class ValueTransaction : BlockTransaction { public int Amount { get; set; } } diff --git a/ScratchPad/TestTransaction.cs b/ScratchPad/TestTransaction.cs index 85b162a..8f3ed5e 100644 --- a/ScratchPad/TestTransaction.cs +++ b/ScratchPad/TestTransaction.cs @@ -6,7 +6,7 @@ namespace ScratchPad { - public class Transaction + public class Transaction : BlockTransaction { public int Amount { get; set; } } diff --git a/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs b/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs index 6da9e17..6e413e3 100644 --- a/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs +++ b/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs @@ -6,7 +6,7 @@ namespace NBlockchain.Tests.Scenarios.Common { [TransactionType("txn-v1")] - public class TestTransaction + public class TestTransaction : BlockTransaction { public string Data { get; set; } } From 7a433123df217e5b39d7a9a8dd663141aa40685c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Sep 2017 12:24:17 -0700 Subject: [PATCH 13/66] fixes --- NBlockchain/Services/Net/PeerConnection.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index c2690d1..5c477b5 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -163,13 +163,22 @@ private async void Poll() var lengthSegment = new ArraySegment(header, _serviceIdentifier.Length, 4).ToArray(); var commandSegment = new ArraySegment(header, _serviceIdentifier.Length + 4, 2).ToArray(); - var msgLength = BitConverter.ToInt32(lengthSegment, 0); - var msgBuffer = new byte[msgLength]; - if (!servIdSegment.SequenceEqual(_serviceIdentifier)) + { + var flushBuffer = new byte[_client.Available]; + + _client.Client.Receive(flushBuffer, _client.Available, SocketFlags.None); continue; + } + + var msgLength = BitConverter.ToInt32(lengthSegment, 0); + var msgBuffer = new byte[msgLength]; - if (_client.Client.Receive(msgBuffer) != msgLength) + var actualRecv = 0; + while (actualRecv < msgLength) + actualRecv += _client.Client.Receive(msgBuffer, actualRecv, msgLength - actualRecv, SocketFlags.None); + + if (actualRecv != msgLength) continue; _lastContact = DateTime.Now; From 129a076997e408855f7f7a24d6c940d3d8832e57 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 9 Sep 2017 13:25:43 -0700 Subject: [PATCH 14/66] LAN testing --- NBlockchain/Models/KnownPeer.cs | 1 + NBlockchain/Services/Net/PeerConnection.cs | 16 +++++++++++----- NBlockchain/Services/Net/TcpPeerNetwork.cs | 13 +++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/NBlockchain/Models/KnownPeer.cs b/NBlockchain/Models/KnownPeer.cs index 826498e..52367ea 100644 --- a/NBlockchain/Models/KnownPeer.cs +++ b/NBlockchain/Models/KnownPeer.cs @@ -8,6 +8,7 @@ public class KnownPeer { public string ConnectionString { get; set; } public DateTime LastContact { get; set; } + public bool IsSelf { get; set; } } } diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index 5c477b5..dea5158 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -156,7 +156,7 @@ private async void Poll() { var header = new byte[headerLength]; - if (_client.Client.Receive(header) != headerLength) + if (Recieve(header) != headerLength) continue; var servIdSegment = new ArraySegment(header, 0, _serviceIdentifier.Length).ToArray(); @@ -166,7 +166,6 @@ private async void Poll() if (!servIdSegment.SequenceEqual(_serviceIdentifier)) { var flushBuffer = new byte[_client.Available]; - _client.Client.Receive(flushBuffer, _client.Available, SocketFlags.None); continue; } @@ -174,9 +173,7 @@ private async void Poll() var msgLength = BitConverter.ToInt32(lengthSegment, 0); var msgBuffer = new byte[msgLength]; - var actualRecv = 0; - while (actualRecv < msgLength) - actualRecv += _client.Client.Receive(msgBuffer, actualRecv, msgLength - actualRecv, SocketFlags.None); + var actualRecv = Recieve(msgBuffer); if (actualRecv != msgLength) continue; @@ -231,6 +228,15 @@ private void ProcessNetworkCommand(byte command, byte[] data) break; } } + + private int Recieve(byte[] msgBuffer) + { + var actualRecv = 0; + while (actualRecv < msgBuffer.Length) + actualRecv += _client.Client.Receive(msgBuffer, actualRecv, (msgBuffer.Length - actualRecv), SocketFlags.None); + + return actualRecv; + } } public delegate void ReceiveMessage(PeerConnection sender, byte command, byte[] data); diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index cf67283..779d732 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -146,6 +146,16 @@ private void AttachEventHandlers(PeerConnection peer) private void Peer_OnIdentify(PeerConnection sender) { _logger.LogInformation($"Peer identify {sender.RemoteEndPoint} - {sender.RemoteId}"); + if (sender.RemoteId == NodeId) + { + if (!string.IsNullOrEmpty(sender.ConnectionString)) + { + var selfs = _peerRoundRobin.Where(x => x.ConnectionString == sender.ConnectionString); + foreach (var self in selfs) + self.IsSelf = true; + } + sender.Disconnect(); + } } private void Peer_OnUnresponsive(PeerConnection sender) @@ -354,6 +364,9 @@ private void ConnectOut() _peerRoundRobin.Enqueue(kp); counter++; + if (kp.IsSelf) + continue; + if (peersOut.Any(x => x.ConnectionString == kp.ConnectionString)) continue; From 4d3b34bee96ee840973912f770ab2c5366f5096b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 10 Sep 2017 07:21:55 -0700 Subject: [PATCH 15/66] bump version --- NBlockchain/NBlockchain.csproj | 6 +++--- Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj | 6 +++--- Samples/DigitalCurrency/readme.md | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index d2012d7..44213fd 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 0.2.0-alpha - 0.2.0.0 - 0.2.0.0 + 0.3.0-alpha + 0.3.0.0 + 0.3.0.0 https://github.com/danielgerlag/NBlockchain https://github.com/danielgerlag/NBlockchain.git git diff --git a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj index bb4fa6c..d7f8fc5 100644 --- a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj +++ b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.2.0-alpha + 0.3.0-alpha MongoDB persistence store for NBlockchain https://github.com/danielgerlag/NBlockchain.git https://github.com/danielgerlag/NBlockchain git Blockchain - 0.2.0.0 - 0.2.0.0 + 0.3.0.0 + 0.3.0.0 true diff --git a/Samples/DigitalCurrency/readme.md b/Samples/DigitalCurrency/readme.md index 36cf356..0079ee7 100644 --- a/Samples/DigitalCurrency/readme.md +++ b/Samples/DigitalCurrency/readme.md @@ -1,13 +1,14 @@ # Digital currency sample for NBlockchain -This example demonstrates how to implement a very basic digital currency with NBlockchain +This example demonstrates how to implement a very basic digital currency with NBlockchain. +(This does not follow the input/output aggregation model that Bitcoin uses but it just meant to illustrate an application of NBlockchin) ## Define our transactions types The first thing we will do is define the schema of the transactions we want to store in our blockchain. ```c# -public abstract class ValueTransaction +public abstract class ValueTransaction : BlockTransaction { public int Amount { get; set; } } From 257f84856bd9b5d84fc580a05f514f813b6f1016 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 10 Sep 2017 08:28:01 -0700 Subject: [PATCH 16/66] samples --- NBlockchain/Services/Net/TcpPeerNetwork.cs | 9 ++--- Samples/DigitalCurrency/Program.cs | 31 ++++++++++++--- ...ory.cs => ICustomTransactionRepository.cs} | 2 +- .../LiteDb/CustomTransactionRespository.cs | 39 +++++++++++++++++++ .../CustomMongoTransactionRepository.cs} | 6 +-- Samples/DigitalCurrency/Rules/BalanceRule.cs | 4 +- 6 files changed, 73 insertions(+), 18 deletions(-) rename Samples/DigitalCurrency/Repositories/{ITransactionRepository.cs => ICustomTransactionRepository.cs} (77%) create mode 100644 Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs rename Samples/DigitalCurrency/Repositories/{TransactionRepository.cs => Mongo/CustomMongoTransactionRepository.cs} (86%) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index 779d732..b126b2f 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -214,11 +214,10 @@ private async Task ProcessBlockRequest(byte[] prevBlockId, PeerConnection peer) } private async Task ProcessBlock(byte[] data, Guid originId, bool tail) - { - _logger.LogInformation($"Processing block - tail: {tail}"); + { var block = DeserializeObject(data); - _logger.LogInformation($"Rec block {BitConverter.ToString(block.Header.BlockId)} from {originId}"); + _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)} from {originId}"); var result = PeerDataResult.Ignore; if (tail) @@ -342,8 +341,6 @@ private async void SharePeers(object state) { foreach (var ds in _discoveryServices) await ds.SharePeers(_peerRoundRobin.ToList()); - - // } private void ConnectOut() @@ -448,7 +445,7 @@ public void RequestNextBlock(byte[] blockId) var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); foreach (var peer in peers) { - _logger.LogInformation($"Requesting block {BitConverter.ToString(blockId)} from incoming peer {peer.RemoteId}"); + _logger.LogDebug($"Requesting block {BitConverter.ToString(blockId)} from incoming peer {peer.RemoteId}"); peer.Send(Commands.BlockRequest, blockId); await Task.Delay(TimeSpan.FromSeconds(5)); diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index cb9c86f..044b205 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -1,4 +1,6 @@ using DigitalCurrency.Repositories; +using DigitalCurrency.Repositories.LiteDb; +using DigitalCurrency.Repositories.Mongo; using DigitalCurrency.Rules; using DigitalCurrency.Transactions; using Microsoft.Extensions.DependencyInjection; @@ -8,6 +10,7 @@ using NBlockchain.Services.PeerDiscovery; using System; using System.Collections.Generic; +using System.Text; namespace DigitalCurrency { @@ -18,16 +21,19 @@ class Program private static IPeerNetwork _network; private static ISignatureService _sigService; private static IAddressEncoder _addressEncoder; - private static ITransactionRepository _txnRepo; + private static ICustomTransactionRepository _txnRepo; + private static IBlockRepository _blockRepo; private static IServiceProvider ConfigureNode(string db, uint port) { IServiceCollection services = new ServiceCollection(); services.AddBlockchain(x => - { - x.UseMongoDB(@"mongodb://localhost:27017", db) - .UseTransactionRepository(); + { + x.UseDataConnection("node.db"); + x.UseTransactionRepository(); x.UseTcpPeerNetwork(port); + //x.UseMongoDB(@"mongodb://localhost:27017", db) + // .UseTransactionRepository(); //x.AddPeerDiscovery(sp => new StaticPeerDiscovery("tcp://localhost:503")); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); x.AddTransactionType(); @@ -37,7 +43,7 @@ private static IServiceProvider ConfigureNode(string db, uint port) x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { - BlockTime = TimeSpan.FromSeconds(10), + BlockTime = TimeSpan.FromSeconds(120), HeaderVersion = 1, ExpectedContentThreshold = 0.8m }); @@ -63,7 +69,8 @@ static void Main(string[] args) _network = serviceProvider.GetService(); _sigService = serviceProvider.GetService(); _addressEncoder = serviceProvider.GetService(); - _txnRepo = serviceProvider.GetService(); + _txnRepo = serviceProvider.GetService(); + _blockRepo = serviceProvider.GetService(); Console.WriteLine("Generating key pair..."); var keys = _sigService.GenerateKeyPair(); @@ -106,6 +113,10 @@ static void RunCommand(string command, KeyPair keys) Console.WriteLine("Mining..."); _miner.Start(keys, false); break; + case "stop-mining": + Console.WriteLine("Stopping..."); + _miner.Stop(); + break; case "peers": var peersIn = _network.GetPeersIn(); var peersOut = _network.GetPeersOut(); @@ -120,6 +131,14 @@ static void RunCommand(string command, KeyPair keys) else Console.WriteLine($"Balance = {_txnRepo.GetAccountBalance(args[1])}"); break; + case "best-block": + var header = _blockRepo.GetNewestBlockHeader().Result; + Console.WriteLine($"Height: {header.Height}, Id: {BitConverter.ToString(header.BlockId)}"); + break; + case "avg-time": + var avgTime = _blockRepo.GetAverageBlockTimeInSecs(DateTime.UtcNow.AddHours(-1), DateTime.UtcNow).Result; + Console.WriteLine($"Avg time: {avgTime}s"); + break; case "send": if (args.Length != 3) { diff --git a/Samples/DigitalCurrency/Repositories/ITransactionRepository.cs b/Samples/DigitalCurrency/Repositories/ICustomTransactionRepository.cs similarity index 77% rename from Samples/DigitalCurrency/Repositories/ITransactionRepository.cs rename to Samples/DigitalCurrency/Repositories/ICustomTransactionRepository.cs index 1b5edbc..d6cc334 100644 --- a/Samples/DigitalCurrency/Repositories/ITransactionRepository.cs +++ b/Samples/DigitalCurrency/Repositories/ICustomTransactionRepository.cs @@ -4,7 +4,7 @@ namespace DigitalCurrency.Repositories { - public interface ITransactionRepository + public interface ICustomTransactionRepository { decimal GetAccountBalance(string account); } diff --git a/Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs b/Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs new file mode 100644 index 0000000..774fea3 --- /dev/null +++ b/Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs @@ -0,0 +1,39 @@ +using DigitalCurrency.Transactions; +using LiteDB; +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; +using NBlockchain.Services.Database; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DigitalCurrency.Repositories.LiteDb +{ + public class CustomTransactionRepository : TransactionRepository, ICustomTransactionRepository + { + public CustomTransactionRepository(ILoggerFactory loggerFactory, IDataConnection dataConnection) + : base(loggerFactory, dataConnection) + { + } + + public decimal GetAccountBalance(string account) + { + var totalOut = Transactions + .Find(Query.EQ("Entity.Originator", account)) + .Select(x => x.Entity.Transaction) + .OfType() + .Sum(x => x.Amount); + + + var totalIn = Transactions + .Find(Query.EQ("Entity.Transaction.Destination", account)) + .Select(x => x.Entity.Transaction) + .OfType() + .Where(x => x.Destination == account) + .Sum(x => x.Amount); + + return (totalIn - totalOut); + } + } +} diff --git a/Samples/DigitalCurrency/Repositories/TransactionRepository.cs b/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoTransactionRepository.cs similarity index 86% rename from Samples/DigitalCurrency/Repositories/TransactionRepository.cs rename to Samples/DigitalCurrency/Repositories/Mongo/CustomMongoTransactionRepository.cs index cd74011..a484629 100644 --- a/Samples/DigitalCurrency/Repositories/TransactionRepository.cs +++ b/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoTransactionRepository.cs @@ -5,11 +5,11 @@ using System.Collections.Generic; using System.Text; -namespace DigitalCurrency.Repositories +namespace DigitalCurrency.Repositories.Mongo { - public class TransactionRepository : MongoTransactionRepository, ITransactionRepository + public class CustomMongoTransactionRepository : MongoTransactionRepository, ICustomTransactionRepository { - public TransactionRepository(IMongoDatabase database) + public CustomMongoTransactionRepository(IMongoDatabase database) : base(database) { } diff --git a/Samples/DigitalCurrency/Rules/BalanceRule.cs b/Samples/DigitalCurrency/Rules/BalanceRule.cs index e3eeabe..80b94ea 100644 --- a/Samples/DigitalCurrency/Rules/BalanceRule.cs +++ b/Samples/DigitalCurrency/Rules/BalanceRule.cs @@ -10,9 +10,9 @@ namespace DigitalCurrency.Rules { public class BalanceRule : TransactionRule { - private readonly ITransactionRepository _txnRepo; + private readonly ICustomTransactionRepository _txnRepo; - public BalanceRule(ITransactionRepository txnRepo) + public BalanceRule(ICustomTransactionRepository txnRepo) { _txnRepo = txnRepo; } From ca08a3d3076c4723373d25f158be21196590515a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 10 Sep 2017 08:35:26 -0700 Subject: [PATCH 17/66] Update readme.md --- Samples/DigitalCurrency/readme.md | 73 ++++++++++++++++++------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/Samples/DigitalCurrency/readme.md b/Samples/DigitalCurrency/readme.md index 0079ee7..94dc13c 100644 --- a/Samples/DigitalCurrency/readme.md +++ b/Samples/DigitalCurrency/readme.md @@ -33,48 +33,34 @@ In this case we want two types of transactions ## Implement a repository to query our transactions Now we need to implement a repository to run queries against our defined transactions. -This can be done by extending `MongoTransactionRepository` which gives us access the the block store (if MongoDB is used as the persistence store) +This can be done by extending `TransactionRepository` which gives us access the the block store (if MongoDB is used as the persistence store, then you would extend `MongoTransactionRepository`, see (sample)[Repositories/Mongo/CustomMongoTransactionRepository.cs]) ```c# -public class TransactionRepository : MongoTransactionRepository, ITransactionRepository +public class CustomTransactionRepository : TransactionRepository, ICustomTransactionRepository { - public TransactionRepository(IMongoDatabase database) - : base(database) + public CustomTransactionRepository(ILoggerFactory loggerFactory, IDataConnection dataConnection) + : base(loggerFactory, dataConnection) { - } + } public decimal GetAccountBalance(string account) { - var totalOut = 0; - var totalIn = 0; - - var outQry = Blocks.Aggregate() - .Unwind(x => x.Transactions) - .Match(new BsonDocument("Transactions.Originator", account)) - .Group(new BsonDocument { { "_id", BsonNull.Value }, { "sum", new BsonDocument("$sum", "$Transactions.Transaction.Amount") } }) - .SingleOrDefault(); + var totalOut = Transactions + .Find(Query.EQ("Entity.Originator", account)) + .Select(x => x.Entity.Transaction) + .OfType() + .Sum(x => x.Amount); - if (outQry != null) - { - if (outQry.TryGetValue("sum", out var bOut)) - totalOut = bOut.AsInt32; - } - var inQry = Blocks.Aggregate() - .Unwind(x => x.Transactions) - .Match(new BsonDocument("Transactions.Transaction.Destination", account)) - .Group(new BsonDocument { { "_id", BsonNull.Value }, { "sum", new BsonDocument("$sum", "$Transactions.Transaction.Amount") } }) - .SingleOrDefault(); - - if (inQry != null) - { - if (inQry.TryGetValue("sum", out var bIn)) - totalIn = bIn.AsInt32; - } + var totalIn = Transactions + .Find(Query.EQ("Entity.Transaction.Destination", account)) + .Select(x => x.Entity.Transaction) + .OfType() + .Where(x => x.Destination == account) + .Sum(x => x.Amount); return (totalIn - totalOut); } - } ``` @@ -133,13 +119,38 @@ public class CoinbaseBuilder : BlockbaseTransactionBuilder When we configure the IoC container for our blockchain node, we have several options In this case we chose - * To use MongoDB as the database + * To use the built-in LiteDb as the database (using node.db as the datafile) + * Register our customer transaction repository that we use in our rule definitions * To use the Tcp peer network and listen on port 500 * Use the multicast peer discovery protocol (to find other peers on the LAN) * Added our Transaction types that we defined earlier * Added our Transaction rules * Set the block time to 10 seconds +```c# +IServiceCollection services = new ServiceCollection(); +services.AddBlockchain(x => +{ + x.UseDataConnection("node.db"); + x.UseTransactionRepository(); + x.UseTcpPeerNetwork(500); + x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + x.AddTransactionType(); + x.AddTransactionType(); + x.AddTransactionRule(); + x.AddTransactionRule(); + x.UseBlockbaseProvider(); + x.UseParameters(new StaticNetworkParameters() + { + BlockTime = TimeSpan.FromSeconds(10), + HeaderVersion = 1, + ExpectedContentThreshold = 0.8m + }); +}); +``` + +If you wanted to use MongoDB as the persistence store, then the config would look something like this + ```c# IServiceCollection services = new ServiceCollection(); services.AddBlockchain(x => From 3afcb3473cb7e164979cd9b4a07b5b047ebed8cc Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 10 Sep 2017 08:35:55 -0700 Subject: [PATCH 18/66] Update readme.md --- Samples/DigitalCurrency/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples/DigitalCurrency/readme.md b/Samples/DigitalCurrency/readme.md index 94dc13c..c1dd07b 100644 --- a/Samples/DigitalCurrency/readme.md +++ b/Samples/DigitalCurrency/readme.md @@ -33,7 +33,7 @@ In this case we want two types of transactions ## Implement a repository to query our transactions Now we need to implement a repository to run queries against our defined transactions. -This can be done by extending `TransactionRepository` which gives us access the the block store (if MongoDB is used as the persistence store, then you would extend `MongoTransactionRepository`, see (sample)[Repositories/Mongo/CustomMongoTransactionRepository.cs]) +This can be done by extending `TransactionRepository` which gives us access the the block store (if MongoDB is used as the persistence store, then you would extend `MongoTransactionRepository`, see [sample](Repositories/Mongo/CustomMongoTransactionRepository.cs)) ```c# public class CustomTransactionRepository : TransactionRepository, ICustomTransactionRepository From a09cc5dc16a976807316855fbee49df131797c88 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 10 Sep 2017 13:11:11 -0700 Subject: [PATCH 19/66] net tweaks --- NBlockchain/Models/KnownPeer.cs | 2 +- NBlockchain/Services/Net/PeerConnection.cs | 40 +++++++++++++--- NBlockchain/Services/Net/TcpPeerNetwork.cs | 46 +++++++++++++++++-- .../Services/MongoPeerDirectory.cs | 4 ++ Samples/DigitalCurrency/Program.cs | 43 ++++++++++++++--- 5 files changed, 118 insertions(+), 17 deletions(-) diff --git a/NBlockchain/Models/KnownPeer.cs b/NBlockchain/Models/KnownPeer.cs index 52367ea..075a7c1 100644 --- a/NBlockchain/Models/KnownPeer.cs +++ b/NBlockchain/Models/KnownPeer.cs @@ -9,6 +9,6 @@ public class KnownPeer public string ConnectionString { get; set; } public DateTime LastContact { get; set; } public bool IsSelf { get; set; } - + public Guid NodeId { get; set; } } } diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index dea5158..2933d23 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -11,13 +11,13 @@ namespace NBlockchain.Services.Net { public class PeerConnection { + private const int MaxMessageSize = 10240000; private const byte NetworkQualifier = 0; private const byte MessageQualifier = 1; private const byte IdentifyCommand = 0; private const byte PingCommand = 1; - private readonly byte[] _serviceIdentifier; private readonly TcpClient _client; private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); @@ -25,6 +25,7 @@ public class PeerConnection private Guid _remoteId = Guid.NewGuid(); private DateTime? _lastContact; private CancellationTokenSource _cancelToken = new CancellationTokenSource(); + private bool _pollExited = false; public event ReceiveMessage OnReceiveMessage; public event PeerEvent OnDisconnect; @@ -120,11 +121,28 @@ public void Send(byte qualifier, byte command, byte[] data) public void Disconnect() { _cancelToken.Cancel(); - //_client.Client.(false); - //_client.Close(); - - _client.Client.Shutdown(SocketShutdown.Both); - //_client.Dispose(); + _resetEvent.WaitOne(); + try + { + _client.Client.Shutdown(SocketShutdown.Both); + } + catch (Exception ex) + { + OnPeerException?.Invoke(this, ex); + } + finally + { + _resetEvent.Set(); + } + } + + public void Close() + { + Task.Factory.StartNew(() => + { + SpinWait.SpinUntil(() => _pollExited); + _client.Dispose(); + }); } private void Maintain(object state) @@ -145,7 +163,7 @@ private void Maintain(object state) private async void Poll() { - //_client.ReceiveTimeout = 3000; + _pollExited = false; _cancelToken = new CancellationTokenSource(); var headerLength = _serviceIdentifier.Length + 6; var timer = new Timer(Maintain, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(120)); @@ -171,6 +189,13 @@ private async void Poll() } var msgLength = BitConverter.ToInt32(lengthSegment, 0); + + if (msgLength < 0) + continue; + + if (msgLength > MaxMessageSize) + continue; + var msgBuffer = new byte[msgLength]; var actualRecv = Recieve(msgBuffer); @@ -214,6 +239,7 @@ private async void Poll() } } timer.Dispose(); + _pollExited = true; } private void ProcessNetworkCommand(byte command, byte[] data) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index b126b2f..8caa476 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -31,6 +31,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly IEnumerable _discoveryServices; private readonly ILogger _logger; private readonly IOwnAddressResolver _ownAddressResolver; + private readonly IPendingTransactionList _pendingTransactionList; private readonly ConcurrentQueue _peerRoundRobin = new ConcurrentQueue(); @@ -46,15 +47,18 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private string _internalConnsctionString; private string _externalConnsctionString; + private object _duplicateLock = new object(); + public Guid NodeId { get; private set; } - public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver) + public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IPendingTransactionList pendingTransactionList) { _port = port; _logger = loggerFactory.CreateLogger(); _blockRepository = blockRepository; _discoveryServices = discoveryServices; _ownAddressResolver = ownAddressResolver; + _pendingTransactionList = pendingTransactionList; NodeId = Guid.NewGuid(); } @@ -146,6 +150,23 @@ private void AttachEventHandlers(PeerConnection peer) private void Peer_OnIdentify(PeerConnection sender) { _logger.LogInformation($"Peer identify {sender.RemoteEndPoint} - {sender.RemoteId}"); + + if (!string.IsNullOrEmpty(sender.ConnectionString)) + { + var peers = _peerRoundRobin.Where(x => x.ConnectionString == sender.ConnectionString); + foreach (var peer in peers) + peer.NodeId = sender.RemoteId; + } + + //remove duplicate connections + lock (_duplicateLock) + { + var peers = GetActivePeers(); + foreach (var peer in peers.Where(x => x.RemoteId == sender.RemoteId && x != sender)) + peer.Disconnect(); + } + + //remove connection to self if (sender.RemoteId == NodeId) { if (!string.IsNullOrEmpty(sender.ConnectionString)) @@ -190,6 +211,9 @@ private async void Peer_OnReceiveMessage(PeerConnection sender, byte command, by case Commands.Txn: await ProcessTransaction(data, sender.RemoteId); break; + case Commands.TxnRequest: + ProcessTxnRequest(sender); + break; } } catch (Exception ex) @@ -255,7 +279,17 @@ private async Task ProcessTransaction(byte[] data, Guid originId) }); } } - + + private void ProcessTxnRequest(PeerConnection peer) + { + var txns = _pendingTransactionList.Get; + + foreach (var txn in txns) + { + var data = SerializeObject(txn); + SendTxn(peer, data); + } + } private void Peer_OnDisconnect(PeerConnection sender) { @@ -264,6 +298,7 @@ private void Peer_OnDisconnect(PeerConnection sender) { _logger.LogInformation($"Peer disconnect {sender.RemoteId} {sender.RemoteEndPoint}"); _peerConnections.Remove(sender); + sender.Close(); } catch (Exception ex) { @@ -345,7 +380,8 @@ private async void SharePeers(object state) private void ConnectOut() { - var peersOut = GetActivePeers().Where(x => x.Outgoing); + var activePeers = GetActivePeers(); + var peersOut = activePeers.Where(x => x.Outgoing); var target = (TargetOutgoingCount - peersOut.Count()); if (target <= 0) return; @@ -367,6 +403,9 @@ private void ConnectOut() if (peersOut.Any(x => x.ConnectionString == kp.ConnectionString)) continue; + if (activePeers.Any(x => x.RemoteId == kp.NodeId)) + continue; + _logger.LogInformation($"Connecting to {kp.ConnectionString}"); OnboardPeer(kp.ConnectionString); actual++; @@ -574,5 +613,6 @@ internal class Commands public const byte Txn = 2; public const byte BlockRequest = 3; public const byte PeerShare = 4; + public const byte TxnRequest = 5; } } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoPeerDirectory.cs b/Providers/NBlockchain.MongoDB/Services/MongoPeerDirectory.cs index e208792..fd1ca6f 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoPeerDirectory.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoPeerDirectory.cs @@ -76,6 +76,8 @@ public MongoPeerNode(KnownPeer node) { this.ConnectionString = node.ConnectionString; this.LastContact = node.LastContact; + this.IsSelf = node.IsSelf; + this.NodeId = node.NodeId; } public MongoPeerNode(ObjectId id, KnownPeer node) @@ -83,6 +85,8 @@ public MongoPeerNode(ObjectId id, KnownPeer node) this.Id = id; this.ConnectionString = node.ConnectionString; this.LastContact = node.LastContact; + this.IsSelf = node.IsSelf; + this.NodeId = node.NodeId; } } } diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 044b205..4e6f5e0 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -24,17 +24,14 @@ class Program private static ICustomTransactionRepository _txnRepo; private static IBlockRepository _blockRepo; - private static IServiceProvider ConfigureNode(string db, uint port) + private static IServiceProvider ConfigureForLiteDb(string db, uint port) { IServiceCollection services = new ServiceCollection(); services.AddBlockchain(x => { x.UseDataConnection("node.db"); x.UseTransactionRepository(); - x.UseTcpPeerNetwork(port); - //x.UseMongoDB(@"mongodb://localhost:27017", db) - // .UseTransactionRepository(); - //x.AddPeerDiscovery(sp => new StaticPeerDiscovery("tcp://localhost:503")); + x.UseTcpPeerNetwork(port); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); x.AddTransactionType(); x.AddTransactionType(); @@ -60,9 +57,43 @@ private static IServiceProvider ConfigureNode(string db, uint port) return serviceProvider; } + private static IServiceProvider ConfigureForMongoDB(string db, uint port) + { + IServiceCollection services = new ServiceCollection(); + services.AddBlockchain(x => + { + x.UseTcpPeerNetwork(port); + x.UseMongoDB(@"mongodb://localhost:27017", db) + .UseTransactionRepository(); + //x.AddPeerDiscovery(sp => new StaticPeerDiscovery("tcp://localhost:503")); + x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + x.AddTransactionType(); + x.AddTransactionType(); + x.AddTransactionRule(); + x.AddTransactionRule(); + x.UseBlockbaseProvider(); + x.UseParameters(new StaticNetworkParameters() + { + BlockTime = TimeSpan.FromSeconds(120), + HeaderVersion = 1, + ExpectedContentThreshold = 0.8m + }); + }); + + services.AddLogging(); + var serviceProvider = services.BuildServiceProvider(); + + //config logging + var loggerFactory = serviceProvider.GetService(); + loggerFactory.AddDebug(); + loggerFactory.AddFile("node.log", LogLevel.Debug); + + return serviceProvider; + } + static void Main(string[] args) { - var serviceProvider = ConfigureNode("DigitalCurrency", 10500); + var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); From cd96d0dc6d3fd1388921bf8f2df4e27379c210d2 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 10 Sep 2017 13:12:02 -0700 Subject: [PATCH 20/66] . --- NBlockchain/Models/BlockchainOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 0d080af..0517e33 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -67,7 +67,7 @@ public void UseBlockRepository(Func factory) public void UseTcpPeerNetwork(uint port) { - Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService())); + Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService())); } public void UseDataConnection(string connectionString) From 400d6d1de713ef456042ddaefdf3120141feddd4 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 10 Sep 2017 22:13:25 -0700 Subject: [PATCH 21/66] tweaks --- NBlockchain/Models/KnownPeer.cs | 1 + NBlockchain/Services/Net/PeerConnection.cs | 20 ++++++++++++++------ NBlockchain/Services/Net/TcpPeerNetwork.cs | 11 +++++++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/NBlockchain/Models/KnownPeer.cs b/NBlockchain/Models/KnownPeer.cs index 075a7c1..1b72b83 100644 --- a/NBlockchain/Models/KnownPeer.cs +++ b/NBlockchain/Models/KnownPeer.cs @@ -10,5 +10,6 @@ public class KnownPeer public DateTime LastContact { get; set; } public bool IsSelf { get; set; } public Guid NodeId { get; set; } + public bool Unreachable { get; set; } } } diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index 2933d23..8729bed 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -57,15 +57,15 @@ public PeerConnection(byte[] serviceIdentifier, Guid nodeId, TcpClient client) Task.Factory.StartNew(Poll); } - public void Connect(string connectionString) + public async Task Connect(string connectionString) { ConnectionString = connectionString; var uri = new Uri(connectionString); if (uri.Scheme != "tcp") throw new InvalidOperationException("Only tcp connections are possible"); - - _client.ConnectAsync(uri.Host, uri.Port).Wait(); - Task.Factory.StartNew(Poll); + + await _client.ConnectAsync(uri.Host, uri.Port); + var pollTask = Task.Factory.StartNew(Poll); Send(NetworkQualifier, IdentifyCommand, _localId.ToByteArray()); } @@ -140,8 +140,16 @@ public void Close() { Task.Factory.StartNew(() => { - SpinWait.SpinUntil(() => _pollExited); - _client.Dispose(); + try + { + _cancelToken.Cancel(); + SpinWait.SpinUntil(() => _pollExited); + _client.Dispose(); + } + catch (Exception ex) + { + OnPeerException?.Invoke(this, ex); + } }); } diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index 8caa476..1355d20 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -163,7 +163,10 @@ private void Peer_OnIdentify(PeerConnection sender) { var peers = GetActivePeers(); foreach (var peer in peers.Where(x => x.RemoteId == sender.RemoteId && x != sender)) + { peer.Disconnect(); + peer.Close(); + } } //remove connection to self @@ -176,6 +179,7 @@ private void Peer_OnIdentify(PeerConnection sender) self.IsSelf = true; } sender.Disconnect(); + sender.Close(); } } @@ -310,12 +314,12 @@ private void Peer_OnDisconnect(PeerConnection sender) } } - private void OnboardPeer(string connStr) + private async Task OnboardPeer(string connStr) { try { var peer = new PeerConnection(_serviceId, NodeId); - peer.Connect(connStr); + await peer.Connect(connStr); AttachEventHandlers(peer); _peerEvent.WaitOne(); try @@ -330,6 +334,9 @@ private void OnboardPeer(string connStr) catch (Exception ex) { _logger.LogError($"Error connecting to {connStr} - {ex.Message}"); + var peers = _peerRoundRobin.Where(x => x.ConnectionString == connStr); + foreach (var peer in peers) + peer.Unreachable = true; } } From baab9f781e6a764d0b35f5d8eb3e8e56de6a0ad6 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 11 Sep 2017 17:42:37 -0700 Subject: [PATCH 22/66] tweaks --- NBlockchain/Interfaces/IBlockRule.cs | 14 +++ NBlockchain/Interfaces/IBlockVerifier.cs | 2 +- NBlockchain/Interfaces/IExpectedBlockList.cs | 9 ++ NBlockchain/Interfaces/INetworkParameters.cs | 1 - NBlockchain/Models/BlockchainOptions.cs | 16 ++- NBlockchain/Models/StaticNetworkParameters.cs | 1 - NBlockchain/Models/TransactionBucket.cs | 97 ------------------- .../Rules/BlockContentThresholdRule.cs | 34 +++++++ .../{Services => Rules}/TransactionRule.cs | 2 +- NBlockchain/Services/BlockVerifier.cs | 24 ++--- NBlockchain/Services/ExpectedBlockList.cs | 88 +++++++++++++++++ NBlockchain/Services/Net/PeerConnection.cs | 3 +- NBlockchain/Services/NodeHost.cs | 28 +++--- Samples/DigitalCurrency/Program.cs | 6 +- Samples/DigitalCurrency/Rules/BalanceRule.cs | 1 + Samples/DigitalCurrency/Rules/CoinbaseRule.cs | 1 + ScratchPad/Program.cs | 3 +- ScratchPad/TestTransactionValidator.cs | 1 + .../NodeSync/NodeOnboardingScenarios.cs | 3 +- 19 files changed, 200 insertions(+), 134 deletions(-) create mode 100644 NBlockchain/Interfaces/IBlockRule.cs create mode 100644 NBlockchain/Interfaces/IExpectedBlockList.cs delete mode 100644 NBlockchain/Models/TransactionBucket.cs create mode 100644 NBlockchain/Rules/BlockContentThresholdRule.cs rename NBlockchain/{Services => Rules}/TransactionRule.cs (96%) create mode 100644 NBlockchain/Services/ExpectedBlockList.cs diff --git a/NBlockchain/Interfaces/IBlockRule.cs b/NBlockchain/Interfaces/IBlockRule.cs new file mode 100644 index 0000000..5b079ab --- /dev/null +++ b/NBlockchain/Interfaces/IBlockRule.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using NBlockchain.Models; + +namespace NBlockchain.Interfaces +{ + public interface IBlockRule + { + bool Validate(Block block); + bool TailRule { get; } + } +} diff --git a/NBlockchain/Interfaces/IBlockVerifier.cs b/NBlockchain/Interfaces/IBlockVerifier.cs index 4f562bb..25053e4 100644 --- a/NBlockchain/Interfaces/IBlockVerifier.cs +++ b/NBlockchain/Interfaces/IBlockVerifier.cs @@ -7,7 +7,7 @@ public interface IBlockVerifier { bool Verify(Block block); - bool VerifyContentThreshold(ICollection actual, ICollection expected); + bool VerifyBlockRules(Block block, bool tail); int VerifyTransaction(TransactionEnvelope transaction, ICollection siblings); } diff --git a/NBlockchain/Interfaces/IExpectedBlockList.cs b/NBlockchain/Interfaces/IExpectedBlockList.cs new file mode 100644 index 0000000..9ac074d --- /dev/null +++ b/NBlockchain/Interfaces/IExpectedBlockList.cs @@ -0,0 +1,9 @@ +namespace NBlockchain.Interfaces +{ + public interface IExpectedBlockList + { + void Confirm(byte[] previousId); + void ExpectNext(byte[] previousId); + bool IsExpected(byte[] previousId); + } +} \ No newline at end of file diff --git a/NBlockchain/Interfaces/INetworkParameters.cs b/NBlockchain/Interfaces/INetworkParameters.cs index 9bb8961..221d963 100644 --- a/NBlockchain/Interfaces/INetworkParameters.cs +++ b/NBlockchain/Interfaces/INetworkParameters.cs @@ -6,6 +6,5 @@ public interface INetworkParameters { TimeSpan BlockTime { get; } uint HeaderVersion { get; } - decimal ExpectedContentThreshold { get; } } } \ No newline at end of file diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 0517e33..58372a2 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -10,6 +10,7 @@ using NBlockchain.Services.Hashers; using NBlockchain.Services.Net; using NBlockchain.Services.PeerDiscovery; +using NBlockchain.Rules; namespace NBlockchain.Models { @@ -92,6 +93,12 @@ public void AddTransactionRule() Services.AddTransient(typeof(ITransactionRule), typeof(T)); } + public void AddBlockRule() + where T : IBlockRule + { + Services.AddTransient(typeof(IBlockRule), typeof(T)); + } + public void AddPeerDiscovery() where T : IPeerDiscoveryService { @@ -103,6 +110,11 @@ public void AddPeerDiscovery(Func facto Services.AddTransient(factory); } + public void AddContentThresholdBlockRule(decimal threshold) + { + Services.AddTransient(typeof(IBlockRule), sp => new BlockContentThresholdRule(sp.GetService(), threshold)); + } + public void UseMulticastDiscovery(string serviceId, string multicastAddress, int port) { Services.AddTransient(sp => new MulticastDiscovery(serviceId, multicastAddress, port, sp.GetService(), sp.GetService())); @@ -129,8 +141,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton, x => new StaticNetworkParameters() { BlockTime = TimeSpan.FromMinutes(1), - HeaderVersion = 1, - ExpectedContentThreshold = 0.8m + HeaderVersion = 1 }); AddDefault(ServiceLifetime.Transient); @@ -160,6 +171,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); } diff --git a/NBlockchain/Models/StaticNetworkParameters.cs b/NBlockchain/Models/StaticNetworkParameters.cs index 72131b8..abafc07 100644 --- a/NBlockchain/Models/StaticNetworkParameters.cs +++ b/NBlockchain/Models/StaticNetworkParameters.cs @@ -9,6 +9,5 @@ public class StaticNetworkParameters : INetworkParameters { public TimeSpan BlockTime { get; set; } public uint HeaderVersion { get; set; } - public decimal ExpectedContentThreshold { get; set; } } } diff --git a/NBlockchain/Models/TransactionBucket.cs b/NBlockchain/Models/TransactionBucket.cs deleted file mode 100644 index bac2837..0000000 --- a/NBlockchain/Models/TransactionBucket.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using NBlockchain.Services; - -namespace NBlockchain.Models -{ - public class TransactionBucket - { - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); - private readonly Dictionary> _buckets = new Dictionary>(); - private readonly Dictionary _txns = new Dictionary(new ByteArrayEqualityComparer()); - private readonly IEqualityComparer _byteArrayEqualityComparer = new ByteArrayEqualityComparer(); - - public bool AddTransaction(byte[] txnId, TransactionEnvelope txn, uint height) - { - _resetEvent.WaitOne(); - try - { - EnsureKey(height); - if (_buckets[height].Add(txnId)) - { - _txns[txnId] = txn; - return true; - } - return false; - } - finally - { - _resetEvent.Set(); - } - } - - public ICollection GetBucket(uint height) - { - _resetEvent.WaitOne(); - try - { - EnsureKey(height); - return _buckets[height]; - } - finally - { - _resetEvent.Set(); - } - } - - public void Shift(uint height, ICollection toRemove) - { - _resetEvent.WaitOne(); - try - { - EnsureKey(height); - EnsureKey(height + 1); - - foreach (var item in _buckets[height].Where(x => !toRemove.Contains(x, _byteArrayEqualityComparer))) - _buckets[height + 1].Add(item); - - _buckets.Remove(height); - - foreach (var txnId in toRemove) - _txns.Remove(txnId); - } - finally - { - _resetEvent.Set(); - } - } - - public ICollection GetTransactions(uint height) - { - _resetEvent.WaitOne(); - try - { - if (_buckets.ContainsKey(height)) - { - return _txns.Where(x => _buckets[height].Contains(x.Key, _byteArrayEqualityComparer)) - .Select(x => x.Value) - .ToList(); - } - return new List(); - } - finally - { - _resetEvent.Set(); - } - } - - private void EnsureKey(uint height) - { - if (!_buckets.ContainsKey(height)) - _buckets.Add(height, new HashSet(_byteArrayEqualityComparer)); - } - - } -} diff --git a/NBlockchain/Rules/BlockContentThresholdRule.cs b/NBlockchain/Rules/BlockContentThresholdRule.cs new file mode 100644 index 0000000..2be9308 --- /dev/null +++ b/NBlockchain/Rules/BlockContentThresholdRule.cs @@ -0,0 +1,34 @@ +using NBlockchain.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using NBlockchain.Models; + +namespace NBlockchain.Rules +{ + public class BlockContentThresholdRule : IBlockRule + { + private readonly IPendingTransactionList _pendingTransactions; + private readonly decimal _threshold; + + public bool TailRule => true; + + public BlockContentThresholdRule(IPendingTransactionList pendingTransactions, decimal threshold) + { + _pendingTransactions = pendingTransactions; + _threshold = threshold; + } + + public bool Validate(Block block) + { + var expected = _pendingTransactions.Get; + if (expected.Count == 0) + return true; + + var count = expected.Count(txn => block.Transactions.Any(actual => actual.OriginKey == txn.OriginKey)); + var ratio = (decimal)count / (decimal)expected.Count; + return (ratio >= _threshold); + } + + } +} diff --git a/NBlockchain/Services/TransactionRule.cs b/NBlockchain/Rules/TransactionRule.cs similarity index 96% rename from NBlockchain/Services/TransactionRule.cs rename to NBlockchain/Rules/TransactionRule.cs index 66afa30..d2f0806 100644 --- a/NBlockchain/Services/TransactionRule.cs +++ b/NBlockchain/Rules/TransactionRule.cs @@ -8,7 +8,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace NBlockchain.Services +namespace NBlockchain.Rules { public abstract class TransactionRule : ITransactionRule where T : class diff --git a/NBlockchain/Services/BlockVerifier.cs b/NBlockchain/Services/BlockVerifier.cs index ed0b84d..76d2abe 100644 --- a/NBlockchain/Services/BlockVerifier.cs +++ b/NBlockchain/Services/BlockVerifier.cs @@ -12,7 +12,8 @@ namespace NBlockchain.Services public class BlockVerifier : IBlockVerifier { private readonly INetworkParameters _parameters; - private readonly IEnumerable _txnValidators; + private readonly IEnumerable _txnRules; + private readonly IEnumerable _blockRules; private readonly IEnumerable _validTxnTypes; private readonly IAddressEncoder _addressEncoder; private readonly ISignatureService _signatureService; @@ -22,12 +23,13 @@ public class BlockVerifier : IBlockVerifier private readonly ITransactionKeyResolver _transactionKeyResolver; private readonly IEqualityComparer _byteArrayEqualityComparer = new ByteArrayEqualityComparer(); - public BlockVerifier(INetworkParameters parameters, IAddressEncoder addressEncoder, ISignatureService signatureService, IEnumerable txnValidators, IEnumerable validTxnTypes, IMerkleTreeBuilder merkleTreeBuilder, ITransactionKeyResolver transactionKeyResolver, IHashTester hashTester, IHasher hasher) + public BlockVerifier(INetworkParameters parameters, IAddressEncoder addressEncoder, ISignatureService signatureService, IEnumerable txnRules, IEnumerable blockRules, IEnumerable validTxnTypes, IMerkleTreeBuilder merkleTreeBuilder, ITransactionKeyResolver transactionKeyResolver, IHashTester hashTester, IHasher hasher) { _parameters = parameters; _addressEncoder = addressEncoder; _signatureService = signatureService; - _txnValidators = txnValidators; + _txnRules = txnRules; + _blockRules = blockRules; _validTxnTypes = validTxnTypes; _merkleTreeBuilder = merkleTreeBuilder; _transactionKeyResolver = transactionKeyResolver; @@ -62,14 +64,14 @@ public bool Verify(Block block) return true; } - public bool VerifyContentThreshold(ICollection actual, ICollection expected) + public bool VerifyBlockRules(Block block, bool tail) { - if (expected.Count == 0) - return true; - - var count = expected.Count(txn => actual.Contains(txn, _byteArrayEqualityComparer)); - var ratio = (decimal)count / (decimal)expected.Count; - return (ratio >= _parameters.ExpectedContentThreshold); + foreach (var rule in _blockRules.Where(x => x.TailRule == tail || tail)) + { + if (!rule.Validate(block)) + return false; + } + return true; } public int VerifyTransaction(TransactionEnvelope transaction, ICollection siblings) @@ -85,7 +87,7 @@ public int VerifyTransaction(TransactionEnvelope transaction, ICollection v.TransactionType == transaction.TransactionType)) + foreach (var validator in _txnRules.Where(v => v.TransactionType == transaction.TransactionType)) result = result & validator.Validate(transaction, siblings); return result; diff --git a/NBlockchain/Services/ExpectedBlockList.cs b/NBlockchain/Services/ExpectedBlockList.cs new file mode 100644 index 0000000..1e48140 --- /dev/null +++ b/NBlockchain/Services/ExpectedBlockList.cs @@ -0,0 +1,88 @@ +using NBlockchain.Interfaces; +using NBlockchain.Models; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace NBlockchain.Services +{ + public class ExpectedBlockList : IExpectedBlockList + { + private readonly Dictionary _expectedExpiries = new Dictionary(new ByteArrayEqualityComparer()); + private readonly AutoResetEvent _resetEvt = new AutoResetEvent(true); + private readonly INetworkParameters _parameters; + private readonly Timer _timer; + + public ExpectedBlockList(INetworkParameters parameters) + { + _parameters = parameters; + _timer = new Timer(ExpireUnconfirmed, null, _parameters.BlockTime, _parameters.BlockTime); + } + + public void ExpectNext(byte[] previousId) + { + _resetEvt.WaitOne(); + try + { + if (!_expectedExpiries.ContainsKey(previousId)) + _expectedExpiries[previousId] = DateTime.MaxValue; + } + finally + { + _resetEvt.Set(); + } + } + + public bool IsExpected(byte[] previousId) + { + _resetEvt.WaitOne(); + try + { + if (_expectedExpiries.ContainsKey(previousId)) + return (_expectedExpiries[previousId] > DateTime.Now); + } + finally + { + _resetEvt.Set(); + } + + return false; + } + + public void Confirm(byte[] previousId) + { + _resetEvt.WaitOne(); + try + { + if (_expectedExpiries.ContainsKey(previousId)) + _expectedExpiries[previousId] = DateTime.Now.Add(_parameters.BlockTime); + } + finally + { + _resetEvt.Set(); + } + } + + private void ExpireUnconfirmed(object state) + { + _resetEvt.WaitOne(); + try + { + var keys = _expectedExpiries.Keys; + foreach (var key in keys) + { + if (_expectedExpiries[key] < DateTime.Now) + _expectedExpiries.Remove(key); + } + } + finally + { + _resetEvt.Set(); + } + } + + } +} diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index 8729bed..a6b1ece 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -144,7 +144,8 @@ public void Close() { _cancelToken.Cancel(); SpinWait.SpinUntil(() => _pollExited); - _client.Dispose(); + _client?.GetStream().Close(); + _client?.Dispose(); } catch (Exception ex) { diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 5c8793f..3b1168e 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -19,7 +19,7 @@ public class NodeHost : INodeHost private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; private readonly IDateTimeProvider _dateTimeProvider; - private readonly ITransactionKeyResolver _transactionKeyResolver; + private readonly IExpectedBlockList _expectedBlockList; private readonly IPeerNetwork _peerNetwork; private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); private readonly IPendingTransactionList _pendingTransactionList; @@ -28,17 +28,17 @@ public class NodeHost : INodeHost private readonly Timer _pollTimer; - public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, INetworkParameters parameters, IDateTimeProvider dateTimeProvider, ITransactionKeyResolver transactionKeyResolver, IPendingTransactionList pendingTransactionList, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator) + public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, INetworkParameters parameters, IDateTimeProvider dateTimeProvider, IPendingTransactionList pendingTransactionList, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) { _blockRepository = blockRepository; _blockVerifier = blockVerifier; _serviceProvider = serviceProvider; _parameters = parameters; _dateTimeProvider = dateTimeProvider; - _transactionKeyResolver = transactionKeyResolver; _pendingTransactionList = pendingTransactionList; _peerNetwork = peerNetwork; _difficultyCalculator = difficultyCalculator; + _expectedBlockList = expectedBlockList; _logger = loggerFactory.CreateLogger(); _peerNetwork.RegisterBlockReceiver(this); @@ -83,15 +83,13 @@ public async Task RecieveTail(Block block) _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Demerit; } + + if (!_blockVerifier.VerifyBlockRules(block, true)) + { + _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Ignore; + } - var contentTxnIds = block.Transactions.Select(x => _transactionKeyResolver.ResolveKey(x)).ToList(); - - //if (!_blockVerifier.VerifyContentThreshold(contentTxnIds, _pendingTransactionList.Get)) - //{ - // _logger.LogWarning($"Block content verification failed for {BitConverter.ToString(block.Header.BlockId)}"); - // return PeerDataResult.Ignore; - //} - await _blockRepository.AddBlock(block); _pendingTransactionList.Remove(block.Transactions); @@ -127,6 +125,12 @@ public async Task RecieveBlock(Block block) return PeerDataResult.Demerit; } + if (!_blockVerifier.VerifyBlockRules(block, false)) + { + _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Ignore; + } + await _blockRepository.AddBlock(block); _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); @@ -175,6 +179,7 @@ private async void GetMissingBlocks(object state) if (prevHeader == null) { _logger.LogDebug("Requesting head block"); + _expectedBlockList.ExpectNext(Block.HeadKey); _peerNetwork.RequestNextBlock(Block.HeadKey); return; } @@ -182,6 +187,7 @@ private async void GetMissingBlocks(object state) //if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) { _logger.LogDebug($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); + _expectedBlockList.ExpectNext(prevHeader.BlockId); _peerNetwork.RequestNextBlock(prevHeader.BlockId); } } diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 4e6f5e0..f3a9c45 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -41,8 +41,7 @@ private static IServiceProvider ConfigureForLiteDb(string db, uint port) x.UseParameters(new StaticNetworkParameters() { BlockTime = TimeSpan.FromSeconds(120), - HeaderVersion = 1, - ExpectedContentThreshold = 0.8m + HeaderVersion = 1 }); }); @@ -75,8 +74,7 @@ private static IServiceProvider ConfigureForMongoDB(string db, uint port) x.UseParameters(new StaticNetworkParameters() { BlockTime = TimeSpan.FromSeconds(120), - HeaderVersion = 1, - ExpectedContentThreshold = 0.8m + HeaderVersion = 1 }); }); diff --git a/Samples/DigitalCurrency/Rules/BalanceRule.cs b/Samples/DigitalCurrency/Rules/BalanceRule.cs index 80b94ea..28e6da8 100644 --- a/Samples/DigitalCurrency/Rules/BalanceRule.cs +++ b/Samples/DigitalCurrency/Rules/BalanceRule.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Text; +using NBlockchain.Rules; namespace DigitalCurrency.Rules { diff --git a/Samples/DigitalCurrency/Rules/CoinbaseRule.cs b/Samples/DigitalCurrency/Rules/CoinbaseRule.cs index 73556f6..52608ae 100644 --- a/Samples/DigitalCurrency/Rules/CoinbaseRule.cs +++ b/Samples/DigitalCurrency/Rules/CoinbaseRule.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Text; +using NBlockchain.Rules; namespace DigitalCurrency.Rules { diff --git a/ScratchPad/Program.cs b/ScratchPad/Program.cs index 75b96de..9129672 100644 --- a/ScratchPad/Program.cs +++ b/ScratchPad/Program.cs @@ -157,8 +157,7 @@ private static IServiceProvider ConfigureNode(string db, uint port, string[] pee x.UseParameters(new StaticNetworkParameters() { BlockTime = TimeSpan.FromSeconds(10), - HeaderVersion = 1, - ExpectedContentThreshold = 0.8m + HeaderVersion = 1 }); }); diff --git a/ScratchPad/TestTransactionValidator.cs b/ScratchPad/TestTransactionValidator.cs index 16e02bb..2bac316 100644 --- a/ScratchPad/TestTransactionValidator.cs +++ b/ScratchPad/TestTransactionValidator.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using NBlockchain.Rules; namespace ScratchPad { diff --git a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs index c8ec8e5..17518b1 100644 --- a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs +++ b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs @@ -28,8 +28,7 @@ private static IServiceProvider ConfigureNode(uint port, ICollection pee x.UseParameters(new StaticNetworkParameters() { BlockTime = TimeSpan.FromSeconds(10), - HeaderVersion = 1, - ExpectedContentThreshold = 0.8m + HeaderVersion = 1 }); }); From 70af8b6393c8e3d8d9590758e8a0c597923243f0 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 11 Sep 2017 21:56:25 -0700 Subject: [PATCH 23/66] platform issues --- NBlockchain/Services/Net/PeerConnection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index a6b1ece..462f981 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -144,7 +144,7 @@ public void Close() { _cancelToken.Cancel(); SpinWait.SpinUntil(() => _pollExited); - _client?.GetStream().Close(); + _client?.GetStream().Dispose(); _client?.Dispose(); } catch (Exception ex) From 077573848cc2e37f861dd3e98345b71c08428869 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 12 Sep 2017 19:14:44 -0700 Subject: [PATCH 24/66] tweaks --- NBlockchain/Interfaces/IPeerNetwork.cs | 2 +- .../Services/Net/InProcessPeerNetwork.cs | 4 +- NBlockchain/Services/Net/TcpPeerNetwork.cs | 75 ++++++++++--------- NBlockchain/Services/NodeHost.cs | 15 +++- 4 files changed, 58 insertions(+), 38 deletions(-) diff --git a/NBlockchain/Interfaces/IPeerNetwork.cs b/NBlockchain/Interfaces/IPeerNetwork.cs index d435349..181a175 100644 --- a/NBlockchain/Interfaces/IPeerNetwork.cs +++ b/NBlockchain/Interfaces/IPeerNetwork.cs @@ -20,7 +20,7 @@ public interface IPeerNetwork void RegisterTransactionReceiver(ITransactionReceiver transactionReciever); - void DiscoverPeers(); + Task DiscoverPeers(); void Open(); diff --git a/NBlockchain/Services/Net/InProcessPeerNetwork.cs b/NBlockchain/Services/Net/InProcessPeerNetwork.cs index e83fb49..e8d8716 100644 --- a/NBlockchain/Services/Net/InProcessPeerNetwork.cs +++ b/NBlockchain/Services/Net/InProcessPeerNetwork.cs @@ -34,9 +34,11 @@ public void RegisterTransactionReceiver(ITransactionReceiver transactionReciever _transactionReciever = transactionReciever; } - public void DiscoverPeers() +#pragma warning disable CS1998 + public async Task DiscoverPeers() { } +#pragma warning restore CS1998 public void Open() { diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index 1355d20..c1f632f 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -40,6 +40,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private TcpListener _listener; private readonly AutoResetEvent _peerEvent = new AutoResetEvent(true); + private readonly AutoResetEvent _connectOutEvent = new AutoResetEvent(true); private Timer _sharePeersTimer; private Timer _discoveryTimer; @@ -111,9 +112,9 @@ public void Open() DiscoverOwnConnectionStrings(); AdvertiseToPeers(); - _discoveryTimer = new Timer((state) => + _discoveryTimer = new Timer(async (state) => { - DiscoverPeers(); + await DiscoverPeers(); }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30)); _sharePeersTimer = new Timer(SharePeers, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); @@ -352,25 +353,22 @@ public void Close() } } - public void DiscoverPeers() + public async Task DiscoverPeers() { - foreach (var discovery in _discoveryServices) + Parallel.ForEach(_discoveryServices, async discovery => { - Task.Factory.StartNew(async () => + try { - try - { - var newPeers = await discovery.DiscoverPeers(); - foreach (var np in newPeers) - AddPeer(np); - ConnectOut(); - } - catch (Exception ex) - { - _logger.LogError(ex.Message); - } - }); - } + var newPeers = await discovery.DiscoverPeers(); + foreach (var np in newPeers) + AddPeer(np); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + }); + await ConnectOut(); } private void AddPeer(KnownPeer newPeer) @@ -385,40 +383,47 @@ private async void SharePeers(object state) await ds.SharePeers(_peerRoundRobin.ToList()); } - private void ConnectOut() + private async Task ConnectOut() { var activePeers = GetActivePeers(); - var peersOut = activePeers.Where(x => x.Outgoing); + var peersOut = activePeers.Where(x => x.Outgoing).ToList(); var target = (TargetOutgoingCount - peersOut.Count()); if (target <= 0) return; var actual = 0; var counter = 0; - lock (_peerRoundRobin) + + _connectOutEvent.WaitOne(); + try { while ((actual < target) && (counter < _peerRoundRobin.Count)) { - if (_peerRoundRobin.TryDequeue(out var kp)) - { - _peerRoundRobin.Enqueue(kp); - counter++; + counter++; - if (kp.IsSelf) - continue; + if (!_peerRoundRobin.TryDequeue(out var kp)) + continue; - if (peersOut.Any(x => x.ConnectionString == kp.ConnectionString)) - continue; + _peerRoundRobin.Enqueue(kp); - if (activePeers.Any(x => x.RemoteId == kp.NodeId)) - continue; + if (kp.IsSelf) + continue; - _logger.LogInformation($"Connecting to {kp.ConnectionString}"); - OnboardPeer(kp.ConnectionString); - actual++; - } + if (peersOut.Any(x => x.ConnectionString == kp.ConnectionString)) + continue; + + if (activePeers.Any(x => x.RemoteId == kp.NodeId)) + continue; + + _logger.LogInformation($"Connecting to {kp.ConnectionString}"); + await OnboardPeer(kp.ConnectionString); + actual++; } } + finally + { + _connectOutEvent.Set(); + } } public void RegisterBlockReceiver(IBlockReceiver blockReceiver) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 3b1168e..4f82fb1 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -49,10 +49,17 @@ public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, public async Task RecieveTail(Block block) { + if (!_expectedBlockList.IsExpected(block.Header.PreviousBlock)) + { + _logger.LogDebug($"Unexpected next block for {BitConverter.ToString(block.Header.PreviousBlock)}"); + return PeerDataResult.Ignore; + } + _blockEvent.WaitOne(); try { _logger.LogDebug($"Recv tail {BitConverter.ToString(block.Header.BlockId)}"); + _expectedBlockList.Confirm(block.Header.PreviousBlock); if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; @@ -105,11 +112,17 @@ public async Task RecieveTail(Block block) public async Task RecieveBlock(Block block) { + if (!_expectedBlockList.IsExpected(block.Header.PreviousBlock)) + { + _logger.LogDebug($"Unexpected next block for {BitConverter.ToString(block.Header.PreviousBlock)}"); + return PeerDataResult.Ignore; + } + _blockEvent.WaitOne(); try { _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)}"); - + _expectedBlockList.Confirm(block.Header.PreviousBlock); if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; From 9582d96045b729ace16ef853c0d1f35df2547486 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Tue, 12 Sep 2017 19:40:06 -0700 Subject: [PATCH 25/66] tweak --- NBlockchain/Services/ExpectedBlockList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NBlockchain/Services/ExpectedBlockList.cs b/NBlockchain/Services/ExpectedBlockList.cs index 1e48140..e227950 100644 --- a/NBlockchain/Services/ExpectedBlockList.cs +++ b/NBlockchain/Services/ExpectedBlockList.cs @@ -71,7 +71,7 @@ private void ExpireUnconfirmed(object state) _resetEvt.WaitOne(); try { - var keys = _expectedExpiries.Keys; + var keys = new List(_expectedExpiries.Keys); foreach (var key in keys) { if (_expectedExpiries[key] < DateTime.Now) From fdbefadd00229f083ab0b92e2cf941170e1375ba Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Thu, 14 Sep 2017 19:38:58 -0700 Subject: [PATCH 26/66] Instructions (#4) instructions --- .../Services/Database/BlockStatistics.cs | 2 + .../Services/Database/PersistedBlock.cs | 16 +-- .../Services/Database/PersistedEntity.cs | 2 + NBlockchain.sln | 7 - NBlockchain/Interfaces/IAddressEncoder.cs | 3 +- .../IAsymetricCryptographyService.cs | 11 ++ NBlockchain/Interfaces/IBlockRepository.cs | 1 + NBlockchain/Interfaces/IBlockRule.cs | 2 +- NBlockchain/Interfaces/IBlockVerifier.cs | 7 +- .../IBlockbaseTransactionBuilder.cs | 3 +- .../Interfaces/IInstructionRepository.cs | 12 ++ NBlockchain/Interfaces/INodeHost.cs | 2 +- NBlockchain/Interfaces/IPeerNetwork.cs | 2 +- .../Interfaces/IPendingTransactionList.cs | 20 --- NBlockchain/Interfaces/ISignatureService.cs | 5 +- NBlockchain/Interfaces/ITransactionBuilder.cs | 11 ++ .../Interfaces/ITransactionKeyResolver.cs | 5 +- .../Interfaces/ITransactionReceiver.cs | 2 +- NBlockchain/Interfaces/ITransactionRule.cs | 3 +- .../IUnconfirmedTransactionCache.cs | 20 +++ NBlockchain/Models/Block.cs | 4 +- NBlockchain/Models/BlockTransaction.cs | 10 -- NBlockchain/Models/BlockchainOptions.cs | 21 +-- NBlockchain/Models/Instruction.cs | 22 ++++ .../Models/InstructionTypeAttribute.cs | 16 +++ NBlockchain/Models/Transaction.cs | 24 ++++ NBlockchain/Models/TransactionEnvelope.cs | 30 ----- .../Models/TransactionTypeAttribute.cs | 16 --- ...sactionType.cs => ValidInstructionType.cs} | 8 +- NBlockchain/NBlockchain.csproj | 1 + .../Rules/BlockContentThresholdRule.cs | 17 +-- NBlockchain/Rules/TransactionRule.cs | 34 ----- NBlockchain/Services/AddressEncoder.cs | 36 +++--- .../Services/AsymetricCryptographyService.cs | 75 +++++++++++ NBlockchain/Services/BlockMiner.cs | 33 ++--- NBlockchain/Services/BlockVerifier.cs | 56 ++++---- .../Services/BlockbaseTransactionBuilder.cs | 37 ++---- .../Database/DefaultBlockRepository.cs | 31 +++-- ...Repository.cs => InstructionRepository.cs} | 13 +- .../Database/InstructionStatistics.cs | 11 ++ .../Services/Database/PersistedInstruction.cs | 27 ++++ .../Services/DefaultSignatureService.cs | 97 ++++++-------- .../Services/Net/InProcessPeerNetwork.cs | 4 +- NBlockchain/Services/Net/PeerConnection.cs | 4 +- NBlockchain/Services/Net/TcpPeerNetwork.cs | 12 +- NBlockchain/Services/NodeHost.cs | 43 ++++--- NBlockchain/Services/TransactionBuilder.cs | 27 ++++ .../Services/TransactionKeyResolver.cs | 15 +-- ...List.cs => UnconfirmedTransactionCache.cs} | 16 +-- .../Models/BlockStatistics.cs | 13 -- .../Models/PersistedBlock.cs | 34 ++++- .../Models/PersistedInstruction.cs | 20 +++ .../Models/PersistedTransaction.cs | 39 ++++++ .../ServiceCollectionExtensions.cs | 6 +- .../Services/MongoBlockRepository.cs | 46 ++++--- ...itory.cs => MongoInstructionRepository.cs} | 20 ++- Samples/DigitalCurrency/Program.cs | 120 +++++++++--------- ...ory.cs => ICustomInstructionRepository.cs} | 4 +- .../LiteDb/CustomInstructionRepository.cs | 43 +++++++ .../LiteDb/CustomTransactionRespository.cs | 39 ------ ...cs => CustomMongoInstructionRepository.cs} | 26 ++-- Samples/DigitalCurrency/Rules/BalanceRule.cs | 29 +++-- .../Rules/CoinbaseBlockRule.cs | 21 +++ Samples/DigitalCurrency/Rules/CoinbaseRule.cs | 21 --- .../Rules/CoinbaseTransactionRule.cs | 25 ++++ .../Transactions/CoinbaseBuilder.cs | 21 +-- ...eTransaction.cs => CoinbaseInstruction.cs} | 4 +- .../Transactions/TransferInstruction.cs | 22 ++++ .../Transactions/TransferTransaction.cs | 15 --- .../Transactions/ValueInstruction.cs | 17 +++ .../Transactions/ValueTransaction.cs | 12 -- ScratchPad/CustomTransactionRepository.cs | 38 ------ ScratchPad/ICustomTransactionRepository.cs | 7 - ScratchPad/IHashTester.cs | 7 - ScratchPad/Program.cs | 6 +- ScratchPad/TestBlockbaseBuilder.cs | 25 ---- ScratchPad/TestTransaction.cs | 26 ---- ScratchPad/TestTransactionValidator.cs | 43 ------- .../Common/BaseBuilder.cs | 17 ++- .../Common/TestTransaction.cs | 9 +- .../NodeSync/NodeOnboardingScenarios.cs | 2 +- 81 files changed, 887 insertions(+), 766 deletions(-) create mode 100644 NBlockchain/Interfaces/IAsymetricCryptographyService.cs create mode 100644 NBlockchain/Interfaces/IInstructionRepository.cs delete mode 100644 NBlockchain/Interfaces/IPendingTransactionList.cs create mode 100644 NBlockchain/Interfaces/ITransactionBuilder.cs create mode 100644 NBlockchain/Interfaces/IUnconfirmedTransactionCache.cs delete mode 100644 NBlockchain/Models/BlockTransaction.cs create mode 100644 NBlockchain/Models/Instruction.cs create mode 100644 NBlockchain/Models/InstructionTypeAttribute.cs create mode 100644 NBlockchain/Models/Transaction.cs delete mode 100644 NBlockchain/Models/TransactionEnvelope.cs delete mode 100644 NBlockchain/Models/TransactionTypeAttribute.cs rename NBlockchain/Models/{ValidTransactionType.cs => ValidInstructionType.cs} (53%) delete mode 100644 NBlockchain/Rules/TransactionRule.cs create mode 100644 NBlockchain/Services/AsymetricCryptographyService.cs rename NBlockchain/Services/Database/{TransactionRepository.cs => InstructionRepository.cs} (50%) create mode 100644 NBlockchain/Services/Database/InstructionStatistics.cs create mode 100644 NBlockchain/Services/Database/PersistedInstruction.cs create mode 100644 NBlockchain/Services/TransactionBuilder.cs rename NBlockchain/Services/{PendingTransactionList.cs => UnconfirmedTransactionCache.cs} (69%) delete mode 100644 Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs create mode 100644 Providers/NBlockchain.MongoDB/Models/PersistedInstruction.cs create mode 100644 Providers/NBlockchain.MongoDB/Models/PersistedTransaction.cs rename Providers/NBlockchain.MongoDB/Services/{MongoTransactionRepository.cs => MongoInstructionRepository.cs} (56%) rename Samples/DigitalCurrency/Repositories/{ICustomTransactionRepository.cs => ICustomInstructionRepository.cs} (55%) create mode 100644 Samples/DigitalCurrency/Repositories/LiteDb/CustomInstructionRepository.cs delete mode 100644 Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs rename Samples/DigitalCurrency/Repositories/Mongo/{CustomMongoTransactionRepository.cs => CustomMongoInstructionRepository.cs} (50%) create mode 100644 Samples/DigitalCurrency/Rules/CoinbaseBlockRule.cs delete mode 100644 Samples/DigitalCurrency/Rules/CoinbaseRule.cs create mode 100644 Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs rename Samples/DigitalCurrency/Transactions/{CoinbaseTransaction.cs => CoinbaseInstruction.cs} (62%) create mode 100644 Samples/DigitalCurrency/Transactions/TransferInstruction.cs delete mode 100644 Samples/DigitalCurrency/Transactions/TransferTransaction.cs create mode 100644 Samples/DigitalCurrency/Transactions/ValueInstruction.cs delete mode 100644 Samples/DigitalCurrency/Transactions/ValueTransaction.cs delete mode 100644 ScratchPad/CustomTransactionRepository.cs delete mode 100644 ScratchPad/ICustomTransactionRepository.cs delete mode 100644 ScratchPad/IHashTester.cs delete mode 100644 ScratchPad/TestBlockbaseBuilder.cs delete mode 100644 ScratchPad/TestTransaction.cs delete mode 100644 ScratchPad/TestTransactionValidator.cs diff --git a/NBlockChain/Services/Database/BlockStatistics.cs b/NBlockChain/Services/Database/BlockStatistics.cs index 4efd53e..6511c3a 100644 --- a/NBlockChain/Services/Database/BlockStatistics.cs +++ b/NBlockChain/Services/Database/BlockStatistics.cs @@ -7,5 +7,7 @@ namespace NBlockchain.Services.Database public class BlockStatistics { public int BlockTime { get; set; } + + public DateTime TimeStamp { get; set; } } } diff --git a/NBlockChain/Services/Database/PersistedBlock.cs b/NBlockChain/Services/Database/PersistedBlock.cs index a5d21da..f5a2681 100644 --- a/NBlockChain/Services/Database/PersistedBlock.cs +++ b/NBlockChain/Services/Database/PersistedBlock.cs @@ -16,21 +16,7 @@ public PersistedBlock(Block block) { Entity = new BlockInfo(block); Statistics = new BlockStatistics(); - } - } - - public class PersistedTransaction : PersistedEntity - { - public byte[] BlockId { get; set; } - - public PersistedTransaction() - { - } - - public PersistedTransaction(byte[] blockId, TransactionEnvelope txn) - { - Entity = txn; - BlockId = blockId; + Statistics.TimeStamp = new DateTime(block.Header.Timestamp); } } diff --git a/NBlockChain/Services/Database/PersistedEntity.cs b/NBlockChain/Services/Database/PersistedEntity.cs index 174fb27..1cd1f17 100644 --- a/NBlockChain/Services/Database/PersistedEntity.cs +++ b/NBlockChain/Services/Database/PersistedEntity.cs @@ -13,11 +13,13 @@ public class PersistedEntity public PersistedEntity() { + Statistics = new TStats(); } public PersistedEntity(TEntity entity) { Entity = entity; + Statistics = new TStats(); } } diff --git a/NBlockchain.sln b/NBlockchain.sln index 469a82a..968a653 100644 --- a/NBlockchain.sln +++ b/NBlockchain.sln @@ -11,8 +11,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{F160 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{A7AAFCC0-A390-4A51-9CE5-3C7ABCE95A9A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "ScratchPad\ScratchPad.csproj", "{432F4777-5194-4628-A77A-101728E6D427}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{E090655F-2F94-4839-9BCD-62B4AFA4E3F0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NBlockchain.MongoDB", "Providers\NBlockchain.MongoDB\NBlockchain.MongoDB.csproj", "{D3D3787E-5B04-4A57-8E9B-9D0AB5DBC8D9}" @@ -40,10 +38,6 @@ Global {804876E4-8D7A-4C18-B772-B716849880DA}.Debug|Any CPU.Build.0 = Debug|Any CPU {804876E4-8D7A-4C18-B772-B716849880DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {804876E4-8D7A-4C18-B772-B716849880DA}.Release|Any CPU.Build.0 = Release|Any CPU - {432F4777-5194-4628-A77A-101728E6D427}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {432F4777-5194-4628-A77A-101728E6D427}.Debug|Any CPU.Build.0 = Debug|Any CPU - {432F4777-5194-4628-A77A-101728E6D427}.Release|Any CPU.ActiveCfg = Release|Any CPU - {432F4777-5194-4628-A77A-101728E6D427}.Release|Any CPU.Build.0 = Release|Any CPU {D3D3787E-5B04-4A57-8E9B-9D0AB5DBC8D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D3D3787E-5B04-4A57-8E9B-9D0AB5DBC8D9}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3D3787E-5B04-4A57-8E9B-9D0AB5DBC8D9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -62,7 +56,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {804876E4-8D7A-4C18-B772-B716849880DA} = {F160E2A1-7F39-4223-9D88-DE86EA01E876} - {432F4777-5194-4628-A77A-101728E6D427} = {A7AAFCC0-A390-4A51-9CE5-3C7ABCE95A9A} {D3D3787E-5B04-4A57-8E9B-9D0AB5DBC8D9} = {E090655F-2F94-4839-9BCD-62B4AFA4E3F0} {23039CFC-0063-40CA-B4C3-88C60B08849D} = {A7AAFCC0-A390-4A51-9CE5-3C7ABCE95A9A} {174D35FD-A3CB-4F33-BD1D-13562AC26D33} = {A7AAFCC0-A390-4A51-9CE5-3C7ABCE95A9A} diff --git a/NBlockchain/Interfaces/IAddressEncoder.cs b/NBlockchain/Interfaces/IAddressEncoder.cs index cc3c0e3..a1e838b 100644 --- a/NBlockchain/Interfaces/IAddressEncoder.cs +++ b/NBlockchain/Interfaces/IAddressEncoder.cs @@ -3,7 +3,8 @@ public interface IAddressEncoder { string EncodeAddress(byte[] publicKey, byte type); - byte[] ExtractPublicKey(string address); + byte[] ExtractPublicKeyHash(string address); + byte[] HashPublicKey(byte[] publicKey); bool IsValidAddress(string address); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/IAsymetricCryptographyService.cs b/NBlockchain/Interfaces/IAsymetricCryptographyService.cs new file mode 100644 index 0000000..e92a8d2 --- /dev/null +++ b/NBlockchain/Interfaces/IAsymetricCryptographyService.cs @@ -0,0 +1,11 @@ +namespace NBlockchain.Interfaces +{ + public interface IAsymetricCryptographyService + { + byte[] BuildPrivateKeyFromPhrase(string phrase); + byte[] GeneratePrivateKey(); + byte[] GetPublicKey(byte[] privateKey); + byte[] Sign(byte[] data, byte[] privateKey); + bool Verify(byte[] data, byte[] sig, byte[] publicKey); + } +} \ No newline at end of file diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index 7c883bd..656af6f 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -13,5 +13,6 @@ public interface IBlockRepository Task GetNextBlock(byte[] prevBlockId); Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc); + } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/IBlockRule.cs b/NBlockchain/Interfaces/IBlockRule.cs index 5b079ab..8fde0a4 100644 --- a/NBlockchain/Interfaces/IBlockRule.cs +++ b/NBlockchain/Interfaces/IBlockRule.cs @@ -8,7 +8,7 @@ namespace NBlockchain.Interfaces { public interface IBlockRule { - bool Validate(Block block); + Task Validate(Block block); bool TailRule { get; } } } diff --git a/NBlockchain/Interfaces/IBlockVerifier.cs b/NBlockchain/Interfaces/IBlockVerifier.cs index 25053e4..58c2721 100644 --- a/NBlockchain/Interfaces/IBlockVerifier.cs +++ b/NBlockchain/Interfaces/IBlockVerifier.cs @@ -1,14 +1,15 @@ using System.Collections.Generic; +using System.Threading.Tasks; using NBlockchain.Models; namespace NBlockchain.Interfaces { public interface IBlockVerifier { - bool Verify(Block block); + Task Verify(Block block); - bool VerifyBlockRules(Block block, bool tail); + Task VerifyBlockRules(Block block, bool tail); - int VerifyTransaction(TransactionEnvelope transaction, ICollection siblings); + Task VerifyTransaction(Transaction transaction, ICollection siblings); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/IBlockbaseTransactionBuilder.cs b/NBlockchain/Interfaces/IBlockbaseTransactionBuilder.cs index 94f0f35..31042af 100644 --- a/NBlockchain/Interfaces/IBlockbaseTransactionBuilder.cs +++ b/NBlockchain/Interfaces/IBlockbaseTransactionBuilder.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; +using System.Threading.Tasks; using NBlockchain.Models; namespace NBlockchain.Interfaces { public interface IBlockbaseTransactionBuilder { - TransactionEnvelope Build(KeyPair builderKeys, ICollection transactions); + Task Build(KeyPair builderKeys, ICollection transactions); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/IInstructionRepository.cs b/NBlockchain/Interfaces/IInstructionRepository.cs new file mode 100644 index 0000000..edf3506 --- /dev/null +++ b/NBlockchain/Interfaces/IInstructionRepository.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace NBlockchain.Interfaces +{ + public interface IInstructionRepository + { + Task HaveInstruction(byte[] instructionId); + } +} diff --git a/NBlockchain/Interfaces/INodeHost.cs b/NBlockchain/Interfaces/INodeHost.cs index c1ab38b..d6b235d 100644 --- a/NBlockchain/Interfaces/INodeHost.cs +++ b/NBlockchain/Interfaces/INodeHost.cs @@ -5,6 +5,6 @@ namespace NBlockchain.Interfaces { public interface INodeHost : IBlockReceiver, ITransactionReceiver { - Task SendTransaction(TransactionEnvelope transaction); + Task SendTransaction(Transaction transaction); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/IPeerNetwork.cs b/NBlockchain/Interfaces/IPeerNetwork.cs index 181a175..f17179b 100644 --- a/NBlockchain/Interfaces/IPeerNetwork.cs +++ b/NBlockchain/Interfaces/IPeerNetwork.cs @@ -12,7 +12,7 @@ public interface IPeerNetwork void BroadcastTail(Block block); - void BroadcastTransaction(TransactionEnvelope transaction); + void BroadcastTransaction(Transaction transaction); void RequestNextBlock(byte[] blockId); diff --git a/NBlockchain/Interfaces/IPendingTransactionList.cs b/NBlockchain/Interfaces/IPendingTransactionList.cs deleted file mode 100644 index 92e1260..0000000 --- a/NBlockchain/Interfaces/IPendingTransactionList.cs +++ /dev/null @@ -1,20 +0,0 @@ -using NBlockchain.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Interfaces -{ - public interface IPendingTransactionList - { - - event EventHandler Changed; - - bool Add(TransactionEnvelope txn); - - void Remove(ICollection toRemove); - - ICollection Get { get; } - - } -} diff --git a/NBlockchain/Interfaces/ISignatureService.cs b/NBlockchain/Interfaces/ISignatureService.cs index 68fd5aa..736cffa 100644 --- a/NBlockchain/Interfaces/ISignatureService.cs +++ b/NBlockchain/Interfaces/ISignatureService.cs @@ -5,7 +5,8 @@ namespace NBlockchain.Interfaces public interface ISignatureService { KeyPair GenerateKeyPair(); - void SignTransaction(TransactionEnvelope transaction, byte[] privateKey); - bool VerifyTransaction(TransactionEnvelope transaction); + KeyPair GetKeyPairFromPhrase(string phrase); + void SignInstruction(Instruction instruction, byte[] privateKey); + bool VerifyInstruction(Instruction instruction); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/ITransactionBuilder.cs b/NBlockchain/Interfaces/ITransactionBuilder.cs new file mode 100644 index 0000000..f09c3bd --- /dev/null +++ b/NBlockchain/Interfaces/ITransactionBuilder.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using NBlockchain.Models; + +namespace NBlockchain.Interfaces +{ + public interface ITransactionBuilder + { + Task Build(ICollection instructions); + } +} \ No newline at end of file diff --git a/NBlockchain/Interfaces/ITransactionKeyResolver.cs b/NBlockchain/Interfaces/ITransactionKeyResolver.cs index 15bcdfa..ac5a204 100644 --- a/NBlockchain/Interfaces/ITransactionKeyResolver.cs +++ b/NBlockchain/Interfaces/ITransactionKeyResolver.cs @@ -1,9 +1,10 @@ -using NBlockchain.Models; +using System.Threading.Tasks; +using NBlockchain.Models; namespace NBlockchain.Interfaces { public interface ITransactionKeyResolver { - byte[] ResolveKey(TransactionEnvelope txn); + Task ResolveKey(Transaction txn); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/ITransactionReceiver.cs b/NBlockchain/Interfaces/ITransactionReceiver.cs index 1463408..baed689 100644 --- a/NBlockchain/Interfaces/ITransactionReceiver.cs +++ b/NBlockchain/Interfaces/ITransactionReceiver.cs @@ -8,6 +8,6 @@ namespace NBlockchain.Interfaces { public interface ITransactionReceiver { - Task RecieveTransaction(TransactionEnvelope transaction); + Task RecieveTransaction(Transaction transaction); } } diff --git a/NBlockchain/Interfaces/ITransactionRule.cs b/NBlockchain/Interfaces/ITransactionRule.cs index 800b312..3839cfc 100644 --- a/NBlockchain/Interfaces/ITransactionRule.cs +++ b/NBlockchain/Interfaces/ITransactionRule.cs @@ -8,7 +8,6 @@ namespace NBlockchain.Interfaces { public interface ITransactionRule { - string TransactionType { get; } - int Validate(TransactionEnvelope transaction, ICollection siblings); + int Validate(Transaction transaction, ICollection siblings); } } diff --git a/NBlockchain/Interfaces/IUnconfirmedTransactionCache.cs b/NBlockchain/Interfaces/IUnconfirmedTransactionCache.cs new file mode 100644 index 0000000..cd3b68c --- /dev/null +++ b/NBlockchain/Interfaces/IUnconfirmedTransactionCache.cs @@ -0,0 +1,20 @@ +using NBlockchain.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Interfaces +{ + public interface IUnconfirmedTransactionCache + { + + event EventHandler Changed; + + bool Add(Transaction txn); + + void Remove(ICollection toRemove); + + ICollection Get { get; } + + } +} diff --git a/NBlockchain/Models/Block.cs b/NBlockchain/Models/Block.cs index 44798f8..e013145 100644 --- a/NBlockchain/Models/Block.cs +++ b/NBlockchain/Models/Block.cs @@ -8,8 +8,8 @@ public class Block { public static byte[] HeadKey = new byte[] { 0x0 }; - public BlockHeader Header { get; set; } - public ICollection Transactions { get; set; } = new HashSet(); + public BlockHeader Header { get; set; } + public ICollection Transactions { get; set; } = new HashSet(); public MerkleNode MerkleRootNode { get; set; } public Block() diff --git a/NBlockchain/Models/BlockTransaction.cs b/NBlockchain/Models/BlockTransaction.cs deleted file mode 100644 index 9553f56..0000000 --- a/NBlockchain/Models/BlockTransaction.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Models -{ - public abstract class BlockTransaction - { - } -} diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 58372a2..ca95f91 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -68,7 +68,7 @@ public void UseBlockRepository(Func factory) public void UseTcpPeerNetwork(uint port) { - Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService())); + Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService())); } public void UseDataConnection(string connectionString) @@ -112,7 +112,7 @@ public void AddPeerDiscovery(Func facto public void AddContentThresholdBlockRule(decimal threshold) { - Services.AddTransient(typeof(IBlockRule), sp => new BlockContentThresholdRule(sp.GetService(), threshold)); + Services.AddTransient(typeof(IBlockRule), sp => new BlockContentThresholdRule(sp.GetService(), threshold)); } public void UseMulticastDiscovery(string serviceId, string multicastAddress, int port) @@ -121,19 +121,19 @@ public void UseMulticastDiscovery(string serviceId, string multicastAddress, int } public void UseTransactionRepository() - where TImplementation : TransactionRepository, TService + where TImplementation : InstructionRepository, TService where TService : class { Services.AddTransient(); } - public void AddTransactionType() + public void AddInstructionType() { - var attr = typeof(T).GetTypeInfo().GetCustomAttribute(); + var attr = typeof(T).GetTypeInfo().GetCustomAttribute(); if (attr == null) - throw new NotSupportedException("Missing TransactionTypeAttribute"); + throw new NotSupportedException("Missing InstructionTypeAttribute"); - Services.AddSingleton(new ValidTransactionType(attr.TypeId, typeof(T))); + Services.AddSingleton(new ValidInstructionType(attr.TypeIdentifier, typeof(T))); } internal void FillDefaults() @@ -145,6 +145,9 @@ internal void FillDefaults() }); AddDefault(ServiceLifetime.Transient); + + AddDefault(ServiceLifetime.Transient); + AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); @@ -157,6 +160,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); //AddDefault(ServiceLifetime.Singleton); //AddDefault(ServiceLifetime.Singleton); @@ -170,9 +174,10 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); } diff --git a/NBlockchain/Models/Instruction.cs b/NBlockchain/Models/Instruction.cs new file mode 100644 index 0000000..eaf3b9c --- /dev/null +++ b/NBlockchain/Models/Instruction.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; + +namespace NBlockchain.Models +{ + public abstract class Instruction + { + public byte[] InstructionId { get; set; } + + public byte[] OriginKey { get; set; } + + public byte[] PublicKey { get; set; } + + public byte[] Signature { get; set; } + + public abstract ICollection ExtractSignableElements(); + } +} diff --git a/NBlockchain/Models/InstructionTypeAttribute.cs b/NBlockchain/Models/InstructionTypeAttribute.cs new file mode 100644 index 0000000..9864cf8 --- /dev/null +++ b/NBlockchain/Models/InstructionTypeAttribute.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Models +{ + public class InstructionTypeAttribute : Attribute + { + public string TypeIdentifier { get; } + + public InstructionTypeAttribute(string typeIdentifier) + { + TypeIdentifier = typeIdentifier; + } + } +} diff --git a/NBlockchain/Models/Transaction.cs b/NBlockchain/Models/Transaction.cs new file mode 100644 index 0000000..1926eed --- /dev/null +++ b/NBlockchain/Models/Transaction.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +using Newtonsoft.Json.Linq; + +namespace NBlockchain.Models +{ + public class Transaction + { + public byte[] TransactionId { get; set; } + + public ICollection Instructions { get; set; } = new HashSet(); + + public Transaction() + { + } + + public Transaction(ICollection instructions) + { + Instructions = instructions; + } + } +} diff --git a/NBlockchain/Models/TransactionEnvelope.cs b/NBlockchain/Models/TransactionEnvelope.cs deleted file mode 100644 index e00eeea..0000000 --- a/NBlockchain/Models/TransactionEnvelope.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Newtonsoft.Json.Linq; - -namespace NBlockchain.Models -{ - public class TransactionEnvelope - { - public string TransactionType { get; set; } - - public Guid OriginKey{ get; set; } - - public string Originator { get; set; } - - public byte[] Signature { get; set; } - - public BlockTransaction Transaction { get; set; } - - public TransactionEnvelope() - { - } - - public TransactionEnvelope(BlockTransaction transaction) - { - Transaction = transaction; - } - - } -} diff --git a/NBlockchain/Models/TransactionTypeAttribute.cs b/NBlockchain/Models/TransactionTypeAttribute.cs deleted file mode 100644 index eab8c58..0000000 --- a/NBlockchain/Models/TransactionTypeAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Models -{ - public class TransactionTypeAttribute : Attribute - { - public string TypeId { get; } - - public TransactionTypeAttribute(string typeId) - { - TypeId = typeId; - } - } -} diff --git a/NBlockchain/Models/ValidTransactionType.cs b/NBlockchain/Models/ValidInstructionType.cs similarity index 53% rename from NBlockchain/Models/ValidTransactionType.cs rename to NBlockchain/Models/ValidInstructionType.cs index 7a8c4f6..6617baa 100644 --- a/NBlockchain/Models/ValidTransactionType.cs +++ b/NBlockchain/Models/ValidInstructionType.cs @@ -4,15 +4,15 @@ namespace NBlockchain.Models { - public class ValidTransactionType + public class ValidInstructionType { - public string TransactionType { get; private set; } + public string InstructionType { get; private set; } public Type ClassType { get; private set; } - public ValidTransactionType(string transactionType, Type classType) + public ValidInstructionType(string instructionType, Type classType) { - TransactionType = transactionType; + InstructionType = instructionType; ClassType = classType; } } diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index 44213fd..de1a0a4 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -20,6 +20,7 @@ + diff --git a/NBlockchain/Rules/BlockContentThresholdRule.cs b/NBlockchain/Rules/BlockContentThresholdRule.cs index 2be9308..17c0409 100644 --- a/NBlockchain/Rules/BlockContentThresholdRule.cs +++ b/NBlockchain/Rules/BlockContentThresholdRule.cs @@ -2,32 +2,33 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using NBlockchain.Models; namespace NBlockchain.Rules { public class BlockContentThresholdRule : IBlockRule { - private readonly IPendingTransactionList _pendingTransactions; + private readonly IUnconfirmedTransactionCache _unconfirmedTransactions; private readonly decimal _threshold; public bool TailRule => true; - public BlockContentThresholdRule(IPendingTransactionList pendingTransactions, decimal threshold) + public BlockContentThresholdRule(IUnconfirmedTransactionCache unconfirmedTransactions, decimal threshold) { - _pendingTransactions = pendingTransactions; + _unconfirmedTransactions = unconfirmedTransactions; _threshold = threshold; } - public bool Validate(Block block) + public Task Validate(Block block) { - var expected = _pendingTransactions.Get; + var expected = _unconfirmedTransactions.Get; if (expected.Count == 0) - return true; + return Task.FromResult(true); - var count = expected.Count(txn => block.Transactions.Any(actual => actual.OriginKey == txn.OriginKey)); + var count = expected.Count(txn => block.Transactions.Any(actual => actual.TransactionId.SequenceEqual(txn.TransactionId))); var ratio = (decimal)count / (decimal)expected.Count; - return (ratio >= _threshold); + return Task.FromResult(ratio >= _threshold); } } diff --git a/NBlockchain/Rules/TransactionRule.cs b/NBlockchain/Rules/TransactionRule.cs deleted file mode 100644 index d2f0806..0000000 --- a/NBlockchain/Rules/TransactionRule.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using NBlockchain.Interfaces; -using NBlockchain.Models; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace NBlockchain.Rules -{ - public abstract class TransactionRule : ITransactionRule - where T : class - { - public string TransactionType { get; } - - protected TransactionRule() - { - var attr = typeof(T).GetTypeInfo().GetCustomAttribute(); - TransactionType = attr.TypeId; - } - - public int Validate(TransactionEnvelope transaction, ICollection siblings) - { - if (!(transaction is T)) - return -5; - - return Validate(transaction, transaction.Transaction as T, siblings); - } - - protected abstract int Validate(TransactionEnvelope envelope, T transaction, ICollection siblings); - } -} diff --git a/NBlockchain/Services/AddressEncoder.cs b/NBlockchain/Services/AddressEncoder.cs index 028e05a..7ddb652 100644 --- a/NBlockchain/Services/AddressEncoder.cs +++ b/NBlockchain/Services/AddressEncoder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; using NBlockchain.Interfaces; using Wiry.Base32; @@ -8,6 +9,7 @@ namespace NBlockchain.Services { public class AddressEncoder : IAddressEncoder { + private const int ChecksumLength = 4; public AddressEncoder() { } @@ -15,40 +17,38 @@ public AddressEncoder() public string EncodeAddress(byte[] publicKey, byte type) { var result = new List {type}; - result.AddRange(publicKey); - result.Add(CalculateCheckSum(result)); + result.AddRange(HashPublicKey(publicKey)); + result.AddRange(CalculateCheckSum(result.ToArray())); return Base32Encoding.ZBase32.GetString(result.ToArray()); } - public byte[] ExtractPublicKey(string address) + public byte[] ExtractPublicKeyHash(string address) { var raw = Base32Encoding.ZBase32.ToBytes(address); - return raw.Skip(1).Take(raw.Length - 2).ToArray(); + return raw.Skip(1).Take(raw.Length - (1 + ChecksumLength)).ToArray(); } + public byte[] HashPublicKey(byte[] publicKey) + { + using (var hasher = SHA1.Create()) + { + return hasher.ComputeHash(publicKey); + } + } + public bool IsValidAddress(string address) { var raw = Base32Encoding.ZBase32.ToBytes(address); - return (raw.Last() == CalculateCheckSum(raw.Take(raw.Length - 1))); + return (raw.Skip(raw.Count() - ChecksumLength).SequenceEqual(CalculateCheckSum(raw.Take(raw.Count() - ChecksumLength).ToArray()))); } - private byte CalculateCheckSum(IEnumerable data) + private static byte[] CalculateCheckSum(byte[] data) { - byte result = 0; - var odd = true; - foreach (var item in data) + using (var hasher = SHA256.Create()) { - if (odd) - result += item; - else - result *= item; - - result++; - odd = !odd; + return hasher.ComputeHash(data).Take(ChecksumLength).ToArray(); } - - return result; } } } diff --git a/NBlockchain/Services/AsymetricCryptographyService.cs b/NBlockchain/Services/AsymetricCryptographyService.cs new file mode 100644 index 0000000..9d68e2f --- /dev/null +++ b/NBlockchain/Services/AsymetricCryptographyService.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text; +using NBlockchain.Interfaces; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; + +namespace NBlockchain.Services +{ + public class AsymetricCryptographyService : IAsymetricCryptographyService + { + private readonly DerObjectIdentifier _curveId = SecObjectIdentifiers.SecP256r1; + + public byte[] GeneratePrivateKey() + { + return SecureRandom.GetNextBytes(SecureRandom.GetInstance("SHA256PRNG"), 32); + } + + public byte[] BuildPrivateKeyFromPhrase(string phrase) + { + using (var hasher = System.Security.Cryptography.SHA256.Create()) + { + var privateKey = hasher.ComputeHash(Encoding.Unicode.GetBytes(phrase)); + return privateKey; + } + } + + public byte[] GetPublicKey(byte[] privateKey) + { + var privKeyInt = new BigInteger(privateKey); + var parameters = NistNamedCurves.GetByOid(_curveId); + var qa = parameters.G.Multiply(privKeyInt); + + return qa.GetEncoded(); + } + + public byte[] Sign(byte[] data, byte[] privateKey) + { + var cp = GetPrivateKeyParameters(privateKey); + var signer = SignerUtilities.GetSigner("ECDSA"); + signer.Init(true, cp); + signer.BlockUpdate(data, 0, data.Length); + return signer.GenerateSignature(); + } + + public bool Verify(byte[] data, byte[] sig, byte[] publicKey) + { + var cp = GetPublicKeyParams(publicKey); + var signer = SignerUtilities.GetSigner("ECDSA"); + signer.Init(false, cp); + signer.BlockUpdate(data, 0, data.Length); + return signer.VerifySignature(sig); + } + + private ECPrivateKeyParameters GetPrivateKeyParameters(byte[] privateKey) + { + var parameters = NistNamedCurves.GetByOid(_curveId); + var privKeyInt = new BigInteger(privateKey); + var dp = new ECDomainParameters(parameters.Curve, parameters.G, parameters.N); + return new ECPrivateKeyParameters("ECDSA", privKeyInt, dp); + } + + private ECPublicKeyParameters GetPublicKeyParams(byte[] publicKey) + { + var parameters = NistNamedCurves.GetByOid(_curveId); + var dp = new ECDomainParameters(parameters.Curve, parameters.G, parameters.N); + var q = parameters.Curve.DecodePoint(publicKey); + return new ECPublicKeyParameters("ECDSA", q, dp); + } + } +} diff --git a/NBlockchain/Services/BlockMiner.cs b/NBlockchain/Services/BlockMiner.cs index 3df7a47..54e7660 100644 --- a/NBlockchain/Services/BlockMiner.cs +++ b/NBlockchain/Services/BlockMiner.cs @@ -22,7 +22,7 @@ public class BlockMiner : IBlockMiner private readonly IBlockNotary _blockNotary; private readonly ILogger _logger; private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); - private readonly IPendingTransactionList _pendingTransactionList; + private readonly IUnconfirmedTransactionCache _unconfirmedTransactionCache; private readonly IBlockRepository _blockRepository; private readonly IPeerNetwork _peerNetwork; private readonly IBlockReceiver _blockReciever; @@ -36,7 +36,7 @@ public class BlockMiner : IBlockMiner private CancellationTokenSource _blockCancelToken; - public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IPendingTransactionList pendingTransactionList, IBlockRepository blockRepository, IBlockReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) + public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IUnconfirmedTransactionCache unconfirmedTransactionCache, IBlockRepository blockRepository, IBlockReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) { _networkParameters = networkParameters; _peerNetwork= peerNetwork; @@ -48,8 +48,8 @@ public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBui _merkleTreeBuilder = merkleTreeBuilder; _logger = loggerFactory.CreateLogger(); _blockRepository = blockRepository; - _pendingTransactionList = pendingTransactionList; - _pendingTransactionList.Changed += PendingTransactionList_Changed; + _unconfirmedTransactionCache = unconfirmedTransactionCache; + _unconfirmedTransactionCache.Changed += UnconfirmedTransactionCacheChanged; } @@ -96,7 +96,7 @@ private async void Mine() } } - private void PendingTransactionList_Changed(object sender, EventArgs e) + private void UnconfirmedTransactionCacheChanged(object sender, EventArgs e) { _resetEvent.WaitOne(); try @@ -114,10 +114,9 @@ private void PendingTransactionList_Changed(object sender, EventArgs e) private async Task AssembleBlock(byte[] prevBlock, uint height, uint difficulty, CancellationToken cancellationToken) { - var targetTxns = _pendingTransactionList.Get; - targetTxns.Add(_blockbaseBuilder.Build(_builderKeys, targetTxns)); - var hashDict = HashTransactions(targetTxns, cancellationToken); - var merkleRoot = await _merkleTreeBuilder.BuildTree(hashDict.Keys); + var targetTxns = _unconfirmedTransactionCache.Get; + targetTxns.Add(await _blockbaseBuilder.Build(_builderKeys, targetTxns)); + var merkleRoot = await _merkleTreeBuilder.BuildTree(targetTxns.Select(x => x.TransactionId).ToList()); if (cancellationToken.IsCancellationRequested) { @@ -153,21 +152,5 @@ private async Task AssembleBlock(byte[] prevBlock, uint height, uint diff return result; } - - - - private IDictionary HashTransactions(ICollection transactions, CancellationToken cancellationToken) - { - var result = new ConcurrentDictionary(); - - Parallel.ForEach(transactions, txn => - { - var key = _transactionKeyResolver.ResolveKey(txn); - result[key] = txn; - }); - - return result; - } - } } diff --git a/NBlockchain/Services/BlockVerifier.cs b/NBlockchain/Services/BlockVerifier.cs index 76d2abe..b4587ac 100644 --- a/NBlockchain/Services/BlockVerifier.cs +++ b/NBlockchain/Services/BlockVerifier.cs @@ -14,30 +14,27 @@ public class BlockVerifier : IBlockVerifier private readonly INetworkParameters _parameters; private readonly IEnumerable _txnRules; private readonly IEnumerable _blockRules; - private readonly IEnumerable _validTxnTypes; - private readonly IAddressEncoder _addressEncoder; + private readonly IInstructionRepository _instructionRepository; private readonly ISignatureService _signatureService; private readonly IHashTester _hashTester; private readonly IHasher _hasher; private readonly IMerkleTreeBuilder _merkleTreeBuilder; private readonly ITransactionKeyResolver _transactionKeyResolver; - private readonly IEqualityComparer _byteArrayEqualityComparer = new ByteArrayEqualityComparer(); - public BlockVerifier(INetworkParameters parameters, IAddressEncoder addressEncoder, ISignatureService signatureService, IEnumerable txnRules, IEnumerable blockRules, IEnumerable validTxnTypes, IMerkleTreeBuilder merkleTreeBuilder, ITransactionKeyResolver transactionKeyResolver, IHashTester hashTester, IHasher hasher) + public BlockVerifier(INetworkParameters parameters, ISignatureService signatureService, IEnumerable txnRules, IEnumerable blockRules, IEnumerable validTxnTypes, IMerkleTreeBuilder merkleTreeBuilder, ITransactionKeyResolver transactionKeyResolver, IHashTester hashTester, IHasher hasher, IInstructionRepository instructionRepository) { _parameters = parameters; - _addressEncoder = addressEncoder; _signatureService = signatureService; _txnRules = txnRules; _blockRules = blockRules; - _validTxnTypes = validTxnTypes; _merkleTreeBuilder = merkleTreeBuilder; _transactionKeyResolver = transactionKeyResolver; _hashTester = hashTester; _hasher = hasher; + _instructionRepository = instructionRepository; } - public bool Verify(Block block) + public async Task Verify(Block block) { if (!_hashTester.TestHash(block.Header.BlockId, block.Header.Difficulty)) return false; @@ -47,9 +44,8 @@ public bool Verify(Block block) if (!hash.SequenceEqual(block.Header.BlockId)) return false; - - var hashDict = HashTransactions(block.Transactions); - var merkleRoot = _merkleTreeBuilder.BuildTree(hashDict.Keys).Result; + + var merkleRoot = await _merkleTreeBuilder.BuildTree(block.Transactions.Select(x => x.TransactionId).ToList()); if (!merkleRoot.Value.SequenceEqual(block.Header.MerkelRoot)) return false; @@ -57,53 +53,47 @@ public bool Verify(Block block) foreach (var txn in block.Transactions) { var siblings = block.Transactions.Where(x => x != txn).ToList(); - if (VerifyTransaction(txn, siblings) != 0) + if (await VerifyTransaction(txn, siblings) != 0) return false; } return true; } - public bool VerifyBlockRules(Block block, bool tail) + public async Task VerifyBlockRules(Block block, bool tail) { foreach (var rule in _blockRules.Where(x => x.TailRule == tail || tail)) { - if (!rule.Validate(block)) + if (! await rule.Validate(block)) return false; } return true; } - public int VerifyTransaction(TransactionEnvelope transaction, ICollection siblings) + public async Task VerifyTransaction(Transaction transaction, ICollection siblings) { - var result = 0; + foreach (var instruction in transaction.Instructions) + if (!await VerifyInstruction(instruction, siblings)) + return -1; - if (!_addressEncoder.IsValidAddress(transaction.Originator)) - return -1; + var expectedId = await _transactionKeyResolver.ResolveKey(transaction); - if (_validTxnTypes.All(x => x.TransactionType != transaction.TransactionType)) + if (!expectedId.SequenceEqual(transaction.TransactionId)) return -2; - if (!_signatureService.VerifyTransaction(transaction)) - return -3; + return _txnRules.Aggregate(0, (current, validator) => current & validator.Validate(transaction, siblings)); + } - foreach (var validator in _txnRules.Where(v => v.TransactionType == transaction.TransactionType)) - result = result & validator.Validate(transaction, siblings); - return result; - } - - private IDictionary HashTransactions(ICollection transactions) + private async Task VerifyInstruction(Instruction instruction, ICollection siblings) { - var result = new ConcurrentDictionary(); + if (siblings.Any(x => x.Instructions.Any(y => y.InstructionId.SequenceEqual(instruction.InstructionId)))) + return false; - Parallel.ForEach(transactions, txn => - { - var key = _transactionKeyResolver.ResolveKey(txn); - result[key] = txn; - }); + if (!_signatureService.VerifyInstruction(instruction)) + return false; - return result; + return (!await _instructionRepository.HaveInstruction(instruction.InstructionId)); } } } diff --git a/NBlockchain/Services/BlockbaseTransactionBuilder.cs b/NBlockchain/Services/BlockbaseTransactionBuilder.cs index 5824e67..72668d5 100644 --- a/NBlockchain/Services/BlockbaseTransactionBuilder.cs +++ b/NBlockchain/Services/BlockbaseTransactionBuilder.cs @@ -2,43 +2,34 @@ using System.Collections.Generic; using System.Reflection; using System.Text; +using System.Threading.Tasks; using NBlockchain.Interfaces; using NBlockchain.Models; using Newtonsoft.Json.Linq; namespace NBlockchain.Services { - public abstract class BlockbaseTransactionBuilder : IBlockbaseTransactionBuilder - where T : BlockTransaction + public abstract class BlockbaseTransactionBuilder : IBlockbaseTransactionBuilder { + protected readonly IAddressEncoder AddressEncoder; + protected readonly ISignatureService SignatureService; + protected readonly ITransactionBuilder TransactionBuilder; - private readonly IAddressEncoder _addressEncoder; - private readonly ISignatureService _signatureService; - private readonly TransactionTypeAttribute _transactionTypeMetadata; - - protected BlockbaseTransactionBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService) + protected BlockbaseTransactionBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService, ITransactionBuilder transactionBuilder) { - _addressEncoder = addressEncoder; - _signatureService = signatureService; - var typeInfo = typeof(T).GetTypeInfo(); - _transactionTypeMetadata = typeInfo.GetCustomAttribute(); + AddressEncoder = addressEncoder; + SignatureService = signatureService; + TransactionBuilder = transactionBuilder; } - public TransactionEnvelope Build(KeyPair builderKeys, ICollection transactions) + public async Task Build(KeyPair builderKeys, ICollection transactions) { - var result = new TransactionEnvelope(); - - result.Originator = _addressEncoder.EncodeAddress(builderKeys.PublicKey, 0); - result.OriginKey = Guid.NewGuid(); - result.TransactionType = _transactionTypeMetadata.TypeId; - result.Transaction = BuildBaseTransaction(transactions); - - _signatureService.SignTransaction(result, builderKeys.PrivateKey); - - return result; + var instructions = BuildInstructions(builderKeys, transactions); + return await TransactionBuilder.Build(instructions); } - protected abstract T BuildBaseTransaction(ICollection transactions); + protected abstract ICollection BuildInstructions(KeyPair builderKeys, ICollection transactions); + } } diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 418a270..57259f0 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -15,20 +15,25 @@ public class DefaultBlockRepository : IBlockRepository { private readonly ILogger _logger; private readonly IDataConnection _connection; + private readonly IAddressEncoder _addressEncoder; protected LiteCollection Blocks => _connection.Database.GetCollection("Blocks"); - protected LiteCollection Txns => _connection.Database.GetCollection("Txns"); + protected LiteCollection Instructions => _connection.Database.GetCollection("Instructions"); - public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection connection) + public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection connection, IAddressEncoder addressEncoder) { _connection = connection; + _addressEncoder = addressEncoder; _logger = loggerFactory.CreateLogger(); Blocks.EnsureIndex(x => x.Entity.Header.BlockId); Blocks.EnsureIndex(x => x.Entity.Header.PreviousBlock); Blocks.EnsureIndex(x => x.Entity.Header.Height); - Txns.EnsureIndex(x => x.BlockId); - Txns.EnsureIndex(x => x.Entity.Originator); + Instructions.EnsureIndex(x => x.BlockId); + Instructions.EnsureIndex(x => x.TransactionId); + Instructions.EnsureIndex(x => x.Entity.InstructionId); + Instructions.EnsureIndex(x => x.Entity.PublicKey); + Instructions.EnsureIndex(x => x.Statistics.PublicKeyHash); } public Task AddBlock(Block block) @@ -44,10 +49,11 @@ public Task AddBlock(Block block) Blocks.Insert(persisted); - - var pt = block.Transactions.Select(txn => new PersistedTransaction(block.Header.BlockId, txn)).ToList(); - - Txns.InsertBulk(pt); + foreach (var txn in block.Transactions) + { + var pt = txn.Instructions.Select(ins => new PersistedInstruction(block.Header.BlockId, txn.TransactionId, ins, _addressEncoder.HashPublicKey(ins.PublicKey))).ToList(); + Instructions.InsertBulk(pt); + } return Task.CompletedTask; } @@ -71,7 +77,7 @@ public async Task GetNewestBlockHeader() var max = Blocks.Max(x => x.Entity.Header.Height).AsInt64; var block = Blocks.Find(Query.EQ("Entity.Header.Height", max)).First(); - return await Task.FromResult(block?.Entity.Header); + return block?.Entity.Header; } public Task GetNextBlock(byte[] prevBlockId) @@ -85,8 +91,11 @@ public Task GetNextBlock(byte[] prevBlockId) result.Header = blockHeader.Entity.Header; result.MerkleRootNode = blockHeader.Entity.MerkleRootNode; - var txns = Txns.Find(Query.EQ("BlockId", blockHeader.Entity.Header.BlockId)); - result.Transactions = txns.Select(x => x.Entity).ToList(); + var instructions = Instructions.Find(Query.EQ("BlockId", blockHeader.Entity.Header.BlockId)); + result.Transactions = instructions + .GroupBy(x => x.TransactionId, new ByteArrayEqualityComparer()) + .Select(x => new Transaction(x.Select(y => y.Entity).ToList()) { TransactionId = x.Key }) + .ToList(); return Task.FromResult(result); } diff --git a/NBlockchain/Services/Database/TransactionRepository.cs b/NBlockchain/Services/Database/InstructionRepository.cs similarity index 50% rename from NBlockchain/Services/Database/TransactionRepository.cs rename to NBlockchain/Services/Database/InstructionRepository.cs index 165f9e6..9749bb1 100644 --- a/NBlockchain/Services/Database/TransactionRepository.cs +++ b/NBlockchain/Services/Database/InstructionRepository.cs @@ -11,18 +11,23 @@ namespace NBlockchain.Services.Database { - public abstract class TransactionRepository + public class InstructionRepository : IInstructionRepository { protected readonly ILogger Logger; protected readonly IDataConnection Connection; protected LiteCollection Blocks => Connection.Database.GetCollection("Blocks"); - protected LiteCollection Transactions => Connection.Database.GetCollection("Txns"); + protected LiteCollection Instructions => Connection.Database.GetCollection("Instructions"); - protected TransactionRepository(ILoggerFactory loggerFactory, IDataConnection connection) + public InstructionRepository(ILoggerFactory loggerFactory, IDataConnection connection) { Connection = connection; - Logger = loggerFactory.CreateLogger(); + Logger = loggerFactory.CreateLogger(); + } + + public Task HaveInstruction(byte[] instructionId) + { + return Task.FromResult(Instructions.Exists(x => x.Entity.InstructionId == instructionId)); } } } \ No newline at end of file diff --git a/NBlockchain/Services/Database/InstructionStatistics.cs b/NBlockchain/Services/Database/InstructionStatistics.cs new file mode 100644 index 0000000..84ab114 --- /dev/null +++ b/NBlockchain/Services/Database/InstructionStatistics.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Database +{ + public class InstructionStatistics + { + public byte[] PublicKeyHash { get; set; } + } +} diff --git a/NBlockchain/Services/Database/PersistedInstruction.cs b/NBlockchain/Services/Database/PersistedInstruction.cs new file mode 100644 index 0000000..f74361c --- /dev/null +++ b/NBlockchain/Services/Database/PersistedInstruction.cs @@ -0,0 +1,27 @@ +using LiteDB; +using NBlockchain.Models; + +namespace NBlockchain.Services.Database +{ + public class PersistedInstruction : PersistedEntity + { + public byte[] BlockId { get; set; } + + public byte[] TransactionId { get; set; } + + public PersistedInstruction() + { + } + + public PersistedInstruction(byte[] blockId, byte[] transactionId, Instruction instruction, byte[] publicKeyHash) + { + Entity = instruction; + BlockId = blockId; + TransactionId = transactionId; + Statistics = new InstructionStatistics() + { + PublicKeyHash = publicKeyHash + }; + } + } +} \ No newline at end of file diff --git a/NBlockchain/Services/DefaultSignatureService.cs b/NBlockchain/Services/DefaultSignatureService.cs index c7ca735..b99bedd 100644 --- a/NBlockchain/Services/DefaultSignatureService.cs +++ b/NBlockchain/Services/DefaultSignatureService.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Security.Cryptography; using System.Linq; +using System.Security.Cryptography; using System.Text; using NBlockchain.Interfaces; using NBlockchain.Models; @@ -12,88 +12,73 @@ namespace NBlockchain.Services public class DefaultSignatureService : ISignatureService { private readonly IAddressEncoder _addressEncoder; - private readonly ITransactionKeyResolver _transactionKeyResolver; - private readonly ECCurve _curve = ECCurve.NamedCurves.nistP256; - private readonly HashAlgorithmName _hashAlgorithm = HashAlgorithmName.SHA512; + private readonly IAsymetricCryptographyService _asymetricCryptography; - public DefaultSignatureService(IAddressEncoder addressEncoder, ITransactionKeyResolver transactionKeyResolver) + public DefaultSignatureService(IAddressEncoder addressEncoder, IAsymetricCryptographyService asymetricCryptography) { _addressEncoder = addressEncoder; - _transactionKeyResolver = transactionKeyResolver; + _asymetricCryptography = asymetricCryptography; } public KeyPair GenerateKeyPair() { - using (var dsa = ECDsa.Create(_curve)) + var privateKey = _asymetricCryptography.GeneratePrivateKey(); + var publicKey = _asymetricCryptography.GetPublicKey(privateKey); + + return new KeyPair() { - var parameters = dsa.ExportParameters(true); - var publicKey = parameters.Q.X.Concat(parameters.Q.Y).ToArray(); - return new KeyPair() - { - PrivateKey = parameters.D.Concat(publicKey).ToArray(), - PublicKey = publicKey - }; - } + PrivateKey = privateKey, + PublicKey = publicKey + }; } - public void SignTransaction(TransactionEnvelope transaction, byte[] privateKey) + public KeyPair GetKeyPairFromPhrase(string phrase) { - using (var dsa = ECDsa.Create(_curve)) - { - dsa.ImportParameters(new ECParameters() - { - Curve = _curve, - D = privateKey.Take(privateKey.Length / 3).ToArray(), - Q = new ECPoint() - { - X = privateKey.Skip(privateKey.Length / 3).Take(privateKey.Length / 3).ToArray(), - Y = privateKey.Skip((privateKey.Length / 3) * 2).Take(privateKey.Length / 3).ToArray() - } - }); + var privateKey = _asymetricCryptography.BuildPrivateKeyFromPhrase(phrase); + var publicKey = _asymetricCryptography.GetPublicKey(privateKey); - var data = ExtractSignableElements(transaction); + return new KeyPair() + { + PrivateKey = privateKey, + PublicKey = publicKey + }; + } - transaction.Signature = dsa.SignData(data, _hashAlgorithm); - } + public void SignInstruction(Instruction instruction, byte[] privateKey) + { + instruction.OriginKey = GenerateOriginKey(); + instruction.InstructionId = ResolveInstructionId(instruction); + instruction.Signature = _asymetricCryptography.Sign(instruction.InstructionId, privateKey); } - public bool VerifyTransaction(TransactionEnvelope transaction) + public bool VerifyInstruction(Instruction instruction) { - if (transaction.Signature == null) + if (instruction.Signature == null) return false; - if (!_addressEncoder.IsValidAddress(transaction.Originator)) + if (!instruction.InstructionId.SequenceEqual(ResolveInstructionId(instruction))) return false; - var pubKey = _addressEncoder.ExtractPublicKey(transaction.Originator); - - using (var dsa = ECDsa.Create(_curve)) + return _asymetricCryptography.Verify(instruction.InstructionId, instruction.Signature, instruction.PublicKey); + } + + private static byte[] ResolveInstructionId(Instruction instruction) + { + using (var h = SHA256.Create()) { - dsa.ImportParameters(new ECParameters() - { - Curve = _curve, - Q = new ECPoint() - { - X = pubKey.Take(pubKey.Length / 2).ToArray(), - Y = pubKey.Skip(pubKey.Length / 2).Take(pubKey.Length / 2).ToArray() - } - }); + var items = instruction.ExtractSignableElements(); + items.Add(instruction.OriginKey); + items.Add(instruction.PublicKey); - var data = ExtractSignableElements(transaction); + var data = items.SelectMany(x => x).ToArray(); - return dsa.VerifyData(data, transaction.Signature, _hashAlgorithm); + return h.ComputeHash(data); } } - private byte[] ExtractSignableElements(TransactionEnvelope txn) + private static byte[] GenerateOriginKey() { - var txnStr = JsonConvert.SerializeObject(txn.Transaction, Formatting.None); - - var result = txn.OriginKey.ToByteArray() - .Concat(Encoding.Unicode.GetBytes(txn.Originator)) - .Concat(Encoding.Unicode.GetBytes(txnStr)); - - return result.ToArray(); + return Guid.NewGuid().ToByteArray(); } } diff --git a/NBlockchain/Services/Net/InProcessPeerNetwork.cs b/NBlockchain/Services/Net/InProcessPeerNetwork.cs index e8d8716..8369f13 100644 --- a/NBlockchain/Services/Net/InProcessPeerNetwork.cs +++ b/NBlockchain/Services/Net/InProcessPeerNetwork.cs @@ -59,7 +59,7 @@ public void Close() _blockReciever.RecieveTail(block); }; - public Action ReceiveTransaction => (peer, txn) => + public Action ReceiveTransaction => (peer, txn) => { _transactionReciever.RecieveTransaction(txn); }; @@ -82,7 +82,7 @@ public void BroadcastTail(Block block) }); } - public void BroadcastTransaction(TransactionEnvelope transaction) + public void BroadcastTransaction(Transaction transaction) { Parallel.ForEach(Peers.Where(x => x.NodeId != NodeId), peer => { diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index 462f981..e60dbd1 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -124,7 +124,7 @@ public void Disconnect() _resetEvent.WaitOne(); try { - _client.Client.Shutdown(SocketShutdown.Both); + _client?.Client?.Shutdown(SocketShutdown.Both); } catch (Exception ex) { @@ -144,7 +144,7 @@ public void Close() { _cancelToken.Cancel(); SpinWait.SpinUntil(() => _pollExited); - _client?.GetStream().Dispose(); + //_client?.GetStream().Dispose(); _client?.Dispose(); } catch (Exception ex) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index c1f632f..dfa38fe 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -31,7 +31,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly IEnumerable _discoveryServices; private readonly ILogger _logger; private readonly IOwnAddressResolver _ownAddressResolver; - private readonly IPendingTransactionList _pendingTransactionList; + private readonly IUnconfirmedTransactionCache _unconfirmedTransactionCache; private readonly ConcurrentQueue _peerRoundRobin = new ConcurrentQueue(); @@ -52,14 +52,14 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable public Guid NodeId { get; private set; } - public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IPendingTransactionList pendingTransactionList) + public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IUnconfirmedTransactionCache unconfirmedTransactionCache) { _port = port; _logger = loggerFactory.CreateLogger(); _blockRepository = blockRepository; _discoveryServices = discoveryServices; _ownAddressResolver = ownAddressResolver; - _pendingTransactionList = pendingTransactionList; + _unconfirmedTransactionCache = unconfirmedTransactionCache; NodeId = Guid.NewGuid(); } @@ -269,7 +269,7 @@ private async Task ProcessBlock(byte[] data, Guid originId, bool tail) private async Task ProcessTransaction(byte[] data, Guid originId) { - var txn = DeserializeObject(data); + var txn = DeserializeObject(data); var result = await _transactionReciever.RecieveTransaction(txn); if (result == PeerDataResult.Relay) @@ -287,7 +287,7 @@ private async Task ProcessTransaction(byte[] data, Guid originId) private void ProcessTxnRequest(PeerConnection peer) { - var txns = _pendingTransactionList.Get; + var txns = _unconfirmedTransactionCache.Get; foreach (var txn in txns) { @@ -463,7 +463,7 @@ private void SendBlock(PeerConnection peer, byte[] data, bool tail) } } - public void BroadcastTransaction(TransactionEnvelope transaction) + public void BroadcastTransaction(Transaction transaction) { var data = SerializeObject(transaction); diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 4f82fb1..5fcc6f0 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -22,20 +22,20 @@ public class NodeHost : INodeHost private readonly IExpectedBlockList _expectedBlockList; private readonly IPeerNetwork _peerNetwork; private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); - private readonly IPendingTransactionList _pendingTransactionList; + private readonly IUnconfirmedTransactionCache _unconfirmedTransactionCache; private readonly IDifficultyCalculator _difficultyCalculator; private readonly Timer _pollTimer; - public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, INetworkParameters parameters, IDateTimeProvider dateTimeProvider, IPendingTransactionList pendingTransactionList, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) + public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, INetworkParameters parameters, IDateTimeProvider dateTimeProvider, IUnconfirmedTransactionCache unconfirmedTransactionCache, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) { _blockRepository = blockRepository; _blockVerifier = blockVerifier; _serviceProvider = serviceProvider; _parameters = parameters; _dateTimeProvider = dateTimeProvider; - _pendingTransactionList = pendingTransactionList; + _unconfirmedTransactionCache = unconfirmedTransactionCache; _peerNetwork = peerNetwork; _difficultyCalculator = difficultyCalculator; _expectedBlockList = expectedBlockList; @@ -59,7 +59,6 @@ public async Task RecieveTail(Block block) try { _logger.LogDebug($"Recv tail {BitConverter.ToString(block.Header.BlockId)}"); - _expectedBlockList.Confirm(block.Header.PreviousBlock); if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; @@ -85,20 +84,22 @@ public async Task RecieveTail(Block block) return PeerDataResult.Ignore; } - if (!_blockVerifier.Verify(block)) + if (!await _blockVerifier.Verify(block)) { _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Demerit; } - if (!_blockVerifier.VerifyBlockRules(block, true)) + if (!await _blockVerifier.VerifyBlockRules(block, true)) { _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Ignore; } await _blockRepository.AddBlock(block); - _pendingTransactionList.Remove(block.Transactions); + _unconfirmedTransactionCache.Remove(block.Transactions); + _expectedBlockList.Confirm(block.Header.PreviousBlock); + _expectedBlockList.ExpectNext(block.Header.BlockId); _logger.LogDebug($"Accepted tail {BitConverter.ToString(block.Header.BlockId)}"); @@ -122,7 +123,7 @@ public async Task RecieveBlock(Block block) try { _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)}"); - _expectedBlockList.Confirm(block.Header.PreviousBlock); + if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; @@ -132,19 +133,20 @@ public async Task RecieveBlock(Block block) return PeerDataResult.Ignore; } - if (!_blockVerifier.Verify(block)) + if (!await _blockVerifier.Verify(block)) { _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Demerit; } - if (!_blockVerifier.VerifyBlockRules(block, false)) + if (!await _blockVerifier.VerifyBlockRules(block, false)) { _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Ignore; } await _blockRepository.AddBlock(block); + _expectedBlockList.Confirm(block.Header.PreviousBlock); _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); } @@ -157,28 +159,27 @@ public async Task RecieveBlock(Block block) return PeerDataResult.Ignore; } - public Task RecieveTransaction(TransactionEnvelope transaction) + public async Task RecieveTransaction(Transaction transaction) { - _logger.LogDebug($"Recv txn {transaction.OriginKey} from {transaction.Originator}"); - var txnResult = _blockVerifier.VerifyTransaction(transaction, _pendingTransactionList.Get); + _logger.LogDebug($"Recv txn {BitConverter.ToString(transaction.TransactionId)}"); + var txnResult = await _blockVerifier.VerifyTransaction(transaction, _unconfirmedTransactionCache.Get); if (txnResult == 0) { - //var txnKey = _transactionKeyResolver.ResolveKey(transaction); - if (_pendingTransactionList.Add(transaction)) + if (_unconfirmedTransactionCache.Add(transaction)) { - _logger.LogDebug($"Accepted txn {transaction.OriginKey} from {transaction.Originator}"); - return Task.FromResult(PeerDataResult.Relay); + _logger.LogDebug($"Accepted txn {BitConverter.ToString(transaction.TransactionId)}"); + return PeerDataResult.Relay; } - return Task.FromResult(PeerDataResult.Ignore); + return PeerDataResult.Ignore; } - _logger.LogDebug($"Rejected txn {transaction.OriginKey} from {transaction.Originator} code: {txnResult}"); - return Task.FromResult(PeerDataResult.Ignore); + _logger.LogDebug($"Rejected txn {BitConverter.ToString(transaction.TransactionId)} code: {txnResult}"); + return PeerDataResult.Ignore; } - public async Task SendTransaction(TransactionEnvelope transaction) + public async Task SendTransaction(Transaction transaction) { _logger.LogDebug("Sending txn"); await RecieveTransaction(transaction); diff --git a/NBlockchain/Services/TransactionBuilder.cs b/NBlockchain/Services/TransactionBuilder.cs new file mode 100644 index 0000000..4beaa59 --- /dev/null +++ b/NBlockchain/Services/TransactionBuilder.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; +using NBlockchain.Interfaces; +using NBlockchain.Models; +using System.Linq; +using System.Threading.Tasks; + +namespace NBlockchain.Services +{ + public class TransactionBuilder : ITransactionBuilder + { + private readonly ITransactionKeyResolver _keyResolver; + + public TransactionBuilder(ITransactionKeyResolver keyResolver) + { + _keyResolver = keyResolver; + } + + public async Task Build(ICollection instructions) + { + var result = new Transaction(instructions); + result.TransactionId = await _keyResolver.ResolveKey(result); + return result; + } + } +} diff --git a/NBlockchain/Services/TransactionKeyResolver.cs b/NBlockchain/Services/TransactionKeyResolver.cs index fa60ef8..75fc7cc 100644 --- a/NBlockchain/Services/TransactionKeyResolver.cs +++ b/NBlockchain/Services/TransactionKeyResolver.cs @@ -13,21 +13,18 @@ namespace NBlockchain.Services public class TransactionKeyResolver : ITransactionKeyResolver { private readonly IHasher _hasher; + private readonly IMerkleTreeBuilder _merkleTreeBuilder; - public TransactionKeyResolver(IHasher hasher) + public TransactionKeyResolver(IHasher hasher, IMerkleTreeBuilder merkleTreeBuilder) { _hasher = hasher; + _merkleTreeBuilder = merkleTreeBuilder; } - public byte[] ResolveKey(TransactionEnvelope txn) + public async Task ResolveKey(Transaction txn) { - var txnStr = JsonConvert.SerializeObject(txn.Transaction, Formatting.None); - - var seed = txn.OriginKey.ToByteArray() - .Concat(Encoding.Unicode.GetBytes(txn.Originator)) - .Concat(Encoding.Unicode.GetBytes(txnStr)); - - return _hasher.ComputeHash(seed.ToArray()); + var tree = await _merkleTreeBuilder.BuildTree(txn.Instructions.Select(x => x.InstructionId).ToList()); + return tree.Value; } } } diff --git a/NBlockchain/Services/PendingTransactionList.cs b/NBlockchain/Services/UnconfirmedTransactionCache.cs similarity index 69% rename from NBlockchain/Services/PendingTransactionList.cs rename to NBlockchain/Services/UnconfirmedTransactionCache.cs index b07465d..881ff90 100644 --- a/NBlockchain/Services/PendingTransactionList.cs +++ b/NBlockchain/Services/UnconfirmedTransactionCache.cs @@ -8,19 +8,19 @@ namespace NBlockchain.Services { - public class PendingTransactionList : IPendingTransactionList + public class UnconfirmedTransactionCache : IUnconfirmedTransactionCache { - private readonly ICollection _list = new List(); + private readonly ICollection _list = new List(); private readonly AutoResetEvent _evt = new AutoResetEvent(true); - public ICollection Get + public ICollection Get { get { _evt.WaitOne(); try { - var result = new List(); + var result = new List(); result.AddRange(_list); return result; } @@ -33,12 +33,12 @@ public ICollection Get public event EventHandler Changed; - public bool Add(TransactionEnvelope txn) + public bool Add(Transaction txn) { _evt.WaitOne(); try { - if (_list.Any(x => x.OriginKey == txn.OriginKey && x.Originator == x.Originator)) + if (_list.Any(x => txn.TransactionId.SequenceEqual(x.TransactionId))) return false; _list.Add(txn); @@ -51,14 +51,14 @@ public bool Add(TransactionEnvelope txn) } } - public void Remove(ICollection toRemove) + public void Remove(ICollection toRemove) { _evt.WaitOne(); try { foreach (var item in toRemove) { - var removals = _list.Where(x => x.OriginKey == item.OriginKey && x.Originator == item.Originator).ToList(); + var removals = _list.Where(x => item.TransactionId.SequenceEqual(x.TransactionId)).ToList(); foreach (var remove in removals) _list.Remove(remove); } diff --git a/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs b/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs deleted file mode 100644 index 330b41c..0000000 --- a/Providers/NBlockchain.MongoDB/Models/BlockStatistics.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.MongoDB.Models -{ - public class BlockStatistics - { - public int BlockTime { get; set; } - - public DateTime TimeStamp { get; set; } - } -} diff --git a/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs b/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs index 8d61ee5..29d9b95 100644 --- a/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs +++ b/Providers/NBlockchain.MongoDB/Models/PersistedBlock.cs @@ -2,25 +2,47 @@ using System.Collections.Generic; using System.Text; using MongoDB.Bson; +using NBlockchain.Interfaces; using NBlockchain.Models; +using NBlockchain.Services.Database; namespace NBlockchain.MongoDB.Models { - public class PersistedBlock : Block + public class PersistedBlock { public ObjectId Id { get; set; } - public BlockStatistics Statistics { get; set; } = new BlockStatistics(); + public BlockHeader Header { get; set; } + public ICollection Transactions { get; set; } = new HashSet(); + public MerkleNode MerkleRootNode { get; set; } + + public PersistedBlock() { } - public PersistedBlock(Block block) + public PersistedBlock(Block block, IAddressEncoder addressEncoder) + { + Header = block.Header; + MerkleRootNode = block.MerkleRootNode; + Statistics.TimeStamp = new DateTime(block.Header.Timestamp); + + foreach (var txn in block.Transactions) + Transactions.Add(new PersistedTransaction(txn, addressEncoder)); + } + + public Block ToBlock() { - this.Header = block.Header; - this.MerkleRootNode = block.MerkleRootNode; - this.Transactions = block.Transactions; + var result = new Block(); + result.Header = Header; + result.MerkleRootNode = MerkleRootNode; + + foreach (var txn in Transactions) + result.Transactions.Add(txn.ToTransaction()); + + return result; } + } } diff --git a/Providers/NBlockchain.MongoDB/Models/PersistedInstruction.cs b/Providers/NBlockchain.MongoDB/Models/PersistedInstruction.cs new file mode 100644 index 0000000..b79bd24 --- /dev/null +++ b/Providers/NBlockchain.MongoDB/Models/PersistedInstruction.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; +using MongoDB.Bson; +using NBlockchain.Interfaces; +using NBlockchain.Models; +using NBlockchain.Services.Database; + +namespace NBlockchain.MongoDB.Models +{ + public class PersistedInstruction : PersistedEntity + { + public PersistedInstruction(Instruction entity, IAddressEncoder addressEncoder) + : base(entity) + { + Id = entity.InstructionId; + Statistics.PublicKeyHash = addressEncoder.HashPublicKey(entity.PublicKey); + } + } +} diff --git a/Providers/NBlockchain.MongoDB/Models/PersistedTransaction.cs b/Providers/NBlockchain.MongoDB/Models/PersistedTransaction.cs new file mode 100644 index 0000000..8f85ccc --- /dev/null +++ b/Providers/NBlockchain.MongoDB/Models/PersistedTransaction.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using MongoDB.Bson; +using NBlockchain.Interfaces; +using NBlockchain.Models; +using NBlockchain.Services.Database; + +namespace NBlockchain.MongoDB.Models +{ + public class PersistedTransaction + { + public byte[] TransactionId { get; set; } + + public ICollection Instructions { get; set; } = new HashSet(); + + public PersistedTransaction() + { + } + + public PersistedTransaction(Transaction txn, IAddressEncoder addressEncoder) + { + TransactionId = txn.TransactionId; + foreach (var ins in txn.Instructions) + Instructions.Add(new PersistedInstruction(ins, addressEncoder)); + } + + public Transaction ToTransaction() + { + var result = new Transaction(); + result.TransactionId = TransactionId; + foreach (var ins in Instructions) + result.Instructions.Add(ins.Entity); + + return result; + } + + } +} \ No newline at end of file diff --git a/Providers/NBlockchain.MongoDB/ServiceCollectionExtensions.cs b/Providers/NBlockchain.MongoDB/ServiceCollectionExtensions.cs index e769983..edf732d 100644 --- a/Providers/NBlockchain.MongoDB/ServiceCollectionExtensions.cs +++ b/Providers/NBlockchain.MongoDB/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using MongoDB.Driver; +using NBlockchain.Interfaces; using NBlockchain.MongoDB; using NBlockchain.MongoDB.Services; using NBlockchain.Models; @@ -19,6 +20,7 @@ public static BlockchainMongoOptions UseMongoDB(this BlockchainOptions options, }); options.UseBlockRepository(); + options.Services.AddTransient(); options.AddPeerDiscovery(); return new BlockchainMongoOptions(options, mongoUrl, databaseName); @@ -38,8 +40,8 @@ public BlockchainMongoOptions(BlockchainOptions options, string mongoUrl, string _databaseName = databaseName; } - public BlockchainMongoOptions UseTransactionRepository() - where TImplementation : MongoTransactionRepository, TService + public BlockchainMongoOptions UseInstructionRepository() + where TImplementation : MongoInstructionRepository, TService where TService : class { _options.Services.AddTransient(); diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index 22dd780..cf11355 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -16,11 +16,12 @@ namespace NBlockchain.MongoDB.Services public class MongoBlockRepository : IBlockRepository { private readonly IMongoDatabase _database; - //private readonly AutoResetEvent _cursorEvent = new AutoResetEvent(true); + private readonly IAddressEncoder _addressEncoder; - public MongoBlockRepository(IMongoDatabase database) + public MongoBlockRepository(IMongoDatabase database, IAddressEncoder addressEncoder) { _database = database; + _addressEncoder = addressEncoder; EnsureIndexes(); } @@ -31,11 +32,11 @@ static MongoBlockRepository() { foreach (var type in asm.ExportedTypes) { - var attr = type.GetTypeInfo().GetCustomAttribute(); + var attr = type.GetTypeInfo().GetCustomAttribute(); if (attr != null) { - //BsonSerializer.RegisterDiscriminator(type, new BsonString(attr.TypeId)); - BsonSerializer.RegisterDiscriminator(type, TypeNameDiscriminator.GetDiscriminator(type)); + BsonSerializer.RegisterDiscriminator(type, new BsonString(attr.TypeIdentifier)); + //BsonSerializer.RegisterDiscriminator(type, TypeNameDiscriminator.GetDiscriminator(type)); //hack for dodgy mongo driver IBsonWriter w = new BsonBinaryWriter(new MemoryStream()); @@ -58,45 +59,47 @@ static MongoBlockRepository() public async Task AddBlock(Block block) { - var persisted = new PersistedBlock(block); + var persisted = new PersistedBlock(block, _addressEncoder); var prevHeader = Blocks .Find(x => x.Header.BlockId == block.Header.PreviousBlock) .Project(x => x.Header) .FirstOrDefault(); - - persisted.Statistics.TimeStamp = new DateTime(block.Header.Timestamp); - + if (prevHeader != null) persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); Blocks.InsertOne(persisted); + + await Task.Yield(); } - public async Task HaveBlock(byte[] blockId) + public Task HaveBlock(byte[] blockId) { var query = Blocks.Find(x => x.Header.BlockId == blockId); - return query.Any(); + return Task.FromResult(query.Any()); } - public async Task IsEmpty() + public Task IsEmpty() { - return (Blocks.Count(x => true) == 0); + return Task.FromResult(Blocks.Count(x => true) == 0); } - public async Task GetNewestBlockHeader() + public Task GetNewestBlockHeader() { if (Blocks.Count(x => true) == 0) - return null; + return Task.FromResult(null); var height = Blocks.AsQueryable().Max(x => x.Header.Height); var query = Blocks.AsQueryable().Select(x => x.Header).Where(x => x.Height == height); - return query.FirstOrDefault(); + var result = query.FirstOrDefault(); + return Task.FromResult(result); } - public async Task GetNextBlock(byte[] prevBlockId) + public Task GetNextBlock(byte[] prevBlockId) { var query = Blocks.Find(x => x.Header.PreviousBlock == prevBlockId); - return query.FirstOrDefault(); + var persistedResult = query.FirstOrDefault(); + return persistedResult == null ? Task.FromResult(null) : Task.FromResult(persistedResult.ToBlock()); } public async Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) @@ -127,8 +130,11 @@ private void EnsureIndexes() Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.BlockId), new CreateIndexOptions() { Background = true, Unique = true }); Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.Height), new CreateIndexOptions() { Background = true }); Blocks.Indexes.CreateOne(Builders.IndexKeys.Hashed(x => x.Header.PreviousBlock), new CreateIndexOptions() { Background = true }); - //Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Transactions.Select(y => y.OriginKey)), new CreateIndexOptions() { Background = true, Name = "idx_origkey" }); - //Blocks.Indexes.CreateOne(Builders.IndexKeys.Hashed(x => x.Transactions.Select(y => y.Originator)), new CreateIndexOptions() { Background = true, Name = "idx_origin" }); + + //? + + Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(new StringFieldDefinition("Transactions.TransactionId")), new CreateIndexOptions() { Background = true }); + Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(new StringFieldDefinition("Transactions.Instructions.Id")), new CreateIndexOptions() { Background = true }); _indexesCreated = true; } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoTransactionRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoInstructionRepository.cs similarity index 56% rename from Providers/NBlockchain.MongoDB/Services/MongoTransactionRepository.cs rename to Providers/NBlockchain.MongoDB/Services/MongoInstructionRepository.cs index 7b9c9ee..4461efe 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoTransactionRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoInstructionRepository.cs @@ -11,18 +11,20 @@ namespace NBlockchain.MongoDB.Services { - public abstract class MongoTransactionRepository + public class MongoInstructionRepository : IInstructionRepository { protected readonly IMongoDatabase Database; protected IMongoCollection Blocks => Database.GetCollection("nbc.blocks"); - protected MongoTransactionRepository(IMongoDatabase database) + public MongoInstructionRepository(IMongoDatabase database) { Database = database; EnsureIndexes(); } - protected abstract void CreateIndexes(); + protected virtual void CreateIndexes() + { + } static bool _indexesCreated = false; @@ -30,10 +32,20 @@ private void EnsureIndexes() { if (!_indexesCreated) { - + CreateIndexes(); _indexesCreated = true; } } + + public Task HaveInstruction(byte[] instructionId) + { + var filter = new FilterDefinitionBuilder() + .Eq(new StringFieldDefinition("Transactions.Instructions.Id"), instructionId); + + var result = Blocks.Find(filter).Any(); + + return Task.FromResult(result); + } } } diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index f3a9c45..09f8915 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -21,26 +21,63 @@ class Program private static IPeerNetwork _network; private static ISignatureService _sigService; private static IAddressEncoder _addressEncoder; - private static ICustomTransactionRepository _txnRepo; + private static ICustomInstructionRepository _txnRepo; private static IBlockRepository _blockRepo; + private static ITransactionBuilder _txnBuilder; + + static void Main(string[] args) + { + var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); + //var serviceProvider = ConfigureForLiteDb("node.db", 10500); + + _host = serviceProvider.GetService(); + _miner = serviceProvider.GetService(); + _network = serviceProvider.GetService(); + _sigService = serviceProvider.GetService(); + _addressEncoder = serviceProvider.GetService(); + _txnRepo = serviceProvider.GetService(); + _blockRepo = serviceProvider.GetService(); + _txnBuilder = serviceProvider.GetService(); + + Console.WriteLine("Generating key pair..."); + var keys = _sigService.GenerateKeyPair(); + var address = _addressEncoder.EncodeAddress(keys.PublicKey, 0); + Console.WriteLine($"Your address is {address}"); + + _network.Open(); + + PrintHelp(); + while (true) + { + Console.Write(">"); + var command = Console.ReadLine(); + + if (command == "exit") + break; + + RunCommand(command, keys); + } + _network.Close(); + } private static IServiceProvider ConfigureForLiteDb(string db, uint port) { IServiceCollection services = new ServiceCollection(); services.AddBlockchain(x => - { + { x.UseDataConnection("node.db"); - x.UseTransactionRepository(); - x.UseTcpPeerNetwork(port); + x.UseTransactionRepository(); + x.UseTcpPeerNetwork(port); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); - x.AddTransactionType(); - x.AddTransactionType(); + x.AddInstructionType(); + x.AddInstructionType(); x.AddTransactionRule(); - x.AddTransactionRule(); + x.AddTransactionRule(); + x.AddBlockRule(); x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { - BlockTime = TimeSpan.FromSeconds(120), + BlockTime = TimeSpan.FromSeconds(120), HeaderVersion = 1 }); }); @@ -51,7 +88,7 @@ private static IServiceProvider ConfigureForLiteDb(string db, uint port) //config logging var loggerFactory = serviceProvider.GetService(); loggerFactory.AddDebug(); - loggerFactory.AddFile("node.log", LogLevel.Debug); + loggerFactory.AddFile("node.log", LogLevel.Debug); return serviceProvider; } @@ -60,16 +97,17 @@ private static IServiceProvider ConfigureForMongoDB(string db, uint port) { IServiceCollection services = new ServiceCollection(); services.AddBlockchain(x => - { + { x.UseTcpPeerNetwork(port); x.UseMongoDB(@"mongodb://localhost:27017", db) - .UseTransactionRepository(); + .UseInstructionRepository(); //x.AddPeerDiscovery(sp => new StaticPeerDiscovery("tcp://localhost:503")); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); - x.AddTransactionType(); - x.AddTransactionType(); + x.AddInstructionType(); + x.AddInstructionType(); x.AddTransactionRule(); - x.AddTransactionRule(); + x.AddTransactionRule(); + x.AddBlockRule(); x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { @@ -89,39 +127,6 @@ private static IServiceProvider ConfigureForMongoDB(string db, uint port) return serviceProvider; } - static void Main(string[] args) - { - var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); - - _host = serviceProvider.GetService(); - _miner = serviceProvider.GetService(); - _network = serviceProvider.GetService(); - _sigService = serviceProvider.GetService(); - _addressEncoder = serviceProvider.GetService(); - _txnRepo = serviceProvider.GetService(); - _blockRepo = serviceProvider.GetService(); - - Console.WriteLine("Generating key pair..."); - var keys = _sigService.GenerateKeyPair(); - var address = _addressEncoder.EncodeAddress(keys.PublicKey, 0); - Console.WriteLine($"Your address is {address}"); - - _network.Open(); - - PrintHelp(); - while (true) - { - Console.Write(">"); - var command = Console.ReadLine(); - - if (command == "exit") - break; - - RunCommand(command, keys); - } - _network.Close(); - } - static void RunCommand(string command, KeyPair keys) @@ -175,22 +180,19 @@ static void RunCommand(string command, KeyPair keys) return; } - var txn = new TransferTransaction() + var instruction = new TransferInstruction() { + PublicKey = keys.PublicKey, Amount = Convert.ToInt32(args[2]), - Destination = args[1] + Destination = _addressEncoder.ExtractPublicKeyHash(args[1]) }; - var txnEenv = new TransactionEnvelope(txn) - { - OriginKey = Guid.NewGuid(), - TransactionType = "txn-v1", - Originator = ownAddress - }; - Console.WriteLine($"Singing transaction {txnEenv.OriginKey}"); - _sigService.SignTransaction(txnEenv, keys.PrivateKey); - Console.WriteLine($"Sending transaction {txnEenv.OriginKey}"); - _host.SendTransaction(txnEenv); + Console.WriteLine($"Signing instruction"); + _sigService.SignInstruction(instruction, keys.PrivateKey); + var txn = _txnBuilder.Build(new List() { instruction }).Result; + + Console.WriteLine($"Sending transaction {BitConverter.ToString(txn.TransactionId)}"); + _host.SendTransaction(txn); break; default: Console.WriteLine("invalid command"); diff --git a/Samples/DigitalCurrency/Repositories/ICustomTransactionRepository.cs b/Samples/DigitalCurrency/Repositories/ICustomInstructionRepository.cs similarity index 55% rename from Samples/DigitalCurrency/Repositories/ICustomTransactionRepository.cs rename to Samples/DigitalCurrency/Repositories/ICustomInstructionRepository.cs index d6cc334..2e86e67 100644 --- a/Samples/DigitalCurrency/Repositories/ICustomTransactionRepository.cs +++ b/Samples/DigitalCurrency/Repositories/ICustomInstructionRepository.cs @@ -4,8 +4,8 @@ namespace DigitalCurrency.Repositories { - public interface ICustomTransactionRepository + public interface ICustomInstructionRepository { - decimal GetAccountBalance(string account); + decimal GetAccountBalance(string address); } } diff --git a/Samples/DigitalCurrency/Repositories/LiteDb/CustomInstructionRepository.cs b/Samples/DigitalCurrency/Repositories/LiteDb/CustomInstructionRepository.cs new file mode 100644 index 0000000..cd95597 --- /dev/null +++ b/Samples/DigitalCurrency/Repositories/LiteDb/CustomInstructionRepository.cs @@ -0,0 +1,43 @@ +using DigitalCurrency.Transactions; +using LiteDB; +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; +using NBlockchain.Services.Database; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DigitalCurrency.Repositories.LiteDb +{ + public class CustomInstructionRepository : InstructionRepository, ICustomInstructionRepository + { + private readonly IAddressEncoder _addressEncoder; + + public CustomInstructionRepository(ILoggerFactory loggerFactory, IDataConnection dataConnection, IAddressEncoder addressEncoder) + : base(loggerFactory, dataConnection) + { + _addressEncoder = addressEncoder; + } + + public decimal GetAccountBalance(string address) + { + var publicKeyHash = _addressEncoder.ExtractPublicKeyHash(address); + + var totalOut = Instructions + .Find(Query.EQ("Statistics.PublicKeyHash", publicKeyHash)) + .Select(x => x.Entity) + .OfType() + .Sum(x => x.Amount); + + + var totalIn = Instructions + .Find(Query.EQ("Entity.Destination", publicKeyHash)) + .Select(x => x.Entity) + .OfType() + .Sum(x => x.Amount); + + return (totalIn - totalOut); + } + } +} diff --git a/Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs b/Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs deleted file mode 100644 index 774fea3..0000000 --- a/Samples/DigitalCurrency/Repositories/LiteDb/CustomTransactionRespository.cs +++ /dev/null @@ -1,39 +0,0 @@ -using DigitalCurrency.Transactions; -using LiteDB; -using Microsoft.Extensions.Logging; -using NBlockchain.Interfaces; -using NBlockchain.Services.Database; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace DigitalCurrency.Repositories.LiteDb -{ - public class CustomTransactionRepository : TransactionRepository, ICustomTransactionRepository - { - public CustomTransactionRepository(ILoggerFactory loggerFactory, IDataConnection dataConnection) - : base(loggerFactory, dataConnection) - { - } - - public decimal GetAccountBalance(string account) - { - var totalOut = Transactions - .Find(Query.EQ("Entity.Originator", account)) - .Select(x => x.Entity.Transaction) - .OfType() - .Sum(x => x.Amount); - - - var totalIn = Transactions - .Find(Query.EQ("Entity.Transaction.Destination", account)) - .Select(x => x.Entity.Transaction) - .OfType() - .Where(x => x.Destination == account) - .Sum(x => x.Amount); - - return (totalIn - totalOut); - } - } -} diff --git a/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoTransactionRepository.cs b/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoInstructionRepository.cs similarity index 50% rename from Samples/DigitalCurrency/Repositories/Mongo/CustomMongoTransactionRepository.cs rename to Samples/DigitalCurrency/Repositories/Mongo/CustomMongoInstructionRepository.cs index a484629..07a28a9 100644 --- a/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoTransactionRepository.cs +++ b/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoInstructionRepository.cs @@ -1,32 +1,41 @@ using MongoDB.Bson; using MongoDB.Driver; +using NBlockchain.MongoDB.Models; using NBlockchain.MongoDB.Services; using System; using System.Collections.Generic; using System.Text; +using NBlockchain.Interfaces; namespace DigitalCurrency.Repositories.Mongo { - public class CustomMongoTransactionRepository : MongoTransactionRepository, ICustomTransactionRepository + public class CustomMongoInstructionRepository : MongoInstructionRepository, ICustomInstructionRepository { - public CustomMongoTransactionRepository(IMongoDatabase database) + + private readonly IAddressEncoder _addressEncoder; + + public CustomMongoInstructionRepository(IMongoDatabase database, IAddressEncoder addressEncoder) : base(database) { + _addressEncoder = addressEncoder; } protected override void CreateIndexes() { } - public decimal GetAccountBalance(string account) + public decimal GetAccountBalance(string address) { + var publicKeyHash = _addressEncoder.ExtractPublicKeyHash(address); + var totalOut = 0; var totalIn = 0; - + var outQry = Blocks.Aggregate() .Unwind(x => x.Transactions) - .Match(new BsonDocument("Transactions.Originator", account)) - .Group(new BsonDocument { { "_id", BsonNull.Value }, { "sum", new BsonDocument("$sum", "$Transactions.Transaction.Amount") } }) + .Unwind(new StringFieldDefinition("Transactions.Instructions")) + .Match(new BsonDocument("Transactions.Instructions.Statistics.PublicKeyHash", publicKeyHash)) + .Group(new BsonDocument { { "_id", BsonNull.Value }, { "sum", new BsonDocument("$sum", "$Transactions.Instructions.Entity.Amount") } }) .SingleOrDefault(); if (outQry != null) @@ -37,8 +46,9 @@ public decimal GetAccountBalance(string account) var inQry = Blocks.Aggregate() .Unwind(x => x.Transactions) - .Match(new BsonDocument("Transactions.Transaction.Destination", account)) - .Group(new BsonDocument { { "_id", BsonNull.Value }, { "sum", new BsonDocument("$sum", "$Transactions.Transaction.Amount") } }) + .Unwind(new StringFieldDefinition("Transactions.Instructions")) + .Match(new BsonDocument("Transactions.Instructions.Entity.Destination", publicKeyHash)) + .Group(new BsonDocument { { "_id", BsonNull.Value }, { "sum", new BsonDocument("$sum", "$Transactions.Instructions.Entity.Amount") } }) .SingleOrDefault(); if (inQry != null) diff --git a/Samples/DigitalCurrency/Rules/BalanceRule.cs b/Samples/DigitalCurrency/Rules/BalanceRule.cs index 28e6da8..0c5b6f0 100644 --- a/Samples/DigitalCurrency/Rules/BalanceRule.cs +++ b/Samples/DigitalCurrency/Rules/BalanceRule.cs @@ -4,28 +4,35 @@ using NBlockchain.Services; using System; using System.Collections.Generic; -using System.Text; +using System.Linq; using NBlockchain.Rules; +using NBlockchain.Interfaces; namespace DigitalCurrency.Rules { - public class BalanceRule : TransactionRule + public class BalanceRule : ITransactionRule { - private readonly ICustomTransactionRepository _txnRepo; + private readonly ICustomInstructionRepository _txnRepo; + private readonly IAddressEncoder _addressEncoder; - public BalanceRule(ICustomTransactionRepository txnRepo) + public BalanceRule(ICustomInstructionRepository txnRepo, IAddressEncoder addressEncoder) { _txnRepo = txnRepo; + _addressEncoder = addressEncoder; } - - protected override int Validate(TransactionEnvelope envelope, TransferTransaction transaction, ICollection siblings) + + public int Validate(Transaction transaction, ICollection siblings) { - if (transaction.Amount < 0) + if (transaction.Instructions.OfType().Any(x => x.Amount < 0)) return 1; - - var balance = _txnRepo.GetAccountBalance(envelope.Originator); - if (transaction.Amount > balance) - return 2; + + foreach (var instruction in transaction.Instructions.OfType()) + { + var sourceAddr = _addressEncoder.EncodeAddress(instruction.PublicKey, 0); + var balance = _txnRepo.GetAccountBalance(sourceAddr); + if (instruction.Amount > balance) + return 2; + } return 0; } diff --git a/Samples/DigitalCurrency/Rules/CoinbaseBlockRule.cs b/Samples/DigitalCurrency/Rules/CoinbaseBlockRule.cs new file mode 100644 index 0000000..bef4f9b --- /dev/null +++ b/Samples/DigitalCurrency/Rules/CoinbaseBlockRule.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DigitalCurrency.Transactions; +using NBlockchain.Interfaces; +using NBlockchain.Models; + +namespace DigitalCurrency.Rules +{ + class CoinbaseBlockRule : IBlockRule + { + public bool TailRule => false; + + public Task Validate(Block block) + { + var coinbaseCount = block.Transactions.Count(x => x.Instructions.OfType().Any()); + return Task.FromResult(coinbaseCount == 1); + } + } +} diff --git a/Samples/DigitalCurrency/Rules/CoinbaseRule.cs b/Samples/DigitalCurrency/Rules/CoinbaseRule.cs deleted file mode 100644 index 52608ae..0000000 --- a/Samples/DigitalCurrency/Rules/CoinbaseRule.cs +++ /dev/null @@ -1,21 +0,0 @@ -using DigitalCurrency.Transactions; -using NBlockchain.Models; -using NBlockchain.Services; -using System; -using System.Collections.Generic; -using System.Text; -using NBlockchain.Rules; - -namespace DigitalCurrency.Rules -{ - public class CoinbaseRule : TransactionRule - { - protected override int Validate(TransactionEnvelope envelope, CoinbaseTransaction transaction, ICollection siblings) - { - if (transaction.Amount != -50) - return 1; - - return 0; - } - } -} diff --git a/Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs b/Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs new file mode 100644 index 0000000..37eafd0 --- /dev/null +++ b/Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs @@ -0,0 +1,25 @@ +using DigitalCurrency.Transactions; +using NBlockchain.Models; +using NBlockchain.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NBlockchain.Rules; +using NBlockchain.Interfaces; + +namespace DigitalCurrency.Rules +{ + public class CoinbaseTransactionRule : ITransactionRule + { + public int Validate(Transaction transaction, ICollection siblings) + { + var coinbaseTotal = transaction.Instructions.OfType().Sum(x => x.Amount); + + if (coinbaseTotal != -50) + return 1; + + return 0; + } + } +} diff --git a/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs b/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs index 480478e..12e899c 100644 --- a/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs +++ b/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs @@ -7,19 +7,24 @@ namespace DigitalCurrency.Transactions { - public class CoinbaseBuilder : BlockbaseTransactionBuilder + public class CoinbaseBuilder : BlockbaseTransactionBuilder { - public CoinbaseBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService) - : base(addressEncoder, signatureService) + public CoinbaseBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService, ITransactionBuilder transactionBuilder) + : base(addressEncoder, signatureService, transactionBuilder) { } - protected override CoinbaseTransaction BuildBaseTransaction(ICollection transactions) + protected override ICollection BuildInstructions(KeyPair builderKeys, ICollection transactions) { - return new CoinbaseTransaction() - { - Amount = -50 - }; + var result = new List(); + var instruction = new CoinbaseInstruction(); + instruction.Amount = -50; + instruction.PublicKey = builderKeys.PublicKey; + + SignatureService.SignInstruction(instruction, builderKeys.PrivateKey); + result.Add(instruction); + + return result; } } } diff --git a/Samples/DigitalCurrency/Transactions/CoinbaseTransaction.cs b/Samples/DigitalCurrency/Transactions/CoinbaseInstruction.cs similarity index 62% rename from Samples/DigitalCurrency/Transactions/CoinbaseTransaction.cs rename to Samples/DigitalCurrency/Transactions/CoinbaseInstruction.cs index b0fdb45..8984a7f 100644 --- a/Samples/DigitalCurrency/Transactions/CoinbaseTransaction.cs +++ b/Samples/DigitalCurrency/Transactions/CoinbaseInstruction.cs @@ -5,8 +5,8 @@ namespace DigitalCurrency.Transactions { - [TransactionType("coinbase-v1")] - public class CoinbaseTransaction : ValueTransaction + [InstructionType("coinbase-v1")] + public class CoinbaseInstruction : ValueInstruction { } } diff --git a/Samples/DigitalCurrency/Transactions/TransferInstruction.cs b/Samples/DigitalCurrency/Transactions/TransferInstruction.cs new file mode 100644 index 0000000..066cc5a --- /dev/null +++ b/Samples/DigitalCurrency/Transactions/TransferInstruction.cs @@ -0,0 +1,22 @@ +using NBlockchain.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace DigitalCurrency.Transactions +{ + [InstructionType("txn-v1")] + public class TransferInstruction : ValueInstruction + { + public string Message { get; set; } + + public byte[] Destination { get; set; } + + public override ICollection ExtractSignableElements() + { + var result = base.ExtractSignableElements(); + result.Add(Destination); + return result; + } + } +} diff --git a/Samples/DigitalCurrency/Transactions/TransferTransaction.cs b/Samples/DigitalCurrency/Transactions/TransferTransaction.cs deleted file mode 100644 index cc21848..0000000 --- a/Samples/DigitalCurrency/Transactions/TransferTransaction.cs +++ /dev/null @@ -1,15 +0,0 @@ -using NBlockchain.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace DigitalCurrency.Transactions -{ - [TransactionType("txn-v1")] - public class TransferTransaction : ValueTransaction - { - public string Message { get; set; } - - public string Destination { get; set; } - } -} diff --git a/Samples/DigitalCurrency/Transactions/ValueInstruction.cs b/Samples/DigitalCurrency/Transactions/ValueInstruction.cs new file mode 100644 index 0000000..945a786 --- /dev/null +++ b/Samples/DigitalCurrency/Transactions/ValueInstruction.cs @@ -0,0 +1,17 @@ +using NBlockchain.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace DigitalCurrency.Transactions +{ + public abstract class ValueInstruction : Instruction + { + public int Amount { get; set; } + + public override ICollection ExtractSignableElements() + { + return new List() { BitConverter.GetBytes(Amount) }; + } + } +} diff --git a/Samples/DigitalCurrency/Transactions/ValueTransaction.cs b/Samples/DigitalCurrency/Transactions/ValueTransaction.cs deleted file mode 100644 index 85ba780..0000000 --- a/Samples/DigitalCurrency/Transactions/ValueTransaction.cs +++ /dev/null @@ -1,12 +0,0 @@ -using NBlockchain.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace DigitalCurrency.Transactions -{ - public abstract class ValueTransaction : BlockTransaction - { - public int Amount { get; set; } - } -} diff --git a/ScratchPad/CustomTransactionRepository.cs b/ScratchPad/CustomTransactionRepository.cs deleted file mode 100644 index 908c878..0000000 --- a/ScratchPad/CustomTransactionRepository.cs +++ /dev/null @@ -1,38 +0,0 @@ -using LiteDB; -using Microsoft.Extensions.Logging; -using NBlockchain.Interfaces; -using NBlockchain.Models; -using NBlockchain.Services.Database; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ScratchPad -{ - public class CustomTransactionRepository : TransactionRepository, ICustomTransactionRepository - { - public CustomTransactionRepository(ILoggerFactory loggerFactory, IDataConnection dataConnection) - :base(loggerFactory, dataConnection) - { - } - - public decimal GetAccountBalance(string account) - { - var totalOut = Transactions - .Find(Query.EQ("Entity.Originator", account)) - .Select(x => x.Entity.Transaction) - .OfType() - .Sum(x => x.Amount); - - - var totalIn = Transactions - .Find(Query.EQ("Entity.Transaction.Destination", account)) - .Select(x => x.Entity.Transaction) - .OfType() - .Where(x => x.Destination == account) - .Sum(x => x.Amount); - - return (totalIn - totalOut); - } - } -} diff --git a/ScratchPad/ICustomTransactionRepository.cs b/ScratchPad/ICustomTransactionRepository.cs deleted file mode 100644 index bd4a61d..0000000 --- a/ScratchPad/ICustomTransactionRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ScratchPad -{ - public interface ICustomTransactionRepository - { - decimal GetAccountBalance(string account); - } -} \ No newline at end of file diff --git a/ScratchPad/IHashTester.cs b/ScratchPad/IHashTester.cs deleted file mode 100644 index 6bb230f..0000000 --- a/ScratchPad/IHashTester.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NBlockchain.Services -{ - public interface IHashTester - { - bool TestHash(byte[] hash, uint difficulty); - } -} \ No newline at end of file diff --git a/ScratchPad/Program.cs b/ScratchPad/Program.cs index 9129672..c2ce453 100644 --- a/ScratchPad/Program.cs +++ b/ScratchPad/Program.cs @@ -87,7 +87,7 @@ private static void SendTxn(IServiceProvider sp, KeyPair keys, string address, i Destination = address }; - var txn1env = new TransactionEnvelope(txn1) + var txn1env = new NBlockchain.Models.Transaction(txn1) { OriginKey = Guid.NewGuid(), TransactionType = "txn-v1", @@ -101,7 +101,7 @@ private static void SendTxn(IServiceProvider sp, KeyPair keys, string address, i private static decimal GetBalance(IServiceProvider sp, KeyPair keys) { - var repo = sp.GetService(); + var repo = sp.GetService(); var addressEncoder = sp.GetService(); var address = addressEncoder.EncodeAddress(keys.PublicKey, 0); @@ -144,7 +144,7 @@ private static IServiceProvider ConfigureNode(string db, uint port, string[] pee services.AddBlockchain(x => { //x.UseMongoDB(@"mongodb://localhost:27017", db) - x.UseTransactionRepository(); + x.UseTransactionRepository(); x.UseTcpPeerNetwork(port); x.AddPeerDiscovery(sp => new StaticPeerDiscovery(peers)); //x.UseMulticastDiscovery("test", "224.100.0.1", 8088); diff --git a/ScratchPad/TestBlockbaseBuilder.cs b/ScratchPad/TestBlockbaseBuilder.cs deleted file mode 100644 index ce314b6..0000000 --- a/ScratchPad/TestBlockbaseBuilder.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NBlockchain.Interfaces; -using NBlockchain.Models; -using NBlockchain.Services; - -namespace ScratchPad -{ - public class TestBlockbaseBuilder : BlockbaseTransactionBuilder - { - public TestBlockbaseBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService) - : base(addressEncoder, signatureService) - { - } - - protected override CoinbaseTransaction BuildBaseTransaction(ICollection transactions) - { - return new CoinbaseTransaction() - { - Amount = -100 - }; - } - } -} diff --git a/ScratchPad/TestTransaction.cs b/ScratchPad/TestTransaction.cs deleted file mode 100644 index 8f3ed5e..0000000 --- a/ScratchPad/TestTransaction.cs +++ /dev/null @@ -1,26 +0,0 @@ -using NBlockchain.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace ScratchPad -{ - - public class Transaction : BlockTransaction - { - public int Amount { get; set; } - } - - [TransactionType("txn-v1")] - public class TestTransaction : Transaction - { - public string Message { get; set; } - - public string Destination { get; set; } - } - - [TransactionType("coinbase-v1")] - public class CoinbaseTransaction : Transaction - { - } -} diff --git a/ScratchPad/TestTransactionValidator.cs b/ScratchPad/TestTransactionValidator.cs deleted file mode 100644 index 2bac316..0000000 --- a/ScratchPad/TestTransactionValidator.cs +++ /dev/null @@ -1,43 +0,0 @@ -using NBlockchain.Models; -using NBlockchain.Services; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using NBlockchain.Rules; - -namespace ScratchPad -{ - public class TestTransactionValidator : TransactionRule - { - private readonly ICustomTransactionRepository _txnRepo; - - public TestTransactionValidator(ICustomTransactionRepository txnRepo) - { - _txnRepo = txnRepo; - } - - protected override int Validate(TransactionEnvelope envelope, TestTransaction transaction, ICollection siblings) - { - if (transaction.Amount < 0) - return 1; - - var balance = _txnRepo.GetAccountBalance(envelope.Originator); - if (transaction.Amount > balance) - return 2; - - return 0; - } - } - - public class CoinbaseTransactionValidator : TransactionRule - { - protected override int Validate(TransactionEnvelope envelope, CoinbaseTransaction transaction, ICollection siblings) - { - if (transaction.Amount != -50) - return 1; - - return 0; - } - } -} diff --git a/Tests/NBlockchain.Tests.Scenarios/Common/BaseBuilder.cs b/Tests/NBlockchain.Tests.Scenarios/Common/BaseBuilder.cs index f8fd930..f8a8196 100644 --- a/Tests/NBlockchain.Tests.Scenarios/Common/BaseBuilder.cs +++ b/Tests/NBlockchain.Tests.Scenarios/Common/BaseBuilder.cs @@ -7,16 +7,23 @@ namespace NBlockchain.Tests.Scenarios.Common { - class BaseBuilder : BlockbaseTransactionBuilder + class BaseBuilder : BlockbaseTransactionBuilder { - protected BaseBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService) - : base(addressEncoder, signatureService) + public BaseBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService, ITransactionBuilder transactionBuilder) + : base(addressEncoder, signatureService, transactionBuilder) { } - protected override TestTransaction BuildBaseTransaction(ICollection transactions) + protected override ICollection BuildInstructions(KeyPair builderKeys, ICollection transactions) { - return new TestTransaction() { Data = "base" }; + var instructions = new HashSet(); + var i1 = new TestInstruction(); + i1.Data = "test"; + i1.PublicKey = builderKeys.PublicKey; + SignatureService.SignInstruction(i1, builderKeys.PrivateKey); + instructions.Add(i1); + + return instructions; } } diff --git a/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs b/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs index 6e413e3..8539ef0 100644 --- a/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs +++ b/Tests/NBlockchain.Tests.Scenarios/Common/TestTransaction.cs @@ -5,9 +5,14 @@ namespace NBlockchain.Tests.Scenarios.Common { - [TransactionType("txn-v1")] - public class TestTransaction : BlockTransaction + [InstructionType("txn-v1")] + public class TestInstruction : Instruction { public string Data { get; set; } + + public override ICollection ExtractSignableElements() + { + return new List() { Encoding.UTF8.GetBytes(Data) }; + } } } diff --git a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs index 17518b1..9f6833c 100644 --- a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs +++ b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs @@ -23,7 +23,7 @@ private static IServiceProvider ConfigureNode(uint port, ICollection pee { x.UseTcpPeerNetwork(port); x.AddPeerDiscovery(sp => new StaticPeerDiscovery(peers)); - x.AddTransactionType(); + x.AddInstructionType(); x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { From 2751ea981dea5175963964c988f56653b576573a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 15 Sep 2017 18:02:45 -0700 Subject: [PATCH 27/66] sample --- NBlockchain/Models/BlockchainOptions.cs | 2 +- Samples/DigitalCurrency/Program.cs | 2 +- .../Transactions/CoinbaseBuilder.cs | 8 +- Samples/DigitalCurrency/readme.md | 169 +++++++++++------- 4 files changed, 108 insertions(+), 73 deletions(-) diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index ca95f91..73eb26f 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -120,7 +120,7 @@ public void UseMulticastDiscovery(string serviceId, string multicastAddress, int Services.AddTransient(sp => new MulticastDiscovery(serviceId, multicastAddress, port, sp.GetService(), sp.GetService())); } - public void UseTransactionRepository() + public void UseInstructionRepository() where TImplementation : InstructionRepository, TService where TService : class { diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 09f8915..1dec123 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -66,7 +66,7 @@ private static IServiceProvider ConfigureForLiteDb(string db, uint port) services.AddBlockchain(x => { x.UseDataConnection("node.db"); - x.UseTransactionRepository(); + x.UseInstructionRepository(); x.UseTcpPeerNetwork(port); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); x.AddInstructionType(); diff --git a/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs b/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs index 12e899c..a09a0e9 100644 --- a/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs +++ b/Samples/DigitalCurrency/Transactions/CoinbaseBuilder.cs @@ -17,9 +17,11 @@ public CoinbaseBuilder(IAddressEncoder addressEncoder, ISignatureService signatu protected override ICollection BuildInstructions(KeyPair builderKeys, ICollection transactions) { var result = new List(); - var instruction = new CoinbaseInstruction(); - instruction.Amount = -50; - instruction.PublicKey = builderKeys.PublicKey; + var instruction = new CoinbaseInstruction + { + Amount = -50, + PublicKey = builderKeys.PublicKey + }; SignatureService.SignInstruction(instruction, builderKeys.PrivateKey); result.Add(instruction); diff --git a/Samples/DigitalCurrency/readme.md b/Samples/DigitalCurrency/readme.md index c1dd07b..6dc8c73 100644 --- a/Samples/DigitalCurrency/readme.md +++ b/Samples/DigitalCurrency/readme.md @@ -1,91 +1,119 @@ # Digital currency sample for NBlockchain This example demonstrates how to implement a very basic digital currency with NBlockchain. -(This does not follow the input/output aggregation model that Bitcoin uses but it just meant to illustrate an application of NBlockchin) +(This does not follow the flexible input/output locking script scheme that Bitcoin uses but it just meant to illustrate an application of NBlockchin) -## Define our transactions types +## Define our instruction types -The first thing we will do is define the schema of the transactions we want to store in our blockchain. +The first thing we will do is define the schema of the instructions we want to store in our blockchain. +Each block in the blockchain holds a collection of atomic transactions and each transaction is a collection of instructions. ```c# -public abstract class ValueTransaction : BlockTransaction +public abstract class ValueInstruction : Instruction { public int Amount { get; set; } + + public override ICollection ExtractSignableElements() + { + return new List() { BitConverter.GetBytes(Amount) }; + } } -[TransactionType("txn-v1")] -public class TransferTransaction : ValueTransaction +public class TransferInstruction : ValueInstruction { public string Message { get; set; } - public string Destination { get; set; } + + public byte[] Destination { get; set; } + + public override ICollection ExtractSignableElements() + { + var result = base.ExtractSignableElements(); + result.Add(Destination); + return result; + } } -[TransactionType("coinbase-v1")] -public class CoinbaseTransaction : ValueTransaction +public class CoinbaseInstruction : ValueInstruction { } ``` -In this case we want two types of transactions - * A normal value transfer transaction which is used to send tokens to someone. - * A coinbase transaction that is created per block by the mining node +In this case we want two types of instructions + * A normal value transfer instruction which is used to send tokens to someone. + * A coinbase instruction that is created per block by the mining node -## Implement a repository to query our transactions +## Implement a repository to query our instructions for balance -Now we need to implement a repository to run queries against our defined transactions. -This can be done by extending `TransactionRepository` which gives us access the the block store (if MongoDB is used as the persistence store, then you would extend `MongoTransactionRepository`, see [sample](Repositories/Mongo/CustomMongoTransactionRepository.cs)) +Now we need to implement a repository to run queries against our defined instructions. +This can be done by extending `InstructionRepository` which gives us access the the block store (if MongoDB is used as the persistence store, then you would extend `MongoInstructionRepository`, see [sample](Repositories/Mongo/CustomMongoInstructionRepository.cs)) ```c# -public class CustomTransactionRepository : TransactionRepository, ICustomTransactionRepository +public interface ICustomInstructionRepository +{ + decimal GetAccountBalance(string address); +} + +public class CustomInstructionRepository : InstructionRepository, ICustomInstructionRepository { - public CustomTransactionRepository(ILoggerFactory loggerFactory, IDataConnection dataConnection) + private readonly IAddressEncoder _addressEncoder; + + public CustomInstructionRepository(ILoggerFactory loggerFactory, IDataConnection dataConnection, IAddressEncoder addressEncoder) : base(loggerFactory, dataConnection) { + _addressEncoder = addressEncoder; } - public decimal GetAccountBalance(string account) + public decimal GetAccountBalance(string address) { - var totalOut = Transactions - .Find(Query.EQ("Entity.Originator", account)) - .Select(x => x.Entity.Transaction) - .OfType() + var publicKeyHash = _addressEncoder.ExtractPublicKeyHash(address); + + var totalOut = Instructions + .Find(Query.EQ("Statistics.PublicKeyHash", publicKeyHash)) + .Select(x => x.Entity) + .OfType() .Sum(x => x.Amount); - var totalIn = Transactions - .Find(Query.EQ("Entity.Transaction.Destination", account)) - .Select(x => x.Entity.Transaction) - .OfType() - .Where(x => x.Destination == account) + var totalIn = Instructions + .Find(Query.EQ("Entity.Destination", publicKeyHash)) + .Select(x => x.Entity) + .OfType() .Sum(x => x.Amount); return (totalIn - totalOut); } } + ``` -## Define the rules for our transactions +## Define the rules for our instructions -Now we want to define a rule that you cannot spend more than the balance of your wallet. +Now we want to define a rule that you cannot spend more than the balance of your account. ```c# -public class BalanceRule : TransactionRule +public class BalanceRule : ITransactionRule { - private readonly ITransactionRepository _txnRepo; + private readonly ICustomInstructionRepository _txnRepo; + private readonly IAddressEncoder _addressEncoder; - public BalanceRule(ITransactionRepository txnRepo) + public BalanceRule(ICustomInstructionRepository txnRepo, IAddressEncoder addressEncoder) { _txnRepo = txnRepo; + _addressEncoder = addressEncoder; } - - protected override int Validate(TransactionEnvelope envelope, TransferTransaction transaction, ICollection siblings) + + public int Validate(Transaction transaction, ICollection siblings) { - if (transaction.Amount < 0) + if (transaction.Instructions.OfType().Any(x => x.Amount < 0)) return 1; - - var balance = _txnRepo.GetAccountBalance(envelope.Originator); - if (transaction.Amount > balance) - return 2; + + foreach (var instruction in transaction.Instructions.OfType()) + { + var sourceAddr = _addressEncoder.EncodeAddress(instruction.PublicKey, 0); + var balance = _txnRepo.GetAccountBalance(sourceAddr); + if (instruction.Amount > balance) + return 2; + } return 0; } @@ -98,19 +126,23 @@ Now we define how the coinbase transaction is built (by the miners). In this case, we will have a static block reward of 50 ```c# -public class CoinbaseBuilder : BlockbaseTransactionBuilder +public class CoinbaseBuilder : BlockbaseTransactionBuilder { - public CoinbaseBuilder(IAddressEncoder addressEncoder, ISignatureService signatureService) - : base(addressEncoder, signatureService) - { - } + ... - protected override CoinbaseTransaction BuildBaseTransaction(ICollection transactions) + protected override ICollection BuildInstructions(KeyPair builderKeys, ICollection transactions) { - return new CoinbaseTransaction() + var result = new List(); + var instruction = new CoinbaseInstruction { - Amount = -50 + Amount = -50, + PublicKey = builderKeys.PublicKey }; + + SignatureService.SignInstruction(instruction, builderKeys.PrivateKey); + result.Add(instruction); + + return result; } } ``` @@ -120,31 +152,32 @@ public class CoinbaseBuilder : BlockbaseTransactionBuilder When we configure the IoC container for our blockchain node, we have several options In this case we chose * To use the built-in LiteDb as the database (using node.db as the datafile) - * Register our customer transaction repository that we use in our rule definitions + * Register our custome instruction repository that we use in our rule definitions * To use the Tcp peer network and listen on port 500 * Use the multicast peer discovery protocol (to find other peers on the LAN) - * Added our Transaction types that we defined earlier - * Added our Transaction rules - * Set the block time to 10 seconds + * Added our Instruction types that we defined earlier + * Added our Instruction rules + * Added our Block rules + * Set the block time to 120 seconds ```c# IServiceCollection services = new ServiceCollection(); services.AddBlockchain(x => { x.UseDataConnection("node.db"); - x.UseTransactionRepository(); - x.UseTcpPeerNetwork(500); + x.UseInstructionRepository(); + x.UseTcpPeerNetwork(port); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); - x.AddTransactionType(); - x.AddTransactionType(); + x.AddInstructionType(); + x.AddInstructionType(); x.AddTransactionRule(); - x.AddTransactionRule(); + x.AddTransactionRule(); + x.AddBlockRule(); x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { - BlockTime = TimeSpan.FromSeconds(10), - HeaderVersion = 1, - ExpectedContentThreshold = 0.8m + BlockTime = TimeSpan.FromSeconds(120), + HeaderVersion = 1 }); }); ``` @@ -155,20 +188,20 @@ If you wanted to use MongoDB as the persistence store, then the config would loo IServiceCollection services = new ServiceCollection(); services.AddBlockchain(x => { - x.UseMongoDB(@"mongodb://localhost:27017", "my-currency-db") - .UseTransactionRepository(); - x.UseTcpPeerNetwork(500); + x.UseTcpPeerNetwork(port); + x.UseMongoDB(@"mongodb://localhost:27017", db) + .UseInstructionRepository(); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); - x.AddTransactionType(); - x.AddTransactionType(); + x.AddInstructionType(); + x.AddInstructionType(); x.AddTransactionRule(); - x.AddTransactionRule(); + x.AddTransactionRule(); + x.AddBlockRule(); x.UseBlockbaseProvider(); x.UseParameters(new StaticNetworkParameters() { - BlockTime = TimeSpan.FromSeconds(10), - HeaderVersion = 1, - ExpectedContentThreshold = 0.8m + BlockTime = TimeSpan.FromSeconds(120), + HeaderVersion = 1 }); }); ``` From 59b5b08eab671639b68157d8f00e49e986f32a85 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 08:01:14 -0700 Subject: [PATCH 28/66] fork testing --- NBlockchain/Interfaces/IBlockRepository.cs | 15 ++ NBlockchain/Interfaces/IBlockVerifier.cs | 2 + NBlockchain/NBlockchain.csproj | 6 +- NBlockchain/Services/BlockVerifier.cs | 6 +- .../Database/DefaultBlockRepository.cs | 74 +++++++-- .../Database/InstructionRepository.cs | 2 +- NBlockchain/Services/NodeHost.cs | 157 ++++++++++++++---- .../NBlockchain.MongoDB.csproj | 6 +- .../Services/MongoBlockRepository.cs | 151 ++++++++++++++--- .../Services/MongoInstructionRepository.cs | 4 +- Samples/DigitalCurrency/Program.cs | 10 +- .../Mongo/CustomMongoInstructionRepository.cs | 4 +- 12 files changed, 363 insertions(+), 74 deletions(-) diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index 656af6f..3b562ae 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using NBlockchain.Models; +using System.Collections.Generic; namespace NBlockchain.Interfaces { @@ -12,6 +13,20 @@ public interface IBlockRepository Task GetNewestBlockHeader(); Task GetNextBlock(byte[] prevBlockId); + Task GetBlockHeader(byte[] blockId); + Task GetBlock(byte[] blockId); + + Task GetMainChainHeader(uint height); + Task GetForkHeader(byte[] forkBlockId); + + Task AddDetachedBlock(Block block); + + Task GetDivergentHeader(byte[] forkTipBlockId); + + Task RewindChain(byte[] blockId); + + Task> GetFork(byte[] forkTipBlockId); + Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc); } diff --git a/NBlockchain/Interfaces/IBlockVerifier.cs b/NBlockchain/Interfaces/IBlockVerifier.cs index 58c2721..7985318 100644 --- a/NBlockchain/Interfaces/IBlockVerifier.cs +++ b/NBlockchain/Interfaces/IBlockVerifier.cs @@ -8,6 +8,8 @@ public interface IBlockVerifier { Task Verify(Block block); + Task VerifyTransactions(Block block); + Task VerifyBlockRules(Block block, bool tail); Task VerifyTransaction(Transaction transaction, ICollection siblings); diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index de1a0a4..3978bb7 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 0.3.0-alpha - 0.3.0.0 - 0.3.0.0 + 0.4.0-alpha + 0.4.0.0 + 0.4.0.0 https://github.com/danielgerlag/NBlockchain https://github.com/danielgerlag/NBlockchain.git git diff --git a/NBlockchain/Services/BlockVerifier.cs b/NBlockchain/Services/BlockVerifier.cs index b4587ac..edc3ec9 100644 --- a/NBlockchain/Services/BlockVerifier.cs +++ b/NBlockchain/Services/BlockVerifier.cs @@ -49,14 +49,18 @@ public async Task Verify(Block block) if (!merkleRoot.Value.SequenceEqual(block.Header.MerkelRoot)) return false; + + return true; + } + public async Task VerifyTransactions(Block block) + { foreach (var txn in block.Transactions) { var siblings = block.Transactions.Where(x => x != txn).ToList(); if (await VerifyTransaction(txn, siblings) != 0) return false; } - return true; } diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 57259f0..e04eb24 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -17,7 +17,8 @@ public class DefaultBlockRepository : IBlockRepository private readonly IDataConnection _connection; private readonly IAddressEncoder _addressEncoder; - protected LiteCollection Blocks => _connection.Database.GetCollection("Blocks"); + protected LiteCollection MainChain => _connection.Database.GetCollection("MainChain"); + protected LiteCollection ForkChain => _connection.Database.GetCollection("ForkChain"); protected LiteCollection Instructions => _connection.Database.GetCollection("Instructions"); public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection connection, IAddressEncoder addressEncoder) @@ -26,9 +27,14 @@ public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection conn _addressEncoder = addressEncoder; _logger = loggerFactory.CreateLogger(); - Blocks.EnsureIndex(x => x.Entity.Header.BlockId); - Blocks.EnsureIndex(x => x.Entity.Header.PreviousBlock); - Blocks.EnsureIndex(x => x.Entity.Header.Height); + MainChain.EnsureIndex(x => x.Entity.Header.BlockId, true); + MainChain.EnsureIndex(x => x.Entity.Header.PreviousBlock, true); + MainChain.EnsureIndex(x => x.Entity.Header.Height, true); + + ForkChain.EnsureIndex(x => x.Entity.Header.BlockId); + ForkChain.EnsureIndex(x => x.Entity.Header.PreviousBlock); + ForkChain.EnsureIndex(x => x.Entity.Header.Height); + Instructions.EnsureIndex(x => x.BlockId); Instructions.EnsureIndex(x => x.TransactionId); Instructions.EnsureIndex(x => x.Entity.InstructionId); @@ -39,7 +45,7 @@ public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection conn public Task AddBlock(Block block) { var persisted = new PersistedBlock(block); - var prevHeader = Blocks + var prevHeader = MainChain .Find(x => x.Entity.Header.BlockId == block.Header.PreviousBlock) .Select(x => x.Entity.Header) .FirstOrDefault(); @@ -47,7 +53,7 @@ public Task AddBlock(Block block) if (prevHeader != null) persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); - Blocks.Insert(persisted); + MainChain.Insert(persisted); foreach (var txn in block.Transactions) { @@ -60,13 +66,17 @@ public Task AddBlock(Block block) public Task HaveBlock(byte[] blockId) { - var result = Blocks.Exists(x => x.Entity.Header.BlockId == blockId); + var result = MainChain.Exists(x => x.Entity.Header.BlockId == blockId); + + if (!result) + result = ForkChain.Exists(x => x.Entity.Header.BlockId == blockId); + return Task.FromResult(result); } public Task IsEmpty() { - var count = Blocks.Count(); + var count = MainChain.Count(); return Task.FromResult(count == 0); } @@ -75,14 +85,14 @@ public async Task GetNewestBlockHeader() if (await IsEmpty()) return null; - var max = Blocks.Max(x => x.Entity.Header.Height).AsInt64; - var block = Blocks.Find(Query.EQ("Entity.Header.Height", max)).First(); + var max = MainChain.Max(x => x.Entity.Header.Height).AsInt64; + var block = MainChain.Find(Query.EQ("Entity.Header.Height", max)).First(); return block?.Entity.Header; } public Task GetNextBlock(byte[] prevBlockId) { - var blockHeader = Blocks.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); + var blockHeader = MainChain.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); if (blockHeader == null) return Task.FromResult(null); @@ -105,12 +115,52 @@ public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) var startTicks = startUtc.Ticks; var endTicks = endUtc.Ticks; - var sample = Blocks.Find(Query.And(Query.LT("Entity.Header.Timestamp", endTicks), Query.GT("Entity.Header.Timestamp", startTicks))); + var sample = MainChain.Find(Query.And(Query.LT("Entity.Header.Timestamp", endTicks), Query.GT("Entity.Header.Timestamp", startTicks))); if (sample.Count() == 0) return Task.FromResult(0); var result = Convert.ToInt32(sample.Average(x => x.Statistics.BlockTime)); return Task.FromResult(result); } + + public Task GetBlockHeader(byte[] blockId) + { + throw new NotImplementedException(); + } + + public Task GetBlock(byte[] blockId) + { + throw new NotImplementedException(); + } + + public Task GetMainChainHeader(uint height) + { + throw new NotImplementedException(); + } + + public Task GetForkHeader(byte[] forkBlockId) + { + throw new NotImplementedException(); + } + + public Task AddDetachedBlock(Block block) + { + throw new NotImplementedException(); + } + + public Task GetDivergentHeader(byte[] forkTipBlockId) + { + throw new NotImplementedException(); + } + + public Task RewindChain(byte[] blockId) + { + throw new NotImplementedException(); + } + + public Task> GetFork(byte[] forkTipBlockId) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/NBlockchain/Services/Database/InstructionRepository.cs b/NBlockchain/Services/Database/InstructionRepository.cs index 9749bb1..fa84cc4 100644 --- a/NBlockchain/Services/Database/InstructionRepository.cs +++ b/NBlockchain/Services/Database/InstructionRepository.cs @@ -16,7 +16,7 @@ public class InstructionRepository : IInstructionRepository protected readonly ILogger Logger; protected readonly IDataConnection Connection; - protected LiteCollection Blocks => Connection.Database.GetCollection("Blocks"); + protected LiteCollection MainChain => Connection.Database.GetCollection("MainChain"); protected LiteCollection Instructions => Connection.Database.GetCollection("Instructions"); public InstructionRepository(ILoggerFactory loggerFactory, IDataConnection connection) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 5fcc6f0..bedf64a 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -63,45 +63,73 @@ public async Task RecieveTail(Block block) if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; - var prevHeader = await _blockRepository.GetNewestBlockHeader(); + if (!await _blockVerifier.Verify(block)) + { + _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Demerit; + } + + if (!await _blockVerifier.VerifyBlockRules(block, true)) + { + _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Ignore; + } + + var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); + var bestHeader = await _blockRepository.GetNewestBlockHeader(); + bool mainChain = true; + bool rebaseChain = false; if (prevHeader != null) { - if (!block.Header.PreviousBlock.SequenceEqual(prevHeader.BlockId)) + if (block.Header.Timestamp < prevHeader.Timestamp) return PeerDataResult.Ignore; - if (block.Header.Timestamp < prevHeader.Timestamp) + if (block.Header.Height != (prevHeader.Height + 1)) return PeerDataResult.Ignore; + + mainChain = (prevHeader.BlockId.SequenceEqual(bestHeader.BlockId)); + rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); - - if (block.Header.Difficulty < expectedDifficulty) + if ((mainChain) && (block.Header.Difficulty < expectedDifficulty)) return PeerDataResult.Ignore; } else { if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && await _blockRepository.IsEmpty())) return PeerDataResult.Ignore; - } + } - if (!await _blockVerifier.Verify(block)) + if (mainChain) { - _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Demerit; + if (!await _blockVerifier.VerifyTransactions(block)) + { + _logger.LogWarning($"Block txn verification failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Demerit; + } + + await _blockRepository.AddBlock(block); + _unconfirmedTransactionCache.Remove(block.Transactions); } - - if (!await _blockVerifier.VerifyBlockRules(block, true)) + else { - _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Ignore; + await _blockRepository.AddDetachedBlock(block); + if (rebaseChain) + { + var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId); + if (divergentHeader != null) + { + if (! await RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) + return PeerDataResult.Ignore; + } + } } - - await _blockRepository.AddBlock(block); - _unconfirmedTransactionCache.Remove(block.Transactions); + _expectedBlockList.Confirm(block.Header.PreviousBlock); _expectedBlockList.ExpectNext(block.Header.BlockId); - _logger.LogDebug($"Accepted tail {BitConverter.ToString(block.Header.BlockId)}"); + _logger.LogDebug($"Accepted tip {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Relay; } @@ -110,7 +138,7 @@ public async Task RecieveTail(Block block) _blockEvent.Set(); } } - + public async Task RecieveBlock(Block block) { if (!_expectedBlockList.IsExpected(block.Header.PreviousBlock)) @@ -123,16 +151,10 @@ public async Task RecieveBlock(Block block) try { _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)}"); - + if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; - if (!await _blockRepository.HaveBlock(block.Header.PreviousBlock)) - { - if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && await _blockRepository.IsEmpty())) - return PeerDataResult.Ignore; - } - if (!await _blockVerifier.Verify(block)) { _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); @@ -145,9 +167,59 @@ public async Task RecieveBlock(Block block) return PeerDataResult.Ignore; } - await _blockRepository.AddBlock(block); - _expectedBlockList.Confirm(block.Header.PreviousBlock); + var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); + var mainFork = await _blockRepository.GetMainChainHeader(block.Header.Height); + var bestHeader = await _blockRepository.GetNewestBlockHeader(); + bool mainChain = true; + bool rebaseChain = false; + if (prevHeader != null) + { + if (block.Header.Timestamp < prevHeader.Timestamp) + return PeerDataResult.Ignore; + + if (block.Header.Height != (prevHeader.Height + 1)) + return PeerDataResult.Ignore; + + mainChain = (mainFork != null); + rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); + + var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); + + if ((mainChain) && (block.Header.Difficulty < expectedDifficulty)) + return PeerDataResult.Ignore; + } + else + { + if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && await _blockRepository.IsEmpty())) + return PeerDataResult.Ignore; + } + + if (mainChain) + { + if (!await _blockVerifier.VerifyTransactions(block)) + { + _logger.LogWarning($"Block txn verification failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Ignore; + } + + await _blockRepository.AddBlock(block); + } + else + { + await _blockRepository.AddDetachedBlock(block); + if (rebaseChain) + { + var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId); + if (divergentHeader != null) + { + if (!await RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) + return PeerDataResult.Ignore; + } + } + } + + _expectedBlockList.Confirm(block.Header.PreviousBlock); _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); } finally @@ -205,6 +277,35 @@ private async void GetMissingBlocks(object state) _peerNetwork.RequestNextBlock(prevHeader.BlockId); } } - + + private async Task RebaseChain(byte[] divergentId, byte[] targetTipId) + { + _logger.LogInformation($"Rebasing chain from {BitConverter.ToString(divergentId)} to {BitConverter.ToString(targetTipId)}"); + var currentTipHeader = await _blockRepository.GetNewestBlockHeader(); + await _blockRepository.RewindChain(divergentId); + var chainFork = await _blockRepository.GetFork(targetTipId); + foreach (var forkedBlock in chainFork.OrderBy(x => x.Header.Height)) + { + var prevHeader = await _blockRepository.GetBlockHeader(forkedBlock.Header.PreviousBlock); + var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); + + if (forkedBlock.Header.Difficulty < expectedDifficulty) + { + _logger.LogWarning($"Rebase failed on expected difficulty {BitConverter.ToString(forkedBlock.Header.BlockId)}"); + await RebaseChain(divergentId, currentTipHeader.BlockId); + return false; + } + + if (!await _blockVerifier.VerifyTransactions(forkedBlock)) + { + _logger.LogWarning($"Rebase failed on block txn verification {BitConverter.ToString(forkedBlock.Header.BlockId)}"); + await RebaseChain(divergentId, currentTipHeader.BlockId); + return false; + } + await _blockRepository.AddBlock(forkedBlock); + } + return true; + } + } } diff --git a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj index d7f8fc5..119e106 100644 --- a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj +++ b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.3.0-alpha + 0.4.0-alpha MongoDB persistence store for NBlockchain https://github.com/danielgerlag/NBlockchain.git https://github.com/danielgerlag/NBlockchain git Blockchain - 0.3.0.0 - 0.3.0.0 + 0.4.0.0 + 0.4.0.0 true diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index cf11355..2569e68 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -10,6 +10,7 @@ using NBlockchain.Interfaces; using NBlockchain.Models; using System.IO; +using System.Collections.Generic; namespace NBlockchain.MongoDB.Services { @@ -55,12 +56,13 @@ static MongoBlockRepository() //BsonSerializer.RegisterDiscriminator(t, t.FullName)); } - private IMongoCollection Blocks => _database.GetCollection("nbc.blocks"); + private IMongoCollection MainChain => _database.GetCollection("MainChain"); + private IMongoCollection ForkChain => _database.GetCollection("ForkChain"); public async Task AddBlock(Block block) { var persisted = new PersistedBlock(block, _addressEncoder); - var prevHeader = Blocks + var prevHeader = MainChain .Find(x => x.Header.BlockId == block.Header.PreviousBlock) .Project(x => x.Header) .FirstOrDefault(); @@ -68,36 +70,39 @@ public async Task AddBlock(Block block) if (prevHeader != null) persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); - Blocks.InsertOne(persisted); + MainChain.InsertOne(persisted); await Task.Yield(); } public Task HaveBlock(byte[] blockId) - { - var query = Blocks.Find(x => x.Header.BlockId == blockId); - return Task.FromResult(query.Any()); + { + var result = MainChain.Find(x => x.Header.BlockId == blockId).Any(); + if (!result) + result = ForkChain.Find(x => x.Header.BlockId == blockId).Any(); + + return Task.FromResult(result); } public Task IsEmpty() { - return Task.FromResult(Blocks.Count(x => true) == 0); + return Task.FromResult(MainChain.Count(x => true) == 0); } public Task GetNewestBlockHeader() { - if (Blocks.Count(x => true) == 0) + if (MainChain.Count(x => true) == 0) return Task.FromResult(null); - var height = Blocks.AsQueryable().Max(x => x.Header.Height); - var query = Blocks.AsQueryable().Select(x => x.Header).Where(x => x.Height == height); + var height = MainChain.AsQueryable().Max(x => x.Header.Height); + var query = MainChain.AsQueryable().Select(x => x.Header).Where(x => x.Height == height); var result = query.FirstOrDefault(); return Task.FromResult(result); } public Task GetNextBlock(byte[] prevBlockId) { - var query = Blocks.Find(x => x.Header.PreviousBlock == prevBlockId); + var query = MainChain.Find(x => x.Header.PreviousBlock == prevBlockId); var persistedResult = query.FirstOrDefault(); return persistedResult == null ? Task.FromResult(null) : Task.FromResult(persistedResult.ToBlock()); } @@ -107,7 +112,7 @@ public async Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime end var startTicks = startUtc.Ticks; var endTicks = endUtc.Ticks; - var avgQuery = Blocks.Aggregate() + var avgQuery = MainChain.Aggregate() .Match(x => x.Header.Timestamp > startTicks && x.Header.Timestamp < endTicks && x.Header.Height > 1) .Group(new BsonDocument { { "_id", "$item" }, { "avg", new BsonDocument("$avg", "$Statistics.BlockTime") } }) .SingleOrDefault(); @@ -120,6 +125,111 @@ public async Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime end return 0; } + + public Task GetBlockHeader(byte[] blockId) + { + if (MainChain.Count(x => true) == 0) + return Task.FromResult(null); + + var result = MainChain.AsQueryable().Select(x => x.Header).Where(x => x.BlockId == blockId).FirstOrDefault(); + + if (result == null) + result = ForkChain.AsQueryable().Select(x => x.Header).Where(x => x.BlockId == blockId).FirstOrDefault(); + + return Task.FromResult(result); + } + + public Task GetBlock(byte[] blockId) + { + var persistedResult = MainChain.Find(x => x.Header.BlockId == blockId).FirstOrDefault(); + + if (persistedResult == null) + persistedResult = ForkChain.Find(x => x.Header.BlockId == blockId).FirstOrDefault(); + + return persistedResult == null ? Task.FromResult(null) : Task.FromResult(persistedResult.ToBlock()); + } + + public Task GetMainChainHeader(uint height) + { + if (MainChain.Count(x => true) == 0) + return Task.FromResult(null); + + var query = MainChain.AsQueryable().Select(x => x.Header).Where(x => x.Height == height); + var result = query.FirstOrDefault(); + return Task.FromResult(result); + } + + public async Task AddDetachedBlock(Block block) + { + var persisted = new PersistedBlock(block, _addressEncoder); + ForkChain.InsertOne(persisted); + await Task.Yield(); + } + + public Task GetForkHeader(byte[] forkBlockId) + { + var forktip = ForkChain.AsQueryable().Select(x => x.Header).Where(x => x.BlockId == forkBlockId).FirstOrDefault(); + return Task.FromResult(forktip); + } + + public async Task GetDivergentHeader(byte[] forkTipBlockId) + { + var forkHeader = await GetForkHeader(forkTipBlockId); + if (forkHeader == null) + return null; + + while (!forkHeader.PreviousBlock.SequenceEqual(Block.HeadKey)) + { + var mainParent = MainChain.AsQueryable().Select(x => x.Header).Where(x => x.BlockId == forkHeader.PreviousBlock).FirstOrDefault(); + if (mainParent != null) + return mainParent; + + forkHeader = await GetForkHeader(forkHeader.PreviousBlock); + if (forkHeader == null) + return null; + } + return null; + } + + public async Task RewindChain(byte[] blockId) + { + var divergent = MainChain.AsQueryable().FirstOrDefault(x => x.Header.BlockId == blockId); + if (divergent == null) + return; + + var archiveFork = MainChain.AsQueryable() + .Where(x => x.Header.Height > divergent.Header.Height) + .ToList() + .Select(x => x.ToBlock()); + + foreach (var block in archiveFork) + await AddDetachedBlock(block); + + MainChain.DeleteMany(x => x.Header.Height > divergent.Header.Height); + } + + public async Task> GetFork(byte[] forkTipBlockId) + { + var result = new List(); + + var forkBlock = ForkChain.AsQueryable().FirstOrDefault(x => x.Header.BlockId == forkTipBlockId); + if (forkBlock == null) + return result; + + while (!forkBlock.Header.PreviousBlock.SequenceEqual(Block.HeadKey)) + { + result.Add(forkBlock.ToBlock()); + var mainParent = MainChain.AsQueryable().Select(x => x.Header).Where(x => x.BlockId == forkBlock.Header.PreviousBlock).FirstOrDefault(); + if (mainParent != null) + break; + + forkBlock = ForkChain.AsQueryable().FirstOrDefault(x => x.Header.BlockId == forkTipBlockId); + if (forkBlock == null) + break; + } + + return result.OrderBy(x => x.Header.Height).ToList(); + } static bool _indexesCreated = false; @@ -127,18 +237,19 @@ private void EnsureIndexes() { if (!_indexesCreated) { - Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.BlockId), new CreateIndexOptions() { Background = true, Unique = true }); - Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.Height), new CreateIndexOptions() { Background = true }); - Blocks.Indexes.CreateOne(Builders.IndexKeys.Hashed(x => x.Header.PreviousBlock), new CreateIndexOptions() { Background = true }); + MainChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.BlockId), new CreateIndexOptions() { Background = true, Unique = true }); + MainChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.Height), new CreateIndexOptions() { Background = true, Unique = true }); + MainChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.PreviousBlock), new CreateIndexOptions() { Background = true, Unique = true }); + MainChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(new StringFieldDefinition("Transactions.TransactionId")), new CreateIndexOptions() { Background = true }); + MainChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(new StringFieldDefinition("Transactions.Instructions.Id")), new CreateIndexOptions() { Background = true }); - //? - - Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(new StringFieldDefinition("Transactions.TransactionId")), new CreateIndexOptions() { Background = true }); - Blocks.Indexes.CreateOne(Builders.IndexKeys.Ascending(new StringFieldDefinition("Transactions.Instructions.Id")), new CreateIndexOptions() { Background = true }); + ForkChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.BlockId), new CreateIndexOptions() { Background = true }); + ForkChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.Height), new CreateIndexOptions() { Background = true }); + ForkChain.Indexes.CreateOne(Builders.IndexKeys.Ascending(x => x.Header.PreviousBlock), new CreateIndexOptions() { Background = true }); _indexesCreated = true; } - } + } } } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoInstructionRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoInstructionRepository.cs index 4461efe..ace6d0f 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoInstructionRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoInstructionRepository.cs @@ -14,7 +14,7 @@ namespace NBlockchain.MongoDB.Services public class MongoInstructionRepository : IInstructionRepository { protected readonly IMongoDatabase Database; - protected IMongoCollection Blocks => Database.GetCollection("nbc.blocks"); + protected IMongoCollection MainChain => Database.GetCollection("MainChain"); public MongoInstructionRepository(IMongoDatabase database) { @@ -42,7 +42,7 @@ public Task HaveInstruction(byte[] instructionId) var filter = new FilterDefinitionBuilder() .Eq(new StringFieldDefinition("Transactions.Instructions.Id"), instructionId); - var result = Blocks.Find(filter).Any(); + var result = MainChain.Find(filter).Any(); return Task.FromResult(result); } diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 1dec123..a150bf7 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -39,8 +39,9 @@ static void Main(string[] args) _blockRepo = serviceProvider.GetService(); _txnBuilder = serviceProvider.GetService(); - Console.WriteLine("Generating key pair..."); - var keys = _sigService.GenerateKeyPair(); + Console.Write("Enter passphrase:"); + var phrase = Console.ReadLine(); + var keys = _sigService.GetKeyPairFromPhrase(phrase); var address = _addressEncoder.EncodeAddress(keys.PublicKey, 0); Console.WriteLine($"Your address is {address}"); @@ -169,6 +170,11 @@ static void RunCommand(string command, KeyPair keys) var header = _blockRepo.GetNewestBlockHeader().Result; Console.WriteLine($"Height: {header.Height}, Id: {BitConverter.ToString(header.BlockId)}"); break; + case "gen-key": + var genkeys = _sigService.GetKeyPairFromPhrase(args[1]); + var address = _addressEncoder.EncodeAddress(genkeys.PublicKey, 0); + Console.WriteLine($"{address}"); + break; case "avg-time": var avgTime = _blockRepo.GetAverageBlockTimeInSecs(DateTime.UtcNow.AddHours(-1), DateTime.UtcNow).Result; Console.WriteLine($"Avg time: {avgTime}s"); diff --git a/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoInstructionRepository.cs b/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoInstructionRepository.cs index 07a28a9..952fe57 100644 --- a/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoInstructionRepository.cs +++ b/Samples/DigitalCurrency/Repositories/Mongo/CustomMongoInstructionRepository.cs @@ -31,7 +31,7 @@ public decimal GetAccountBalance(string address) var totalOut = 0; var totalIn = 0; - var outQry = Blocks.Aggregate() + var outQry = MainChain.Aggregate() .Unwind(x => x.Transactions) .Unwind(new StringFieldDefinition("Transactions.Instructions")) .Match(new BsonDocument("Transactions.Instructions.Statistics.PublicKeyHash", publicKeyHash)) @@ -44,7 +44,7 @@ public decimal GetAccountBalance(string address) totalOut = bOut.AsInt32; } - var inQry = Blocks.Aggregate() + var inQry = MainChain.Aggregate() .Unwind(x => x.Transactions) .Unwind(new StringFieldDefinition("Transactions.Instructions")) .Match(new BsonDocument("Transactions.Instructions.Entity.Destination", publicKeyHash)) From b36c769baa742f9c3e87ecec1f91c5cc80162f91 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 08:58:54 -0700 Subject: [PATCH 29/66] fork testing --- NBlockchain/Interfaces/IBlockReceiver.cs | 4 +- NBlockchain/Interfaces/IPeerNetwork.cs | 4 +- NBlockchain/Services/BlockMiner.cs | 2 +- .../Services/Net/InProcessPeerNetwork.cs | 9 +- NBlockchain/Services/Net/TcpPeerNetwork.cs | 52 +++++-- NBlockchain/Services/NodeHost.cs | 141 ++++-------------- 6 files changed, 81 insertions(+), 131 deletions(-) diff --git a/NBlockchain/Interfaces/IBlockReceiver.cs b/NBlockchain/Interfaces/IBlockReceiver.cs index ad11843..a9a2614 100644 --- a/NBlockchain/Interfaces/IBlockReceiver.cs +++ b/NBlockchain/Interfaces/IBlockReceiver.cs @@ -8,9 +8,7 @@ namespace NBlockchain.Interfaces { public interface IBlockReceiver { - Task RecieveBlock(Block block); - - Task RecieveTail(Block block); + Task RecieveBlock(Block block, bool tip); } public enum PeerDataResult { Ignore, Relay, Demerit } diff --git a/NBlockchain/Interfaces/IPeerNetwork.cs b/NBlockchain/Interfaces/IPeerNetwork.cs index f17179b..80655bd 100644 --- a/NBlockchain/Interfaces/IPeerNetwork.cs +++ b/NBlockchain/Interfaces/IPeerNetwork.cs @@ -15,7 +15,9 @@ public interface IPeerNetwork void BroadcastTransaction(Transaction transaction); void RequestNextBlock(byte[] blockId); - + + void RequestBlock(byte[] blockId); + void RegisterBlockReceiver(IBlockReceiver blockReceiver); void RegisterTransactionReceiver(ITransactionReceiver transactionReciever); diff --git a/NBlockchain/Services/BlockMiner.cs b/NBlockchain/Services/BlockMiner.cs index 54e7660..50d29f9 100644 --- a/NBlockchain/Services/BlockMiner.cs +++ b/NBlockchain/Services/BlockMiner.cs @@ -88,7 +88,7 @@ private async void Mine() { if (block.Header.Status == BlockStatus.Confirmed) { - var recvResult = await _blockReciever.RecieveTail(block); + var recvResult = await _blockReciever.RecieveBlock(block, true); if (recvResult == PeerDataResult.Relay) _peerNetwork.BroadcastTail(block); } diff --git a/NBlockchain/Services/Net/InProcessPeerNetwork.cs b/NBlockchain/Services/Net/InProcessPeerNetwork.cs index 8369f13..d847dec 100644 --- a/NBlockchain/Services/Net/InProcessPeerNetwork.cs +++ b/NBlockchain/Services/Net/InProcessPeerNetwork.cs @@ -50,13 +50,13 @@ public void Close() public Action ReceiveBlock => (peer, block) => { - _blockReciever.RecieveBlock(block); + _blockReciever.RecieveBlock(block, false); }; public Action ReceiveTail => (peer, block) => { - _blockReciever.RecieveTail(block); + _blockReciever.RecieveBlock(block, true); }; public Action ReceiveTransaction => (peer, txn) => @@ -112,5 +112,10 @@ public ICollection GetPeersOut() { throw new NotImplementedException(); } + + public void RequestBlock(byte[] blockId) + { + throw new NotImplementedException(); + } } } diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index dfa38fe..feb5487 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -204,7 +204,10 @@ private async void Peer_OnReceiveMessage(PeerConnection sender, byte command, by await ProcessBlock(data, sender.RemoteId, false); break; case Commands.BlockRequest: - await ProcessBlockRequest(data, sender); + await ProcessBlockRequest(data, false, sender); + break; + case Commands.NextBlockRequest: + await ProcessBlockRequest(data, true, sender); break; case Commands.PeerShare: if (IsSharablePeer(Encoding.UTF8.GetString(data))) @@ -227,9 +230,14 @@ private async void Peer_OnReceiveMessage(PeerConnection sender, byte command, by } } - private async Task ProcessBlockRequest(byte[] prevBlockId, PeerConnection peer) + private async Task ProcessBlockRequest(byte[] blockId, bool next, PeerConnection peer) { - var block = await _blockRepository.GetNextBlock(prevBlockId); + Block block = null; + if (next) + block = await _blockRepository.GetNextBlock(blockId); + else + block = await _blockRepository.GetBlock(blockId); + if (block != null) { _logger.LogDebug("Responding to block request"); @@ -242,26 +250,23 @@ private async Task ProcessBlockRequest(byte[] prevBlockId, PeerConnection peer) } } - private async Task ProcessBlock(byte[] data, Guid originId, bool tail) + private async Task ProcessBlock(byte[] data, Guid originId, bool tip) { var block = DeserializeObject(data); _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)} from {originId}"); var result = PeerDataResult.Ignore; - if (tail) - result = await _blockReciever.RecieveTail(block); - else - result = await _blockReciever.RecieveBlock(block); + result = await _blockReciever.RecieveBlock(block, tip); - if ((tail) && (result == PeerDataResult.Relay)) + if ((tip) && (result == PeerDataResult.Relay)) { var relayTask = Task.Factory.StartNew(() => { var peerList = GetActivePeers().Where(x => x.RemoteId != originId); Parallel.ForEach(peerList, peer => { - SendBlock(peer, data, tail); + SendBlock(peer, data, tip); }); }); } @@ -494,10 +499,11 @@ public void RequestNextBlock(byte[] blockId) Task.Factory.StartNew(async () => { var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); + //TODO: round robin foreach (var peer in peers) { _logger.LogDebug($"Requesting block {BitConverter.ToString(blockId)} from incoming peer {peer.RemoteId}"); - peer.Send(Commands.BlockRequest, blockId); + peer.Send(Commands.NextBlockRequest, blockId); await Task.Delay(TimeSpan.FromSeconds(5)); @@ -507,6 +513,25 @@ public void RequestNextBlock(byte[] blockId) }); } + public void RequestBlock(byte[] blockId) + { + Task.Factory.StartNew(async () => + { + var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); + //TODO: round robin + foreach (var peer in peers) + { + _logger.LogDebug($"Requesting block {BitConverter.ToString(blockId)} from incoming peer {peer.RemoteId}"); + peer.Send(Commands.BlockRequest, blockId); + + await Task.Delay(TimeSpan.FromSeconds(5)); + + if ((await _blockRepository.GetBlockHeader(blockId)) != null) + return; + } + }); + } + public void Dispose() { @@ -624,7 +649,8 @@ internal class Commands public const byte Block = 1; public const byte Txn = 2; public const byte BlockRequest = 3; - public const byte PeerShare = 4; - public const byte TxnRequest = 5; + public const byte NextBlockRequest = 4; + public const byte PeerShare = 5; + public const byte TxnRequest = 6; } } diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index bedf64a..f2e489f 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -19,7 +19,7 @@ public class NodeHost : INodeHost private readonly ILogger _logger; private readonly IServiceProvider _serviceProvider; private readonly IDateTimeProvider _dateTimeProvider; - private readonly IExpectedBlockList _expectedBlockList; + //private readonly IExpectedBlockList _expectedBlockList; private readonly IPeerNetwork _peerNetwork; private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); private readonly IUnconfirmedTransactionCache _unconfirmedTransactionCache; @@ -38,7 +38,7 @@ public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, _unconfirmedTransactionCache = unconfirmedTransactionCache; _peerNetwork = peerNetwork; _difficultyCalculator = difficultyCalculator; - _expectedBlockList = expectedBlockList; + //_expectedBlockList = expectedBlockList; _logger = loggerFactory.CreateLogger(); _peerNetwork.RegisterBlockReceiver(this); @@ -46,20 +46,15 @@ public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, _pollTimer = new Timer(GetMissingBlocks, null, TimeSpan.FromSeconds(5), _parameters.BlockTime); } - - public async Task RecieveTail(Block block) + + + public async Task RecieveBlock(Block block, bool tip) { - if (!_expectedBlockList.IsExpected(block.Header.PreviousBlock)) - { - _logger.LogDebug($"Unexpected next block for {BitConverter.ToString(block.Header.PreviousBlock)}"); - return PeerDataResult.Ignore; - } - _blockEvent.WaitOne(); try { - _logger.LogDebug($"Recv tail {BitConverter.ToString(block.Header.BlockId)}"); - + _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)} {tip}"); + if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; @@ -77,7 +72,8 @@ public async Task RecieveTail(Block block) var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); var bestHeader = await _blockRepository.GetNewestBlockHeader(); - bool mainChain = true; + var isEmpty = await _blockRepository.IsEmpty(); + bool mainChain = false; bool rebaseChain = false; if (prevHeader != null) @@ -87,8 +83,13 @@ public async Task RecieveTail(Block block) if (block.Header.Height != (prevHeader.Height + 1)) return PeerDataResult.Ignore; - - mainChain = (prevHeader.BlockId.SequenceEqual(bestHeader.BlockId)); + + var isTip = (prevHeader.BlockId.SequenceEqual(bestHeader.BlockId)); + var mainPrev = await _blockRepository.GetMainChainHeader(prevHeader.Height); + + if (mainPrev != null) + mainChain = (isTip || (mainPrev.BlockId.SequenceEqual(prevHeader.BlockId))); + rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); @@ -97,9 +98,11 @@ public async Task RecieveTail(Block block) } else { - if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && await _blockRepository.IsEmpty())) + if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && isEmpty)) return PeerDataResult.Ignore; - } + mainChain = isEmpty; + _peerNetwork.RequestBlock(block.Header.PreviousBlock); + } if (mainChain) { @@ -120,115 +123,31 @@ public async Task RecieveTail(Block block) var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId); if (divergentHeader != null) { - if (! await RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) + if (!await RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) return PeerDataResult.Ignore; } } } - - _expectedBlockList.Confirm(block.Header.PreviousBlock); - _expectedBlockList.ExpectNext(block.Header.BlockId); - - _logger.LogDebug($"Accepted tip {BitConverter.ToString(block.Header.BlockId)}"); - - return PeerDataResult.Relay; - } - finally - { - _blockEvent.Set(); - } - } - - public async Task RecieveBlock(Block block) - { - if (!_expectedBlockList.IsExpected(block.Header.PreviousBlock)) - { - _logger.LogDebug($"Unexpected next block for {BitConverter.ToString(block.Header.PreviousBlock)}"); - return PeerDataResult.Ignore; - } - - _blockEvent.WaitOne(); - try - { - _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)}"); - - if (await _blockRepository.HaveBlock(block.Header.BlockId)) - return PeerDataResult.Ignore; - - if (!await _blockVerifier.Verify(block)) - { - _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Demerit; - } - if (!await _blockVerifier.VerifyBlockRules(block, false)) - { - _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Ignore; - } + //_expectedBlockList.Confirm(block.Header.PreviousBlock); + //_expectedBlockList.ExpectNext(block.Header.BlockId); - var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); - var mainFork = await _blockRepository.GetMainChainHeader(block.Header.Height); - var bestHeader = await _blockRepository.GetNewestBlockHeader(); - bool mainChain = true; - bool rebaseChain = false; - - if (prevHeader != null) - { - if (block.Header.Timestamp < prevHeader.Timestamp) - return PeerDataResult.Ignore; - - if (block.Header.Height != (prevHeader.Height + 1)) - return PeerDataResult.Ignore; - - mainChain = (mainFork != null); - rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); - - var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); - - if ((mainChain) && (block.Header.Difficulty < expectedDifficulty)) - return PeerDataResult.Ignore; - } - else - { - if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && await _blockRepository.IsEmpty())) - return PeerDataResult.Ignore; - } + _logger.LogDebug($"Accepted tip {BitConverter.ToString(block.Header.BlockId)}"); - if (mainChain) + if (tip) { - if (!await _blockVerifier.VerifyTransactions(block)) - { - _logger.LogWarning($"Block txn verification failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Ignore; - } - - await _blockRepository.AddBlock(block); + return PeerDataResult.Relay; } else { - await _blockRepository.AddDetachedBlock(block); - if (rebaseChain) - { - var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId); - if (divergentHeader != null) - { - if (!await RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) - return PeerDataResult.Ignore; - } - } + GetMissingBlocks(null); + return PeerDataResult.Ignore; } - - _expectedBlockList.Confirm(block.Header.PreviousBlock); - _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); } finally { _blockEvent.Set(); } - - GetMissingBlocks(null); - return PeerDataResult.Ignore; } public async Task RecieveTransaction(Transaction transaction) @@ -265,7 +184,7 @@ private async void GetMissingBlocks(object state) if (prevHeader == null) { _logger.LogDebug("Requesting head block"); - _expectedBlockList.ExpectNext(Block.HeadKey); + //_expectedBlockList.ExpectNext(Block.HeadKey); _peerNetwork.RequestNextBlock(Block.HeadKey); return; } @@ -273,7 +192,7 @@ private async void GetMissingBlocks(object state) //if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) { _logger.LogDebug($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); - _expectedBlockList.ExpectNext(prevHeader.BlockId); + //_expectedBlockList.ExpectNext(prevHeader.BlockId); _peerNetwork.RequestNextBlock(prevHeader.BlockId); } } From 8089da5cd6749cb1be00979e5dbf71c673e762e1 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 09:26:21 -0700 Subject: [PATCH 30/66] logging --- NBlockchain/Services/NodeHost.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index f2e489f..97cdda9 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -53,7 +53,7 @@ public async Task RecieveBlock(Block block, bool tip) _blockEvent.WaitOne(); try { - _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)} {tip}"); + _logger.LogInformation($"Recv block {BitConverter.ToString(block.Header.BlockId)} {tip}"); if (await _blockRepository.HaveBlock(block.Header.BlockId)) return PeerDataResult.Ignore; @@ -92,6 +92,8 @@ public async Task RecieveBlock(Block block, bool tip) rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); + _logger.LogInformation($"Processing block, have prev, main chain: {mainChain}, rebase: {rebaseChain}"); + var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); if ((mainChain) && (block.Header.Difficulty < expectedDifficulty)) return PeerDataResult.Ignore; @@ -101,6 +103,7 @@ public async Task RecieveBlock(Block block, bool tip) if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && isEmpty)) return PeerDataResult.Ignore; mainChain = isEmpty; + _logger.LogInformation($"Processing block, missing prev, main chain: {mainChain}, rebase: {rebaseChain}"); _peerNetwork.RequestBlock(block.Header.PreviousBlock); } @@ -117,29 +120,36 @@ public async Task RecieveBlock(Block block, bool tip) } else { + _logger.LogInformation($"Adding detached block"); await _blockRepository.AddDetachedBlock(block); if (rebaseChain) { + _logger.LogInformation($"Searching for divergent block"); var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId); if (divergentHeader != null) { + _logger.LogInformation($"Rebasing chain from {divergentHeader.Height}"); if (!await RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) return PeerDataResult.Ignore; } + else + { + _logger.LogInformation($"Divergent block not found"); + } } } //_expectedBlockList.Confirm(block.Header.PreviousBlock); //_expectedBlockList.ExpectNext(block.Header.BlockId); - - _logger.LogDebug($"Accepted tip {BitConverter.ToString(block.Header.BlockId)}"); - + if (tip) { + _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Relay; } else { + _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); GetMissingBlocks(null); return PeerDataResult.Ignore; } From 347ac75fa2692926eddc201252a51e138a370ed9 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 09:30:48 -0700 Subject: [PATCH 31/66] logging --- NBlockchain/Services/Net/TcpPeerNetwork.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index feb5487..8309bde 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -240,13 +240,13 @@ private async Task ProcessBlockRequest(byte[] blockId, bool next, PeerConnection if (block != null) { - _logger.LogDebug("Responding to block request"); + _logger.LogInformation($"Responding to block request {next} {BitConverter.ToString(blockId)}"); var data = SerializeObject(block); SendBlock(peer, data, false); } else { - _logger.LogDebug("Unable to respond to block request"); + _logger.LogInformation($"Unable to respond to block request {next} {BitConverter.ToString(blockId)}"); } } @@ -502,7 +502,7 @@ public void RequestNextBlock(byte[] blockId) //TODO: round robin foreach (var peer in peers) { - _logger.LogDebug($"Requesting block {BitConverter.ToString(blockId)} from incoming peer {peer.RemoteId}"); + _logger.LogInformation($"Requesting next block {BitConverter.ToString(blockId)} from peer {peer.RemoteId}"); peer.Send(Commands.NextBlockRequest, blockId); await Task.Delay(TimeSpan.FromSeconds(5)); @@ -521,7 +521,7 @@ public void RequestBlock(byte[] blockId) //TODO: round robin foreach (var peer in peers) { - _logger.LogDebug($"Requesting block {BitConverter.ToString(blockId)} from incoming peer {peer.RemoteId}"); + _logger.LogInformation($"Requesting block {BitConverter.ToString(blockId)} from peer {peer.RemoteId}"); peer.Send(Commands.BlockRequest, blockId); await Task.Delay(TimeSpan.FromSeconds(5)); From 663a13601e51f71fe04064161fba9bffd1ad1afc Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 09:41:20 -0700 Subject: [PATCH 32/66] logging --- NBlockchain/Services/NodeHost.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 97cdda9..7bc53c3 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -56,7 +56,10 @@ public async Task RecieveBlock(Block block, bool tip) _logger.LogInformation($"Recv block {BitConverter.ToString(block.Header.BlockId)} {tip}"); if (await _blockRepository.HaveBlock(block.Header.BlockId)) + { + _logger.LogInformation("already have block"); return PeerDataResult.Ignore; + } if (!await _blockVerifier.Verify(block)) { @@ -78,13 +81,24 @@ public async Task RecieveBlock(Block block, bool tip) if (prevHeader != null) { + _logger.LogInformation("Do Have previous block"); + if (block.Header.Timestamp < prevHeader.Timestamp) + { + _logger.LogInformation("Timestamps dont match"); return PeerDataResult.Ignore; + } if (block.Header.Height != (prevHeader.Height + 1)) + { + _logger.LogInformation($"Height mismatch prev: {prevHeader.Height}, this: {block.Header.Height}"); return PeerDataResult.Ignore; + } var isTip = (prevHeader.BlockId.SequenceEqual(bestHeader.BlockId)); + + _logger.LogInformation($"Is Tip {isTip}"); + var mainPrev = await _blockRepository.GetMainChainHeader(prevHeader.Height); if (mainPrev != null) @@ -96,12 +110,19 @@ public async Task RecieveBlock(Block block, bool tip) var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); if ((mainChain) && (block.Header.Difficulty < expectedDifficulty)) + { + _logger.LogInformation("Difficulty mismatch"); return PeerDataResult.Ignore; + } } else { + _logger.LogInformation("Dont have prev block"); if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && isEmpty)) + { + _logger.LogInformation("not first block but am empty"); return PeerDataResult.Ignore; + } mainChain = isEmpty; _logger.LogInformation($"Processing block, missing prev, main chain: {mainChain}, rebase: {rebaseChain}"); _peerNetwork.RequestBlock(block.Header.PreviousBlock); @@ -109,6 +130,7 @@ public async Task RecieveBlock(Block block, bool tip) if (mainChain) { + _logger.LogInformation("processing for main chain"); if (!await _blockVerifier.VerifyTransactions(block)) { _logger.LogWarning($"Block txn verification failed for {BitConverter.ToString(block.Header.BlockId)}"); From 7896cff463e8001a8ecab61384292ea647fe4fc3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 10:05:55 -0700 Subject: [PATCH 33/66] tweaking --- NBlockchain/Services/NodeHost.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 7bc53c3..2e2afca 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -53,7 +53,7 @@ public async Task RecieveBlock(Block block, bool tip) _blockEvent.WaitOne(); try { - _logger.LogInformation($"Recv block {BitConverter.ToString(block.Header.BlockId)} {tip}"); + _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)} {tip}"); if (await _blockRepository.HaveBlock(block.Header.BlockId)) { @@ -96,13 +96,17 @@ public async Task RecieveBlock(Block block, bool tip) } var isTip = (prevHeader.BlockId.SequenceEqual(bestHeader.BlockId)); - _logger.LogInformation($"Is Tip {isTip}"); - - var mainPrev = await _blockRepository.GetMainChainHeader(prevHeader.Height); - - if (mainPrev != null) - mainChain = (isTip || (mainPrev.BlockId.SequenceEqual(prevHeader.BlockId))); + + if (!isTip) + { + var mainExisiting = await _blockRepository.GetMainChainHeader(block.Header.Height); + mainChain = (mainExisiting == null); + } + else + { + mainChain = true; + } rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); @@ -118,7 +122,7 @@ public async Task RecieveBlock(Block block, bool tip) else { _logger.LogInformation("Dont have prev block"); - if (!(block.Header.PreviousBlock.SequenceEqual(Block.HeadKey) && isEmpty)) + if (isEmpty && !block.Header.PreviousBlock.SequenceEqual(Block.HeadKey)) { _logger.LogInformation("not first block but am empty"); return PeerDataResult.Ignore; From dab27454262f56390bcd7da78691a85ad94b956d Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 10:16:06 -0700 Subject: [PATCH 34/66] tweaks --- NBlockchain/Services/NodeHost.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 2e2afca..efe52ac 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -100,8 +100,10 @@ public async Task RecieveBlock(Block block, bool tip) if (!isTip) { + var prevMain = await _blockRepository.GetMainChainHeader(block.Header.Height - 1); + var isPrevOnMainChain = prevMain?.BlockId.SequenceEqual(block.Header.PreviousBlock) ?? false; var mainExisiting = await _blockRepository.GetMainChainHeader(block.Header.Height); - mainChain = (mainExisiting == null); + mainChain = ((mainExisiting == null) && (isPrevOnMainChain)); } else { From 4642427b7aa4b0c631520ab9d3ea3f921f726d43 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 10:29:45 -0700 Subject: [PATCH 35/66] tweaks --- NBlockchain/Interfaces/IBlockRepository.cs | 2 +- NBlockchain/Services/NodeHost.cs | 17 +++++++++++++++++ .../Services/MongoBlockRepository.cs | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index 3b562ae..c3ecff6 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -22,7 +22,7 @@ public interface IBlockRepository Task AddDetachedBlock(Block block); Task GetDivergentHeader(byte[] forkTipBlockId); - + Task RewindChain(byte[] blockId); Task> GetFork(byte[] forkTipBlockId); diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index efe52ac..58123cd 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -163,6 +163,9 @@ public async Task RecieveBlock(Block block, bool tip) else { _logger.LogInformation($"Divergent block not found"); + var firstForkHeader = await FindKnownForkbase(block.Header.BlockId); + if (firstForkHeader != null) + _peerNetwork.RequestBlock(firstForkHeader.PreviousBlock); } } } @@ -264,5 +267,19 @@ private async Task RebaseChain(byte[] divergentId, byte[] targetTipId) return true; } + + private async Task FindKnownForkbase(byte[] forkTipId) + { + _logger.LogInformation($"Searching for fork base"); + var header = await _blockRepository.GetForkHeader(forkTipId); + + var prevHeader = await _blockRepository.GetForkHeader(header.PreviousBlock); + while (prevHeader != null) + { + header = prevHeader; + prevHeader = await _blockRepository.GetForkHeader(prevHeader.PreviousBlock); + } + return header; + } } } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index 2569e68..853e4c9 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -171,7 +171,7 @@ public Task GetForkHeader(byte[] forkBlockId) var forktip = ForkChain.AsQueryable().Select(x => x.Header).Where(x => x.BlockId == forkBlockId).FirstOrDefault(); return Task.FromResult(forktip); } - + public async Task GetDivergentHeader(byte[] forkTipBlockId) { var forkHeader = await GetForkHeader(forkTipBlockId); From 7d61473da4bf5b9829b181f92461159c640bbeeb Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 10:45:43 -0700 Subject: [PATCH 36/66] tweaks --- NBlockchain/Interfaces/IBlockRepository.cs | 1 + NBlockchain/Services/NodeHost.cs | 10 +++++++++- .../Services/MongoBlockRepository.cs | 7 +++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index c3ecff6..9d327c6 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -26,6 +26,7 @@ public interface IBlockRepository Task RewindChain(byte[] blockId); Task> GetFork(byte[] forkTipBlockId); + Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc); diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 58123cd..9aa9850 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -234,7 +234,15 @@ private async void GetMissingBlocks(object state) { _logger.LogDebug($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); //_expectedBlockList.ExpectNext(prevHeader.BlockId); - _peerNetwork.RequestNextBlock(prevHeader.BlockId); + var cached = await _blockRepository.GetNextBlock(prevHeader.BlockId); + if (cached == null) + { + _peerNetwork.RequestNextBlock(prevHeader.BlockId); + } + else + { + await RecieveBlock(cached, false); + } } } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index 853e4c9..43ea82a 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -102,8 +102,11 @@ public Task GetNewestBlockHeader() public Task GetNextBlock(byte[] prevBlockId) { - var query = MainChain.Find(x => x.Header.PreviousBlock == prevBlockId); - var persistedResult = query.FirstOrDefault(); + var persistedResult = MainChain.Find(x => x.Header.PreviousBlock == prevBlockId).FirstOrDefault(); + + if (persistedResult == null) + persistedResult = ForkChain.Find(x => x.Header.PreviousBlock == prevBlockId).FirstOrDefault(); + return persistedResult == null ? Task.FromResult(null) : Task.FromResult(persistedResult.ToBlock()); } From 6c9a97d4e33ccd5ce65e7b6ae1edf979b9408aec Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 10:52:03 -0700 Subject: [PATCH 37/66] tweaks --- NBlockchain/Interfaces/IBlockRepository.cs | 3 ++- .../Services/Database/DefaultBlockRepository.cs | 11 ++++++----- NBlockchain/Services/NodeHost.cs | 9 ++++++--- .../Services/MongoBlockRepository.cs | 9 ++++++--- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index 9d327c6..7db2939 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -8,7 +8,8 @@ namespace NBlockchain.Interfaces public interface IBlockRepository { Task AddBlock(Block block); - Task HaveBlock(byte[] blockId); + Task HaveBlockMainChain(byte[] blockId); + Task HaveBlockForkChain(byte[] blockId); Task IsEmpty(); Task GetNewestBlockHeader(); Task GetNextBlock(byte[] prevBlockId); diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index e04eb24..6ecdfe0 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -64,13 +64,9 @@ public Task AddBlock(Block block) return Task.CompletedTask; } - public Task HaveBlock(byte[] blockId) + public Task HaveBlockMainChain(byte[] blockId) { var result = MainChain.Exists(x => x.Entity.Header.BlockId == blockId); - - if (!result) - result = ForkChain.Exists(x => x.Entity.Header.BlockId == blockId); - return Task.FromResult(result); } @@ -162,5 +158,10 @@ public Task> GetFork(byte[] forkTipBlockId) { throw new NotImplementedException(); } + + public Task HaveBlockForkChain(byte[] blockId) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 9aa9850..9412b27 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -55,7 +55,7 @@ public async Task RecieveBlock(Block block, bool tip) { _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)} {tip}"); - if (await _blockRepository.HaveBlock(block.Header.BlockId)) + if (await _blockRepository.HaveBlockMainChain(block.Header.BlockId)) { _logger.LogInformation("already have block"); return PeerDataResult.Ignore; @@ -148,8 +148,11 @@ public async Task RecieveBlock(Block block, bool tip) } else { - _logger.LogInformation($"Adding detached block"); - await _blockRepository.AddDetachedBlock(block); + if (!await _blockRepository.HaveBlockForkChain(block.Header.BlockId)) + { + _logger.LogInformation($"Adding detached block"); + await _blockRepository.AddDetachedBlock(block); + } if (rebaseChain) { _logger.LogInformation($"Searching for divergent block"); diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index 43ea82a..9bfe9aa 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -75,12 +75,15 @@ public async Task AddBlock(Block block) await Task.Yield(); } - public Task HaveBlock(byte[] blockId) + public Task HaveBlockMainChain(byte[] blockId) { var result = MainChain.Find(x => x.Header.BlockId == blockId).Any(); - if (!result) - result = ForkChain.Find(x => x.Header.BlockId == blockId).Any(); + return Task.FromResult(result); + } + public Task HaveBlockForkChain(byte[] blockId) + { + var result = ForkChain.Find(x => x.Header.BlockId == blockId).Any(); return Task.FromResult(result); } From d5a9ab73141fe8339515d07367091fcf5bb72a3d Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 11:15:32 -0700 Subject: [PATCH 38/66] tweaks --- .gitignore | 5 ++++- NBlockchain/Models/Instruction.cs | 1 - NBlockchain/Services/Net/PeerConnection.cs | 2 +- NBlockchain/Services/Net/TcpPeerNetwork.cs | 12 ++++++------ NBlockchain/Services/NodeHost.cs | 13 +++++++++---- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 3c4efe2..4b1cec6 100644 --- a/.gitignore +++ b/.gitignore @@ -258,4 +258,7 @@ paket-files/ # Python Tools for Visual Studio (PTVS) __pycache__/ -*.pyc \ No newline at end of file +*.pyc +Samples/DigitalCurrency/node.db + +Samples/DigitalCurrency/node-journal.db diff --git a/NBlockchain/Models/Instruction.cs b/NBlockchain/Models/Instruction.cs index eaf3b9c..63239f8 100644 --- a/NBlockchain/Models/Instruction.cs +++ b/NBlockchain/Models/Instruction.cs @@ -3,7 +3,6 @@ using System.IO; using System.Security.Cryptography; using System.Linq; -using System.Runtime.Serialization.Formatters.Binary; namespace NBlockchain.Models { diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index e60dbd1..d9d9aee 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -35,7 +35,7 @@ public class PeerConnection public Guid RemoteId => _remoteId; - public EndPoint RemoteEndPoint => _client.Client.RemoteEndPoint; + public EndPoint RemoteEndPoint => _client?.Client?.RemoteEndPoint; public bool Outgoing { get; private set; } public string ConnectionString { get; private set; } public DateTime? LastContact => _lastContact; diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index 8309bde..bac9c97 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -129,11 +129,11 @@ public void Open() _logger.LogInformation($"Check in - Outgoing peers: {peers.Count(x => x.Outgoing)}"); _logger.LogInformation($"Check in - Incoming peers: {peers.Count(x => !x.Outgoing)}"); - var process = Process.GetCurrentProcess(); + //var process = Process.GetCurrentProcess(); - _logger.LogInformation($"Check in - Thread count: {process.Threads.Count}"); - _logger.LogInformation($"Check in - Working set: {process.WorkingSet64}"); - _logger.LogInformation($"Check in - PrivateMemorySize: {process.PrivateMemorySize64}"); + //_logger.LogInformation($"Check in - Thread count: {process.Threads.Count}"); + //_logger.LogInformation($"Check in - Working set: {process.WorkingSet64}"); + //_logger.LogInformation($"Check in - PrivateMemorySize: {process.PrivateMemorySize64}"); } }); @@ -629,7 +629,7 @@ public ICollection GetPeersIn() { return GetActivePeers() .Where(x => !x.Outgoing) - .Select(x => new ConnectedPeer(x.RemoteId, x.RemoteEndPoint.ToString())) + .Select(x => new ConnectedPeer(x.RemoteId, x.RemoteEndPoint?.ToString())) .ToList(); } @@ -637,7 +637,7 @@ public ICollection GetPeersOut() { return GetActivePeers() .Where(x => x.Outgoing) - .Select(x => new ConnectedPeer(x.RemoteId, x.RemoteEndPoint.ToString())) + .Select(x => new ConnectedPeer(x.RemoteId, x.RemoteEndPoint?.ToString())) .ToList(); } diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 9412b27..3464b6e 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -184,13 +184,16 @@ public async Task RecieveBlock(Block block, bool tip) else { _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); - GetMissingBlocks(null); return PeerDataResult.Ignore; } } finally { _blockEvent.Set(); + if (tip) + { + GetMissingBlocks(null); + } } } @@ -227,7 +230,7 @@ private async void GetMissingBlocks(object state) if (prevHeader == null) { - _logger.LogDebug("Requesting head block"); + _logger.LogInformation("Requesting head block"); //_expectedBlockList.ExpectNext(Block.HeadKey); _peerNetwork.RequestNextBlock(Block.HeadKey); return; @@ -235,7 +238,7 @@ private async void GetMissingBlocks(object state) //if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) { - _logger.LogDebug($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); + _logger.LogInformation($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); //_expectedBlockList.ExpectNext(prevHeader.BlockId); var cached = await _blockRepository.GetNextBlock(prevHeader.BlockId); if (cached == null) @@ -244,7 +247,9 @@ private async void GetMissingBlocks(object state) } else { - await RecieveBlock(cached, false); + _logger.LogInformation("Have cached block"); + var recvTask = RecieveBlock(cached, true); + //GetMissingBlocks(null); } } } From a6934c8dc5c91f51492d97e88b24cd5b8bb8c7c6 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 16 Sep 2017 20:27:33 -0700 Subject: [PATCH 39/66] fork rebasing --- .../Services/Database/PersistedBlock.cs | 15 +- NBlockchain/Interfaces/IBlockReceiver.cs | 2 +- NBlockchain/Interfaces/IBlockRepository.cs | 2 +- NBlockchain/Interfaces/IForkRebaser.cs | 12 ++ NBlockchain/Models/BlockchainOptions.cs | 1 + .../{Interfaces => Rules}/IBlockRule.cs | 0 .../{Interfaces => Rules}/ITransactionRule.cs | 0 NBlockchain/Services/BlockMiner.cs | 4 +- .../Database/DefaultBlockRepository.cs | 134 ++++++++++++++---- NBlockchain/Services/DifficultyCalculator.cs | 2 +- NBlockchain/Services/ForkRebaser.cs | 67 +++++++++ .../Services/Net/InProcessPeerNetwork.cs | 4 +- NBlockchain/Services/Net/TcpPeerNetwork.cs | 2 +- NBlockchain/Services/NodeHost.cs | 90 +++--------- .../Services/MongoBlockRepository.cs | 3 +- Samples/DigitalCurrency/Program.cs | 2 +- .../NodeSync/NodeOnboardingScenarios.cs | 8 +- 17 files changed, 235 insertions(+), 113 deletions(-) create mode 100644 NBlockchain/Interfaces/IForkRebaser.cs rename NBlockchain/{Interfaces => Rules}/IBlockRule.cs (100%) rename NBlockchain/{Interfaces => Rules}/ITransactionRule.cs (100%) create mode 100644 NBlockchain/Services/ForkRebaser.cs diff --git a/NBlockChain/Services/Database/PersistedBlock.cs b/NBlockChain/Services/Database/PersistedBlock.cs index f5a2681..109ae11 100644 --- a/NBlockChain/Services/Database/PersistedBlock.cs +++ b/NBlockChain/Services/Database/PersistedBlock.cs @@ -20,6 +20,19 @@ public PersistedBlock(Block block) } } + public class PersistedOrphan : PersistedEntity + { + public PersistedOrphan() + { + } + + public PersistedOrphan(Block block) + { + Entity = block; + } + } + + public class BlockInfo { public BlockHeader Header { get; set; } @@ -27,7 +40,6 @@ public class BlockInfo public BlockInfo() { - } public BlockInfo(Block block) @@ -36,4 +48,5 @@ public BlockInfo(Block block) MerkleRootNode = block.MerkleRootNode; } } + } diff --git a/NBlockchain/Interfaces/IBlockReceiver.cs b/NBlockchain/Interfaces/IBlockReceiver.cs index a9a2614..e0f420e 100644 --- a/NBlockchain/Interfaces/IBlockReceiver.cs +++ b/NBlockchain/Interfaces/IBlockReceiver.cs @@ -8,7 +8,7 @@ namespace NBlockchain.Interfaces { public interface IBlockReceiver { - Task RecieveBlock(Block block, bool tip); + Task RecieveBlock(Block block); } public enum PeerDataResult { Ignore, Relay, Demerit } diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index 7db2939..d980981 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -11,7 +11,7 @@ public interface IBlockRepository Task HaveBlockMainChain(byte[] blockId); Task HaveBlockForkChain(byte[] blockId); Task IsEmpty(); - Task GetNewestBlockHeader(); + Task GetBestBlockHeader(); Task GetNextBlock(byte[] prevBlockId); Task GetBlockHeader(byte[] blockId); diff --git a/NBlockchain/Interfaces/IForkRebaser.cs b/NBlockchain/Interfaces/IForkRebaser.cs new file mode 100644 index 0000000..a6b3a76 --- /dev/null +++ b/NBlockchain/Interfaces/IForkRebaser.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using NBlockchain.Models; + +namespace NBlockchain.Interfaces +{ + public interface IForkRebaser + { + Task FindKnownForkbase(byte[] forkTipId); + Task RebaseChain(byte[] divergentId, byte[] targetTipId); + void RegisterBlockReceiver(IBlockReceiver blockReceiver); + } +} \ No newline at end of file diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 73eb26f..017fce7 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -178,6 +178,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); } diff --git a/NBlockchain/Interfaces/IBlockRule.cs b/NBlockchain/Rules/IBlockRule.cs similarity index 100% rename from NBlockchain/Interfaces/IBlockRule.cs rename to NBlockchain/Rules/IBlockRule.cs diff --git a/NBlockchain/Interfaces/ITransactionRule.cs b/NBlockchain/Rules/ITransactionRule.cs similarity index 100% rename from NBlockchain/Interfaces/ITransactionRule.cs rename to NBlockchain/Rules/ITransactionRule.cs diff --git a/NBlockchain/Services/BlockMiner.cs b/NBlockchain/Services/BlockMiner.cs index 50d29f9..6e39d0b 100644 --- a/NBlockchain/Services/BlockMiner.cs +++ b/NBlockchain/Services/BlockMiner.cs @@ -71,7 +71,7 @@ private async void Mine() while (!_buildCancelToken.IsCancellationRequested) { _blockCancelToken = new CancellationTokenSource(); - var prevHeader = await _blockRepository.GetNewestBlockHeader(); + var prevHeader = await _blockRepository.GetBestBlockHeader(); if (prevHeader == null) { if (!_buildGenesis) @@ -88,7 +88,7 @@ private async void Mine() { if (block.Header.Status == BlockStatus.Confirmed) { - var recvResult = await _blockReciever.RecieveBlock(block, true); + var recvResult = await _blockReciever.RecieveBlock(block); if (recvResult == PeerDataResult.Relay) _peerNetwork.BroadcastTail(block); } diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 6ecdfe0..107fc36 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -18,7 +18,7 @@ public class DefaultBlockRepository : IBlockRepository private readonly IAddressEncoder _addressEncoder; protected LiteCollection MainChain => _connection.Database.GetCollection("MainChain"); - protected LiteCollection ForkChain => _connection.Database.GetCollection("ForkChain"); + protected LiteCollection ForkChain => _connection.Database.GetCollection("ForkChain"); protected LiteCollection Instructions => _connection.Database.GetCollection("Instructions"); public DefaultBlockRepository(ILoggerFactory loggerFactory, IDataConnection connection, IAddressEncoder addressEncoder) @@ -61,6 +61,8 @@ public Task AddBlock(Block block) Instructions.InsertBulk(pt); } + ForkChain.Delete(x => x.Entity.Header.BlockId == block.Header.BlockId); + return Task.CompletedTask; } @@ -76,36 +78,45 @@ public Task IsEmpty() return Task.FromResult(count == 0); } - public async Task GetNewestBlockHeader() + public async Task GetBestBlockHeader() { if (await IsEmpty()) return null; var max = MainChain.Max(x => x.Entity.Header.Height).AsInt64; - var block = MainChain.Find(Query.EQ("Entity.Header.Height", max)).First(); + var block = MainChain.Find(Query.EQ("Entity.Header.Height", max)).FirstOrDefault(); return block?.Entity.Header; } public Task GetNextBlock(byte[] prevBlockId) { - var blockHeader = MainChain.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); + var persistedBlock = MainChain.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); + + if (persistedBlock == null) + { + var forkBlock = ForkChain.FindOne(x => x.Entity.Header.PreviousBlock == prevBlockId); + return Task.FromResult(forkBlock?.Entity); + } + var result = RehydratePersistedBlock(persistedBlock); - if (blockHeader == null) - return Task.FromResult(null); + return Task.FromResult(result); + } + private Block RehydratePersistedBlock(PersistedBlock persistedBlock) + { var result = new Block(); - result.Header = blockHeader.Entity.Header; - result.MerkleRootNode = blockHeader.Entity.MerkleRootNode; + result.Header = persistedBlock.Entity.Header; + result.MerkleRootNode = persistedBlock.Entity.MerkleRootNode; - var instructions = Instructions.Find(Query.EQ("BlockId", blockHeader.Entity.Header.BlockId)); + var instructions = Instructions.Find(Query.EQ("BlockId", persistedBlock.Entity.Header.BlockId)); result.Transactions = instructions .GroupBy(x => x.TransactionId, new ByteArrayEqualityComparer()) - .Select(x => new Transaction(x.Select(y => y.Entity).ToList()) { TransactionId = x.Key }) + .Select(x => new Transaction(x.Select(y => y.Entity).ToList()) { TransactionId = x.Key }) .ToList(); - return Task.FromResult(result); + return result; } - + public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) { var startTicks = startUtc.Ticks; @@ -119,49 +130,114 @@ public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) return Task.FromResult(result); } - public Task GetBlockHeader(byte[] blockId) + public async Task GetBlockHeader(byte[] blockId) { - throw new NotImplementedException(); + var block = MainChain.Find(Query.EQ("Entity.Header.BlockId", blockId)).FirstOrDefault(); + if (block == null) + { + var fork = ForkChain.Find(Query.EQ("Entity.Header.BlockId", blockId)).FirstOrDefault(); + return fork?.Entity.Header; + } + + return block?.Entity.Header; } - public Task GetBlock(byte[] blockId) + public async Task GetBlock(byte[] blockId) { - throw new NotImplementedException(); + var block = MainChain.Find(Query.EQ("Entity.Header.BlockId", blockId)).FirstOrDefault(); + if (block == null) + { + var fork = ForkChain.Find(Query.EQ("Entity.Header.BlockId", blockId)).FirstOrDefault(); + return fork?.Entity; + } + + return RehydratePersistedBlock(block); } - public Task GetMainChainHeader(uint height) + public async Task GetMainChainHeader(uint height) { - throw new NotImplementedException(); + //TODO: project result + var block = MainChain.Find(Query.EQ("Entity.Header.Height", Convert.ToInt64(height))).FirstOrDefault(); + return block?.Entity.Header; } - public Task GetForkHeader(byte[] forkBlockId) + public async Task GetForkHeader(byte[] forkBlockId) { - throw new NotImplementedException(); + //TODO: project result + var fork = ForkChain.Find(Query.EQ("Entity.Header.BlockId", forkBlockId)).FirstOrDefault(); + return fork?.Entity.Header; } - public Task AddDetachedBlock(Block block) + public async Task AddDetachedBlock(Block block) { - throw new NotImplementedException(); + ForkChain.Insert(new PersistedOrphan(block)); } - public Task GetDivergentHeader(byte[] forkTipBlockId) + public async Task GetDivergentHeader(byte[] forkTipBlockId) { - throw new NotImplementedException(); + var forkHeader = await GetForkHeader(forkTipBlockId); + if (forkHeader == null) + return null; + + while (!forkHeader.PreviousBlock.SequenceEqual(Block.HeadKey)) + { + var mainParent = MainChain.Find(x => x.Entity.Header.BlockId == forkHeader.PreviousBlock).FirstOrDefault(); + if (mainParent != null) + return mainParent.Entity.Header; + + forkHeader = await GetForkHeader(forkHeader.PreviousBlock); + if (forkHeader == null) + return null; + } + return null; } - public Task RewindChain(byte[] blockId) + public async Task RewindChain(byte[] blockId) { - throw new NotImplementedException(); + var divergent = MainChain.Find(x => x.Entity.Header.BlockId == blockId).FirstOrDefault(); + if (divergent == null) + return; + + var archiveFork = MainChain + .Find(x => x.Entity.Header.Height > divergent.Entity.Header.Height) + .ToList() + .Select(x => RehydratePersistedBlock(x)); + + foreach (var block in archiveFork.OrderByDescending(x => x.Header.Height)) + { + await AddDetachedBlock(block); + Instructions.Delete(x => x.BlockId == block.Header.BlockId); + MainChain.Delete(x => x.Entity.Header.BlockId == block.Header.BlockId); + } } - public Task> GetFork(byte[] forkTipBlockId) + public async Task> GetFork(byte[] forkTipBlockId) { - throw new NotImplementedException(); + var result = new List(); + + var forkBlock = ForkChain.Find(x => x.Entity.Header.BlockId == forkTipBlockId).FirstOrDefault(); + if (forkBlock == null) + return result; + + while (!forkBlock.Entity.Header.PreviousBlock.SequenceEqual(Block.HeadKey)) + { + result.Add(forkBlock.Entity); + var mainParent = MainChain.Find(x => x.Entity.Header.BlockId == forkBlock.Entity.Header.PreviousBlock).FirstOrDefault(); + if (mainParent != null) + break; + + forkBlock = ForkChain.Find(x => x.Entity.Header.BlockId == forkTipBlockId).FirstOrDefault(); + if (forkBlock == null) + break; + } + + return result.OrderBy(x => x.Header.Height).ToList(); } public Task HaveBlockForkChain(byte[] blockId) { - throw new NotImplementedException(); + var result = ForkChain.Exists(x => x.Entity.Header.BlockId == blockId); + return Task.FromResult(result); } } } \ No newline at end of file diff --git a/NBlockchain/Services/DifficultyCalculator.cs b/NBlockchain/Services/DifficultyCalculator.cs index 492f402..bca27be 100644 --- a/NBlockchain/Services/DifficultyCalculator.cs +++ b/NBlockchain/Services/DifficultyCalculator.cs @@ -24,7 +24,7 @@ public async Task CalculateDifficulty(long timestamp) { var end = new DateTime(timestamp + 1); var start = end.Subtract(_sampleInterval); - var latestHeader = await _blockRepository.GetNewestBlockHeader(); + var latestHeader = await _blockRepository.GetBestBlockHeader(); if (latestHeader == null) return _genesisValue; diff --git a/NBlockchain/Services/ForkRebaser.cs b/NBlockchain/Services/ForkRebaser.cs new file mode 100644 index 0000000..0211f91 --- /dev/null +++ b/NBlockchain/Services/ForkRebaser.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; +using NBlockchain.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NBlockchain.Services +{ + public class ForkRebaser : IForkRebaser + { + private readonly IBlockRepository _blockRepository; + private IBlockReceiver _blockReceiver; + private readonly ILogger _logger; + + public ForkRebaser(IBlockRepository blockRepository, ILoggerFactory loggerFactory) + { + _blockRepository = blockRepository; + _logger = loggerFactory.CreateLogger(); + } + + public async Task RebaseChain(byte[] divergentId, byte[] targetTipId) + { + _logger.LogInformation($"Rebasing chain from {BitConverter.ToString(divergentId)} to {BitConverter.ToString(targetTipId)}"); + var currentTipHeader = await _blockRepository.GetBestBlockHeader(); + await _blockRepository.RewindChain(divergentId); + var chainFork = await _blockRepository.GetFork(targetTipId); + + var ffwdTask = Task.Factory.StartNew(async () => + { + foreach (var forkedBlock in chainFork.OrderBy(x => x.Header.Height)) + { + try + { + await _blockReceiver.RecieveBlock(forkedBlock); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + } + } + }); + } + + + public async Task FindKnownForkbase(byte[] forkTipId) + { + _logger.LogInformation($"Searching for fork base"); + var header = await _blockRepository.GetForkHeader(forkTipId); + + var prevHeader = await _blockRepository.GetForkHeader(header.PreviousBlock); + while (prevHeader != null) + { + header = prevHeader; + prevHeader = await _blockRepository.GetForkHeader(prevHeader.PreviousBlock); + } + return header; + } + + public void RegisterBlockReceiver(IBlockReceiver blockReceiver) + { + _blockReceiver = blockReceiver; + } + } +} diff --git a/NBlockchain/Services/Net/InProcessPeerNetwork.cs b/NBlockchain/Services/Net/InProcessPeerNetwork.cs index d847dec..278123b 100644 --- a/NBlockchain/Services/Net/InProcessPeerNetwork.cs +++ b/NBlockchain/Services/Net/InProcessPeerNetwork.cs @@ -50,13 +50,13 @@ public void Close() public Action ReceiveBlock => (peer, block) => { - _blockReciever.RecieveBlock(block, false); + _blockReciever.RecieveBlock(block); }; public Action ReceiveTail => (peer, block) => { - _blockReciever.RecieveBlock(block, true); + _blockReciever.RecieveBlock(block); }; public Action ReceiveTransaction => (peer, txn) => diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index bac9c97..fc92441 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -257,7 +257,7 @@ private async Task ProcessBlock(byte[] data, Guid originId, bool tip) _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)} from {originId}"); var result = PeerDataResult.Ignore; - result = await _blockReciever.RecieveBlock(block, tip); + result = await _blockReciever.RecieveBlock(block); if ((tip) && (result == PeerDataResult.Relay)) { diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 3464b6e..1a92888 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -17,8 +17,8 @@ public class NodeHost : INodeHost private readonly IBlockRepository _blockRepository; private readonly IBlockVerifier _blockVerifier; private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; - private readonly IDateTimeProvider _dateTimeProvider; + + private readonly IForkRebaser _forkRebaser; //private readonly IExpectedBlockList _expectedBlockList; private readonly IPeerNetwork _peerNetwork; private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); @@ -28,13 +28,12 @@ public class NodeHost : INodeHost private readonly Timer _pollTimer; - public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, INetworkParameters parameters, IDateTimeProvider dateTimeProvider, IUnconfirmedTransactionCache unconfirmedTransactionCache, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) + public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, ILoggerFactory loggerFactory, IForkRebaser forkRebaser, INetworkParameters parameters, IUnconfirmedTransactionCache unconfirmedTransactionCache, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) { _blockRepository = blockRepository; - _blockVerifier = blockVerifier; - _serviceProvider = serviceProvider; + _blockVerifier = blockVerifier; _parameters = parameters; - _dateTimeProvider = dateTimeProvider; + _forkRebaser = forkRebaser; _unconfirmedTransactionCache = unconfirmedTransactionCache; _peerNetwork = peerNetwork; _difficultyCalculator = difficultyCalculator; @@ -43,17 +42,18 @@ public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, _peerNetwork.RegisterBlockReceiver(this); _peerNetwork.RegisterTransactionReceiver(this); + _forkRebaser.RegisterBlockReceiver(this); _pollTimer = new Timer(GetMissingBlocks, null, TimeSpan.FromSeconds(5), _parameters.BlockTime); } - public async Task RecieveBlock(Block block, bool tip) + public async Task RecieveBlock(Block block) { _blockEvent.WaitOne(); try { - _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)} {tip}"); + _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)}"); if (await _blockRepository.HaveBlockMainChain(block.Header.BlockId)) { @@ -74,10 +74,12 @@ public async Task RecieveBlock(Block block, bool tip) } var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); - var bestHeader = await _blockRepository.GetNewestBlockHeader(); + var bestHeader = await _blockRepository.GetBestBlockHeader(); var isEmpty = await _blockRepository.IsEmpty(); bool mainChain = false; bool rebaseChain = false; + var isTip = (block.Header.PreviousBlock.SequenceEqual(bestHeader?.BlockId ?? Block.HeadKey)); + _logger.LogInformation($"Is Tip {isTip}"); if (prevHeader != null) { @@ -93,10 +95,7 @@ public async Task RecieveBlock(Block block, bool tip) { _logger.LogInformation($"Height mismatch prev: {prevHeader.Height}, this: {block.Header.Height}"); return PeerDataResult.Ignore; - } - - var isTip = (prevHeader.BlockId.SequenceEqual(bestHeader.BlockId)); - _logger.LogInformation($"Is Tip {isTip}"); + } if (!isTip) { @@ -160,13 +159,12 @@ public async Task RecieveBlock(Block block, bool tip) if (divergentHeader != null) { _logger.LogInformation($"Rebasing chain from {divergentHeader.Height}"); - if (!await RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) - return PeerDataResult.Ignore; + await _forkRebaser.RebaseChain(divergentHeader.BlockId, block.Header.BlockId); } else { _logger.LogInformation($"Divergent block not found"); - var firstForkHeader = await FindKnownForkbase(block.Header.BlockId); + var firstForkHeader = await _forkRebaser.FindKnownForkbase(block.Header.BlockId); if (firstForkHeader != null) _peerNetwork.RequestBlock(firstForkHeader.PreviousBlock); } @@ -176,24 +174,21 @@ public async Task RecieveBlock(Block block, bool tip) //_expectedBlockList.Confirm(block.Header.PreviousBlock); //_expectedBlockList.ExpectNext(block.Header.BlockId); - if (tip) + if (isTip) { - _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); + _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); return PeerDataResult.Relay; } else { _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); + var missingBlockTask = Task.Factory.StartNew(() => GetMissingBlocks(null)); return PeerDataResult.Ignore; } } finally { - _blockEvent.Set(); - if (tip) - { - GetMissingBlocks(null); - } + _blockEvent.Set(); } } @@ -226,7 +221,7 @@ public async Task SendTransaction(Transaction transaction) private async void GetMissingBlocks(object state) { - var prevHeader = await _blockRepository.GetNewestBlockHeader(); + var prevHeader = await _blockRepository.GetBestBlockHeader(); if (prevHeader == null) { @@ -248,54 +243,11 @@ private async void GetMissingBlocks(object state) else { _logger.LogInformation("Have cached block"); - var recvTask = RecieveBlock(cached, true); + var recvTask = RecieveBlock(cached); //GetMissingBlocks(null); } } } - - private async Task RebaseChain(byte[] divergentId, byte[] targetTipId) - { - _logger.LogInformation($"Rebasing chain from {BitConverter.ToString(divergentId)} to {BitConverter.ToString(targetTipId)}"); - var currentTipHeader = await _blockRepository.GetNewestBlockHeader(); - await _blockRepository.RewindChain(divergentId); - var chainFork = await _blockRepository.GetFork(targetTipId); - foreach (var forkedBlock in chainFork.OrderBy(x => x.Header.Height)) - { - var prevHeader = await _blockRepository.GetBlockHeader(forkedBlock.Header.PreviousBlock); - var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); - - if (forkedBlock.Header.Difficulty < expectedDifficulty) - { - _logger.LogWarning($"Rebase failed on expected difficulty {BitConverter.ToString(forkedBlock.Header.BlockId)}"); - await RebaseChain(divergentId, currentTipHeader.BlockId); - return false; - } - - if (!await _blockVerifier.VerifyTransactions(forkedBlock)) - { - _logger.LogWarning($"Rebase failed on block txn verification {BitConverter.ToString(forkedBlock.Header.BlockId)}"); - await RebaseChain(divergentId, currentTipHeader.BlockId); - return false; - } - await _blockRepository.AddBlock(forkedBlock); - } - return true; - } - - - private async Task FindKnownForkbase(byte[] forkTipId) - { - _logger.LogInformation($"Searching for fork base"); - var header = await _blockRepository.GetForkHeader(forkTipId); - - var prevHeader = await _blockRepository.GetForkHeader(header.PreviousBlock); - while (prevHeader != null) - { - header = prevHeader; - prevHeader = await _blockRepository.GetForkHeader(prevHeader.PreviousBlock); - } - return header; - } + } } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index 9bfe9aa..edadf1e 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -71,6 +71,7 @@ public async Task AddBlock(Block block) persisted.Statistics.BlockTime = Convert.ToInt32(TimeSpan.FromTicks(block.Header.Timestamp - prevHeader.Timestamp).TotalSeconds); MainChain.InsertOne(persisted); + ForkChain.DeleteMany(x => x.Header.BlockId == block.Header.BlockId); await Task.Yield(); } @@ -92,7 +93,7 @@ public Task IsEmpty() return Task.FromResult(MainChain.Count(x => true) == 0); } - public Task GetNewestBlockHeader() + public Task GetBestBlockHeader() { if (MainChain.Count(x => true) == 0) return Task.FromResult(null); diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index a150bf7..6661118 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -167,7 +167,7 @@ static void RunCommand(string command, KeyPair keys) Console.WriteLine($"Balance = {_txnRepo.GetAccountBalance(args[1])}"); break; case "best-block": - var header = _blockRepo.GetNewestBlockHeader().Result; + var header = _blockRepo.GetBestBlockHeader().Result; Console.WriteLine($"Height: {header.Height}, Id: {BitConverter.ToString(header.BlockId)}"); break; case "gen-key": diff --git a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs index 9f6833c..5eb23ba 100644 --- a/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs +++ b/Tests/NBlockchain.Tests.Scenarios/NodeSync/NodeOnboardingScenarios.cs @@ -80,12 +80,12 @@ public async void should_sync_data_over_mesh() net2.Open(); net3.Open(); - var target = await repo1.GetNewestBlockHeader(); + var target = await repo1.GetBestBlockHeader(); var timeOut = DateTime.Now.AddSeconds(30); while (timeOut > DateTime.Now) { await Task.Delay(500); - var header3 = await repo3.GetNewestBlockHeader(); + var header3 = await repo3.GetBestBlockHeader(); if (header3?.Height == target.Height) break; } @@ -94,8 +94,8 @@ public async void should_sync_data_over_mesh() net2.Close(); net3.Close(); - var last2 = await repo2.GetNewestBlockHeader(); - var last3 = await repo3.GetNewestBlockHeader(); + var last2 = await repo2.GetBestBlockHeader(); + var last3 = await repo3.GetBestBlockHeader(); last2.Height.Should().Be(target.Height); last3.Height.Should().Be(target.Height); From e460fbe907d4d2d77ba75d9c3ef9351ace37efe9 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 17 Sep 2017 07:27:49 -0700 Subject: [PATCH 40/66] tweaks --- NBlockchain/Services/NodeHost.cs | 27 ++++++++++++++------------- Samples/DigitalCurrency/Program.cs | 4 ++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 1a92888..b679704 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -50,6 +50,7 @@ public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, public async Task RecieveBlock(Block block) { + var isTip = false; _blockEvent.WaitOne(); try { @@ -78,7 +79,7 @@ public async Task RecieveBlock(Block block) var isEmpty = await _blockRepository.IsEmpty(); bool mainChain = false; bool rebaseChain = false; - var isTip = (block.Header.PreviousBlock.SequenceEqual(bestHeader?.BlockId ?? Block.HeadKey)); + isTip = (block.Header.PreviousBlock.SequenceEqual(bestHeader?.BlockId ?? Block.HeadKey)); _logger.LogInformation($"Is Tip {isTip}"); if (prevHeader != null) @@ -173,23 +174,23 @@ public async Task RecieveBlock(Block block) //_expectedBlockList.Confirm(block.Header.PreviousBlock); //_expectedBlockList.ExpectNext(block.Header.BlockId); - - if (isTip) - { - _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Relay; - } - else - { - _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); - var missingBlockTask = Task.Factory.StartNew(() => GetMissingBlocks(null)); - return PeerDataResult.Ignore; - } + } finally { _blockEvent.Set(); } + if (isTip) + { + _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Relay; + } + else + { + _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); + var missingBlockTask = Task.Factory.StartNew(() => GetMissingBlocks(null)); + return PeerDataResult.Ignore; + } } public async Task RecieveTransaction(Transaction transaction) diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 6661118..cd5647d 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -27,8 +27,8 @@ class Program static void Main(string[] args) { - var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); - //var serviceProvider = ConfigureForLiteDb("node.db", 10500); + //var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); + var serviceProvider = ConfigureForLiteDb("node.db", 10500); _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); From b5a1e32deb2af3cd80563bac7eeec9f6f13fbdcd Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 17 Sep 2017 08:13:52 -0700 Subject: [PATCH 41/66] tweaks --- NBlockchain/Services/NodeHost.cs | 4 +++- Samples/DigitalCurrency/Program.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index b679704..74e292c 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -180,6 +180,7 @@ public async Task RecieveBlock(Block block) { _blockEvent.Set(); } + var missingBlockTask = Task.Factory.StartNew(() => GetMissingBlocks(null)); if (isTip) { _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); @@ -188,7 +189,6 @@ public async Task RecieveBlock(Block block) else { _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); - var missingBlockTask = Task.Factory.StartNew(() => GetMissingBlocks(null)); return PeerDataResult.Ignore; } } @@ -222,6 +222,8 @@ public async Task SendTransaction(Transaction transaction) private async void GetMissingBlocks(object state) { + _logger.LogInformation("GetMissingBlocks"); + var prevHeader = await _blockRepository.GetBestBlockHeader(); if (prevHeader == null) diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index cd5647d..6661118 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -27,8 +27,8 @@ class Program static void Main(string[] args) { - //var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); - var serviceProvider = ConfigureForLiteDb("node.db", 10500); + var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); + //var serviceProvider = ConfigureForLiteDb("node.db", 10500); _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); From 650afd3ec394ea8f29966e1373c98344072ce58a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 17 Sep 2017 10:54:19 -0700 Subject: [PATCH 42/66] tweaks --- NBlockchain/Interfaces/IInboundQueue.cs | 10 ++++++++ NBlockchain/Services/ForkRebaser.cs | 30 +++++++++++------------ NBlockchain/Services/InboundBlockQueue.cs | 12 +++++++++ NBlockchain/Services/InboundQueue.cs | 27 ++++++++++++++++++++ NBlockchain/Services/NodeHost.cs | 8 ++++-- 5 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 NBlockchain/Interfaces/IInboundQueue.cs create mode 100644 NBlockchain/Services/InboundBlockQueue.cs create mode 100644 NBlockchain/Services/InboundQueue.cs diff --git a/NBlockchain/Interfaces/IInboundQueue.cs b/NBlockchain/Interfaces/IInboundQueue.cs new file mode 100644 index 0000000..227c8d6 --- /dev/null +++ b/NBlockchain/Interfaces/IInboundQueue.cs @@ -0,0 +1,10 @@ +using NBlockchain.Models; + +namespace NBlockchain.Interfaces +{ + public interface IInboundBlockQueue + { + Block Dequeue(); + void Enqueue(Block data); + } +} \ No newline at end of file diff --git a/NBlockchain/Services/ForkRebaser.cs b/NBlockchain/Services/ForkRebaser.cs index 0211f91..4aa76bb 100644 --- a/NBlockchain/Services/ForkRebaser.cs +++ b/NBlockchain/Services/ForkRebaser.cs @@ -26,22 +26,22 @@ public async Task RebaseChain(byte[] divergentId, byte[] targetTipId) _logger.LogInformation($"Rebasing chain from {BitConverter.ToString(divergentId)} to {BitConverter.ToString(targetTipId)}"); var currentTipHeader = await _blockRepository.GetBestBlockHeader(); await _blockRepository.RewindChain(divergentId); - var chainFork = await _blockRepository.GetFork(targetTipId); + //var chainFork = await _blockRepository.GetFork(targetTipId); - var ffwdTask = Task.Factory.StartNew(async () => - { - foreach (var forkedBlock in chainFork.OrderBy(x => x.Header.Height)) - { - try - { - await _blockReceiver.RecieveBlock(forkedBlock); - } - catch (Exception ex) - { - _logger.LogError(ex.Message); - } - } - }); + //var ffwdTask = Task.Factory.StartNew(async () => + //{ + // foreach (var forkedBlock in chainFork.OrderBy(x => x.Header.Height)) + // { + // try + // { + // await _blockReceiver.RecieveBlock(forkedBlock); + // } + // catch (Exception ex) + // { + // _logger.LogError(ex.Message); + // } + // } + //}); } diff --git a/NBlockchain/Services/InboundBlockQueue.cs b/NBlockchain/Services/InboundBlockQueue.cs new file mode 100644 index 0000000..a7f9614 --- /dev/null +++ b/NBlockchain/Services/InboundBlockQueue.cs @@ -0,0 +1,12 @@ +using NBlockchain.Interfaces; +using NBlockchain.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services +{ + public class InboundBlockQueue : InboundQueue, IInboundBlockQueue + { + } +} diff --git a/NBlockchain/Services/InboundQueue.cs b/NBlockchain/Services/InboundQueue.cs new file mode 100644 index 0000000..c438364 --- /dev/null +++ b/NBlockchain/Services/InboundQueue.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services +{ + public abstract class InboundQueue + where T : class + { + private ConcurrentQueue _queue = new ConcurrentQueue(); + + public void Enqueue(T data) + { + _queue.Enqueue(data); + } + + public T Dequeue() + { + if (_queue.TryDequeue(out var result)) + return result; + + return null; + } + + } +} diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 74e292c..4a4a9cd 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -50,8 +50,12 @@ public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, public async Task RecieveBlock(Block block) { - var isTip = false; - _blockEvent.WaitOne(); + var isTip = false; + if (!_blockEvent.WaitOne(TimeSpan.FromSeconds(30))) + { + _logger.LogError($"Timeout waiting for block lock event"); + return PeerDataResult.Ignore; + } try { _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)}"); From 26124bf8ba86ea9e0bf6c25a101834dbc5e59897 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 17 Sep 2017 14:09:11 -0700 Subject: [PATCH 43/66] Update README.md --- Providers/NBlockchain.MongoDB/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Providers/NBlockchain.MongoDB/README.md b/Providers/NBlockchain.MongoDB/README.md index cca611f..dd9990b 100644 --- a/Providers/NBlockchain.MongoDB/README.md +++ b/Providers/NBlockchain.MongoDB/README.md @@ -14,10 +14,10 @@ dotnet add package NBlockchain.MongoDB --version 0.1.0-alpha ## Usage ```c# -services.AddBlockchain(x => -{ - x.UseMongoDB(@"mongodb://localhost:27017", "my-blockchain-db") - .UseTransactionRepository(); +services.AddBlockchain(blockchain => +{ + blockchain.UseMongoDB(@"mongodb://localhost:27017", "my-blockchain-db") + .UseInstructionRepository(); ... } -``` \ No newline at end of file +``` From 128f7ed1cd7c93cce0033725bb7576ab124a9154 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 17 Sep 2017 15:27:12 -0700 Subject: [PATCH 44/66] refactor --- NBlockchain/Interfaces/IBlockReceiver.cs | 15 ----------- NBlockchain/Interfaces/IForkRebaser.cs | 1 - NBlockchain/Interfaces/IInboundQueue.cs | 10 ------- NBlockchain/Interfaces/INodeHost.cs | 2 +- NBlockchain/Interfaces/IPeerNetwork.cs | 6 +---- NBlockchain/Interfaces/IReceiver.cs | 21 +++++++++++++++ .../Interfaces/ITransactionReceiver.cs | 13 --------- NBlockchain/Models/BlockchainOptions.cs | 6 ++--- NBlockchain/Services/BlockMiner.cs | 4 +-- NBlockchain/Services/ForkRebaser.cs | 8 ++---- NBlockchain/Services/InboundBlockQueue.cs | 12 --------- NBlockchain/Services/InboundQueue.cs | 27 ------------------- .../Services/Net/InProcessPeerNetwork.cs | 23 +++++----------- NBlockchain/Services/Net/TcpPeerNetwork.cs | 21 ++++----------- NBlockchain/Services/NodeHost.cs | 24 ++++++++--------- NBlockchain/Services/Receiver.cs | 25 +++++++++++++++++ 16 files changed, 79 insertions(+), 139 deletions(-) delete mode 100644 NBlockchain/Interfaces/IBlockReceiver.cs delete mode 100644 NBlockchain/Interfaces/IInboundQueue.cs create mode 100644 NBlockchain/Interfaces/IReceiver.cs delete mode 100644 NBlockchain/Interfaces/ITransactionReceiver.cs delete mode 100644 NBlockchain/Services/InboundBlockQueue.cs delete mode 100644 NBlockchain/Services/InboundQueue.cs create mode 100644 NBlockchain/Services/Receiver.cs diff --git a/NBlockchain/Interfaces/IBlockReceiver.cs b/NBlockchain/Interfaces/IBlockReceiver.cs deleted file mode 100644 index e0f420e..0000000 --- a/NBlockchain/Interfaces/IBlockReceiver.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using NBlockchain.Models; - -namespace NBlockchain.Interfaces -{ - public interface IBlockReceiver - { - Task RecieveBlock(Block block); - } - - public enum PeerDataResult { Ignore, Relay, Demerit } -} diff --git a/NBlockchain/Interfaces/IForkRebaser.cs b/NBlockchain/Interfaces/IForkRebaser.cs index a6b3a76..84ad813 100644 --- a/NBlockchain/Interfaces/IForkRebaser.cs +++ b/NBlockchain/Interfaces/IForkRebaser.cs @@ -7,6 +7,5 @@ public interface IForkRebaser { Task FindKnownForkbase(byte[] forkTipId); Task RebaseChain(byte[] divergentId, byte[] targetTipId); - void RegisterBlockReceiver(IBlockReceiver blockReceiver); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/IInboundQueue.cs b/NBlockchain/Interfaces/IInboundQueue.cs deleted file mode 100644 index 227c8d6..0000000 --- a/NBlockchain/Interfaces/IInboundQueue.cs +++ /dev/null @@ -1,10 +0,0 @@ -using NBlockchain.Models; - -namespace NBlockchain.Interfaces -{ - public interface IInboundBlockQueue - { - Block Dequeue(); - void Enqueue(Block data); - } -} \ No newline at end of file diff --git a/NBlockchain/Interfaces/INodeHost.cs b/NBlockchain/Interfaces/INodeHost.cs index d6b235d..c769232 100644 --- a/NBlockchain/Interfaces/INodeHost.cs +++ b/NBlockchain/Interfaces/INodeHost.cs @@ -3,7 +3,7 @@ namespace NBlockchain.Interfaces { - public interface INodeHost : IBlockReceiver, ITransactionReceiver + public interface INodeHost { Task SendTransaction(Transaction transaction); } diff --git a/NBlockchain/Interfaces/IPeerNetwork.cs b/NBlockchain/Interfaces/IPeerNetwork.cs index 80655bd..a3b9359 100644 --- a/NBlockchain/Interfaces/IPeerNetwork.cs +++ b/NBlockchain/Interfaces/IPeerNetwork.cs @@ -16,11 +16,7 @@ public interface IPeerNetwork void RequestNextBlock(byte[] blockId); - void RequestBlock(byte[] blockId); - - void RegisterBlockReceiver(IBlockReceiver blockReceiver); - - void RegisterTransactionReceiver(ITransactionReceiver transactionReciever); + void RequestBlock(byte[] blockId); Task DiscoverPeers(); diff --git a/NBlockchain/Interfaces/IReceiver.cs b/NBlockchain/Interfaces/IReceiver.cs new file mode 100644 index 0000000..9860459 --- /dev/null +++ b/NBlockchain/Interfaces/IReceiver.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using NBlockchain.Models; + +namespace NBlockchain.Interfaces +{ + public interface IReceiver + { + Task RecieveBlock(Block block); + Task RecieveTransaction(Transaction transaction); + event ReceiveBlock OnReceiveBlock; + event RecieveTransaction OnRecieveTransaction; + } + + public enum PeerDataResult { Ignore, Relay, Demerit } + + public delegate Task ReceiveBlock(Block block); + public delegate Task RecieveTransaction(Transaction transaction); +} diff --git a/NBlockchain/Interfaces/ITransactionReceiver.cs b/NBlockchain/Interfaces/ITransactionReceiver.cs deleted file mode 100644 index baed689..0000000 --- a/NBlockchain/Interfaces/ITransactionReceiver.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using NBlockchain.Models; - -namespace NBlockchain.Interfaces -{ - public interface ITransactionReceiver - { - Task RecieveTransaction(Transaction transaction); - } -} diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 017fce7..2cf4f40 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -68,7 +68,7 @@ public void UseBlockRepository(Func factory) public void UseTcpPeerNetwork(uint port) { - Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService())); + Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); } public void UseDataConnection(string connectionString) @@ -166,8 +166,8 @@ internal void FillDefaults() //AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton, sp => sp.GetService()); - AddDefault(ServiceLifetime.Singleton, sp => sp.GetService()); + AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); diff --git a/NBlockchain/Services/BlockMiner.cs b/NBlockchain/Services/BlockMiner.cs index 6e39d0b..e90ea82 100644 --- a/NBlockchain/Services/BlockMiner.cs +++ b/NBlockchain/Services/BlockMiner.cs @@ -25,7 +25,7 @@ public class BlockMiner : IBlockMiner private readonly IUnconfirmedTransactionCache _unconfirmedTransactionCache; private readonly IBlockRepository _blockRepository; private readonly IPeerNetwork _peerNetwork; - private readonly IBlockReceiver _blockReciever; + private readonly IReceiver _blockReciever; private readonly IDifficultyCalculator _difficultyCalculator; private KeyPair _builderKeys; @@ -36,7 +36,7 @@ public class BlockMiner : IBlockMiner private CancellationTokenSource _blockCancelToken; - public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IUnconfirmedTransactionCache unconfirmedTransactionCache, IBlockRepository blockRepository, IBlockReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) + public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IUnconfirmedTransactionCache unconfirmedTransactionCache, IBlockRepository blockRepository, IReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) { _networkParameters = networkParameters; _peerNetwork= peerNetwork; diff --git a/NBlockchain/Services/ForkRebaser.cs b/NBlockchain/Services/ForkRebaser.cs index 4aa76bb..079fecd 100644 --- a/NBlockchain/Services/ForkRebaser.cs +++ b/NBlockchain/Services/ForkRebaser.cs @@ -12,7 +12,7 @@ namespace NBlockchain.Services public class ForkRebaser : IForkRebaser { private readonly IBlockRepository _blockRepository; - private IBlockReceiver _blockReceiver; + //private IReceiver _blockReceiver; private readonly ILogger _logger; public ForkRebaser(IBlockRepository blockRepository, ILoggerFactory loggerFactory) @@ -58,10 +58,6 @@ public async Task FindKnownForkbase(byte[] forkTipId) } return header; } - - public void RegisterBlockReceiver(IBlockReceiver blockReceiver) - { - _blockReceiver = blockReceiver; - } + } } diff --git a/NBlockchain/Services/InboundBlockQueue.cs b/NBlockchain/Services/InboundBlockQueue.cs deleted file mode 100644 index a7f9614..0000000 --- a/NBlockchain/Services/InboundBlockQueue.cs +++ /dev/null @@ -1,12 +0,0 @@ -using NBlockchain.Interfaces; -using NBlockchain.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Services -{ - public class InboundBlockQueue : InboundQueue, IInboundBlockQueue - { - } -} diff --git a/NBlockchain/Services/InboundQueue.cs b/NBlockchain/Services/InboundQueue.cs deleted file mode 100644 index c438364..0000000 --- a/NBlockchain/Services/InboundQueue.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Services -{ - public abstract class InboundQueue - where T : class - { - private ConcurrentQueue _queue = new ConcurrentQueue(); - - public void Enqueue(T data) - { - _queue.Enqueue(data); - } - - public T Dequeue() - { - if (_queue.TryDequeue(out var result)) - return result; - - return null; - } - - } -} diff --git a/NBlockchain/Services/Net/InProcessPeerNetwork.cs b/NBlockchain/Services/Net/InProcessPeerNetwork.cs index 278123b..96cf80b 100644 --- a/NBlockchain/Services/Net/InProcessPeerNetwork.cs +++ b/NBlockchain/Services/Net/InProcessPeerNetwork.cs @@ -12,27 +12,18 @@ public class InProcessPeerNetwork : IPeerNetwork, IDisposable private static readonly IList Peers = new List(); private readonly IBlockRepository _blockRepository; - private IBlockReceiver _blockReciever; - private ITransactionReceiver _transactionReciever; + private IReceiver _reciever; public Guid NodeId { get; private set; } - public InProcessPeerNetwork(IBlockRepository blockRepository) + public InProcessPeerNetwork(IBlockRepository blockRepository, IReceiver reciever) { _blockRepository = blockRepository; + _reciever = reciever; NodeId = Guid.NewGuid(); Peers.Add(this); } - - public void RegisterBlockReceiver(IBlockReceiver blockReceiver) - { - _blockReciever = blockReceiver; - } - - public void RegisterTransactionReceiver(ITransactionReceiver transactionReciever) - { - _transactionReciever = transactionReciever; - } + #pragma warning disable CS1998 public async Task DiscoverPeers() @@ -50,18 +41,18 @@ public void Close() public Action ReceiveBlock => (peer, block) => { - _blockReciever.RecieveBlock(block); + _reciever.RecieveBlock(block); }; public Action ReceiveTail => (peer, block) => { - _blockReciever.RecieveBlock(block); + _reciever.RecieveBlock(block); }; public Action ReceiveTransaction => (peer, txn) => { - _transactionReciever.RecieveTransaction(txn); + _reciever.RecieveTransaction(txn); }; public Action ReceiveBlockRequest => async (peer, txn) => diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index fc92441..5b0c3a5 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -24,8 +24,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly uint _port; private byte[] _serviceId = new byte[] { 0x0, 0x1 }; - private IBlockReceiver _blockReciever; - private ITransactionReceiver _transactionReciever; + private readonly IReceiver _reciever; private readonly IBlockRepository _blockRepository; private readonly IEnumerable _discoveryServices; @@ -52,9 +51,10 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable public Guid NodeId { get; private set; } - public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IUnconfirmedTransactionCache unconfirmedTransactionCache) + public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IUnconfirmedTransactionCache unconfirmedTransactionCache, IReceiver reciever) { _port = port; + _reciever = reciever; _logger = loggerFactory.CreateLogger(); _blockRepository = blockRepository; _discoveryServices = discoveryServices; @@ -257,7 +257,7 @@ private async Task ProcessBlock(byte[] data, Guid originId, bool tip) _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)} from {originId}"); var result = PeerDataResult.Ignore; - result = await _blockReciever.RecieveBlock(block); + result = await _reciever.RecieveBlock(block); if ((tip) && (result == PeerDataResult.Relay)) { @@ -275,7 +275,7 @@ private async Task ProcessBlock(byte[] data, Guid originId, bool tip) private async Task ProcessTransaction(byte[] data, Guid originId) { var txn = DeserializeObject(data); - var result = await _transactionReciever.RecieveTransaction(txn); + var result = await _reciever.RecieveTransaction(txn); if (result == PeerDataResult.Relay) { @@ -430,18 +430,7 @@ private async Task ConnectOut() _connectOutEvent.Set(); } } - - public void RegisterBlockReceiver(IBlockReceiver blockReceiver) - { - _blockReciever = blockReceiver; - } - - public void RegisterTransactionReceiver(ITransactionReceiver transactionReciever) - { - _transactionReciever = transactionReciever; - } - public void BroadcastTail(Block block) { var data = SerializeObject(block); diff --git a/NBlockchain/Services/NodeHost.cs b/NBlockchain/Services/NodeHost.cs index 4a4a9cd..02dad38 100644 --- a/NBlockchain/Services/NodeHost.cs +++ b/NBlockchain/Services/NodeHost.cs @@ -19,6 +19,7 @@ public class NodeHost : INodeHost private readonly ILogger _logger; private readonly IForkRebaser _forkRebaser; + private readonly IReceiver _receiver; //private readonly IExpectedBlockList _expectedBlockList; private readonly IPeerNetwork _peerNetwork; private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); @@ -28,10 +29,11 @@ public class NodeHost : INodeHost private readonly Timer _pollTimer; - public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, ILoggerFactory loggerFactory, IForkRebaser forkRebaser, INetworkParameters parameters, IUnconfirmedTransactionCache unconfirmedTransactionCache, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) + public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, IReceiver receiver, ILoggerFactory loggerFactory, IForkRebaser forkRebaser, INetworkParameters parameters, IUnconfirmedTransactionCache unconfirmedTransactionCache, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) { _blockRepository = blockRepository; - _blockVerifier = blockVerifier; + _blockVerifier = blockVerifier; + _receiver = receiver; _parameters = parameters; _forkRebaser = forkRebaser; _unconfirmedTransactionCache = unconfirmedTransactionCache; @@ -40,15 +42,13 @@ public NodeHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, //_expectedBlockList = expectedBlockList; _logger = loggerFactory.CreateLogger(); - _peerNetwork.RegisterBlockReceiver(this); - _peerNetwork.RegisterTransactionReceiver(this); - _forkRebaser.RegisterBlockReceiver(this); - + _receiver.OnReceiveBlock += OnRecieveBlock; + _receiver.OnRecieveTransaction += OnRecieveTransaction; + _pollTimer = new Timer(GetMissingBlocks, null, TimeSpan.FromSeconds(5), _parameters.BlockTime); } - - - public async Task RecieveBlock(Block block) + + private async Task OnRecieveBlock(Block block) { var isTip = false; if (!_blockEvent.WaitOne(TimeSpan.FromSeconds(30))) @@ -197,7 +197,7 @@ public async Task RecieveBlock(Block block) } } - public async Task RecieveTransaction(Transaction transaction) + private async Task OnRecieveTransaction(Transaction transaction) { _logger.LogDebug($"Recv txn {BitConverter.ToString(transaction.TransactionId)}"); var txnResult = await _blockVerifier.VerifyTransaction(transaction, _unconfirmedTransactionCache.Get); @@ -220,7 +220,7 @@ public async Task RecieveTransaction(Transaction transaction) public async Task SendTransaction(Transaction transaction) { _logger.LogDebug("Sending txn"); - await RecieveTransaction(transaction); + await _receiver.RecieveTransaction(transaction); _peerNetwork.BroadcastTransaction(transaction); } @@ -250,7 +250,7 @@ private async void GetMissingBlocks(object state) else { _logger.LogInformation("Have cached block"); - var recvTask = RecieveBlock(cached); + var recvTask = _receiver.RecieveBlock(cached); //GetMissingBlocks(null); } } diff --git a/NBlockchain/Services/Receiver.cs b/NBlockchain/Services/Receiver.cs new file mode 100644 index 0000000..1c3b890 --- /dev/null +++ b/NBlockchain/Services/Receiver.cs @@ -0,0 +1,25 @@ +using NBlockchain.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; +using NBlockchain.Models; +using System.Threading.Tasks; + +namespace NBlockchain.Services +{ + public class Receiver : IReceiver + { + public event ReceiveBlock OnReceiveBlock; + public event RecieveTransaction OnRecieveTransaction; + + public Task RecieveBlock(Block block) + { + return OnReceiveBlock?.Invoke(block); + } + + public Task RecieveTransaction(Transaction transaction) + { + return OnRecieveTransaction?.Invoke(transaction); + } + } +} From 3c5d295c59900bd5b4dc7412493f66de1bee9251 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 18 Sep 2017 19:16:11 -0700 Subject: [PATCH 45/66] refactor --- NBlockchain/Interfaces/IBlockRepository.cs | 13 +- .../{INodeHost.cs => IBlockchainHost.cs} | 2 +- NBlockchain/Interfaces/IForkRebaser.cs | 5 +- ...ache.cs => IUnconfirmedTransactionPool.cs} | 2 +- NBlockchain/Models/BlockchainOptions.cs | 8 +- .../Rules/BlockContentThresholdRule.cs | 4 +- NBlockchain/Services/BlockMiner.cs | 12 +- NBlockchain/Services/BlockchainHost.cs | 280 ++++++++++++++++++ .../Database/DefaultBlockRepository.cs | 24 +- NBlockchain/Services/ForkRebaser.cs | 26 +- NBlockchain/Services/Net/TcpPeerNetwork.cs | 8 +- NBlockchain/Services/NodeHost.cs | 260 ---------------- ...Cache.cs => UnconfirmedTransactionPool.cs} | 2 +- .../Services/MongoBlockRepository.cs | 22 +- Samples/DigitalCurrency/Program.cs | 4 +- 15 files changed, 346 insertions(+), 326 deletions(-) rename NBlockchain/Interfaces/{INodeHost.cs => IBlockchainHost.cs} (81%) rename NBlockchain/Interfaces/{IUnconfirmedTransactionCache.cs => IUnconfirmedTransactionPool.cs} (86%) create mode 100644 NBlockchain/Services/BlockchainHost.cs delete mode 100644 NBlockchain/Services/NodeHost.cs rename NBlockchain/Services/{UnconfirmedTransactionCache.cs => UnconfirmedTransactionPool.cs} (96%) diff --git a/NBlockchain/Interfaces/IBlockRepository.cs b/NBlockchain/Interfaces/IBlockRepository.cs index d980981..d377805 100644 --- a/NBlockchain/Interfaces/IBlockRepository.cs +++ b/NBlockchain/Interfaces/IBlockRepository.cs @@ -8,8 +8,8 @@ namespace NBlockchain.Interfaces public interface IBlockRepository { Task AddBlock(Block block); - Task HaveBlockMainChain(byte[] blockId); - Task HaveBlockForkChain(byte[] blockId); + Task HavePrimaryBlock(byte[] blockId); + Task HaveSecondaryBlock(byte[] blockId); Task IsEmpty(); Task GetBestBlockHeader(); Task GetNextBlock(byte[] prevBlockId); @@ -17,17 +17,18 @@ public interface IBlockRepository Task GetBlockHeader(byte[] blockId); Task GetBlock(byte[] blockId); - Task GetMainChainHeader(uint height); - Task GetForkHeader(byte[] forkBlockId); + Task GetPrimaryHeader(uint height); + Task GetSecondaryHeader(byte[] forkBlockId); - Task AddDetachedBlock(Block block); + Task AddSecondaryBlock(Block block); Task GetDivergentHeader(byte[] forkTipBlockId); Task RewindChain(byte[] blockId); Task> GetFork(byte[] forkTipBlockId); - + + Task DiscardSecondaryBlock(byte[] blockId); Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc); diff --git a/NBlockchain/Interfaces/INodeHost.cs b/NBlockchain/Interfaces/IBlockchainHost.cs similarity index 81% rename from NBlockchain/Interfaces/INodeHost.cs rename to NBlockchain/Interfaces/IBlockchainHost.cs index c769232..a1e643b 100644 --- a/NBlockchain/Interfaces/INodeHost.cs +++ b/NBlockchain/Interfaces/IBlockchainHost.cs @@ -3,7 +3,7 @@ namespace NBlockchain.Interfaces { - public interface INodeHost + public interface IBlockchainHost { Task SendTransaction(Transaction transaction); } diff --git a/NBlockchain/Interfaces/IForkRebaser.cs b/NBlockchain/Interfaces/IForkRebaser.cs index 84ad813..629f551 100644 --- a/NBlockchain/Interfaces/IForkRebaser.cs +++ b/NBlockchain/Interfaces/IForkRebaser.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; using NBlockchain.Models; namespace NBlockchain.Interfaces @@ -6,6 +7,6 @@ namespace NBlockchain.Interfaces public interface IForkRebaser { Task FindKnownForkbase(byte[] forkTipId); - Task RebaseChain(byte[] divergentId, byte[] targetTipId); + Task> RebaseChain(byte[] divergentId, byte[] targetTipId); } } \ No newline at end of file diff --git a/NBlockchain/Interfaces/IUnconfirmedTransactionCache.cs b/NBlockchain/Interfaces/IUnconfirmedTransactionPool.cs similarity index 86% rename from NBlockchain/Interfaces/IUnconfirmedTransactionCache.cs rename to NBlockchain/Interfaces/IUnconfirmedTransactionPool.cs index cd3b68c..ae6f7e5 100644 --- a/NBlockchain/Interfaces/IUnconfirmedTransactionCache.cs +++ b/NBlockchain/Interfaces/IUnconfirmedTransactionPool.cs @@ -5,7 +5,7 @@ namespace NBlockchain.Interfaces { - public interface IUnconfirmedTransactionCache + public interface IUnconfirmedTransactionPool { event EventHandler Changed; diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 2cf4f40..e9905e9 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -68,7 +68,7 @@ public void UseBlockRepository(Func factory) public void UseTcpPeerNetwork(uint port) { - Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); + Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); } public void UseDataConnection(string connectionString) @@ -112,7 +112,7 @@ public void AddPeerDiscovery(Func facto public void AddContentThresholdBlockRule(decimal threshold) { - Services.AddTransient(typeof(IBlockRule), sp => new BlockContentThresholdRule(sp.GetService(), threshold)); + Services.AddTransient(typeof(IBlockRule), sp => new BlockContentThresholdRule(sp.GetService(), threshold)); } public void UseMulticastDiscovery(string serviceId, string multicastAddress, int port) @@ -165,7 +165,7 @@ internal void FillDefaults() //AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); @@ -174,7 +174,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); diff --git a/NBlockchain/Rules/BlockContentThresholdRule.cs b/NBlockchain/Rules/BlockContentThresholdRule.cs index 17c0409..dbc9e9f 100644 --- a/NBlockchain/Rules/BlockContentThresholdRule.cs +++ b/NBlockchain/Rules/BlockContentThresholdRule.cs @@ -9,12 +9,12 @@ namespace NBlockchain.Rules { public class BlockContentThresholdRule : IBlockRule { - private readonly IUnconfirmedTransactionCache _unconfirmedTransactions; + private readonly IUnconfirmedTransactionPool _unconfirmedTransactions; private readonly decimal _threshold; public bool TailRule => true; - public BlockContentThresholdRule(IUnconfirmedTransactionCache unconfirmedTransactions, decimal threshold) + public BlockContentThresholdRule(IUnconfirmedTransactionPool unconfirmedTransactions, decimal threshold) { _unconfirmedTransactions = unconfirmedTransactions; _threshold = threshold; diff --git a/NBlockchain/Services/BlockMiner.cs b/NBlockchain/Services/BlockMiner.cs index e90ea82..0566a50 100644 --- a/NBlockchain/Services/BlockMiner.cs +++ b/NBlockchain/Services/BlockMiner.cs @@ -22,7 +22,7 @@ public class BlockMiner : IBlockMiner private readonly IBlockNotary _blockNotary; private readonly ILogger _logger; private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); - private readonly IUnconfirmedTransactionCache _unconfirmedTransactionCache; + private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; private readonly IBlockRepository _blockRepository; private readonly IPeerNetwork _peerNetwork; private readonly IReceiver _blockReciever; @@ -36,7 +36,7 @@ public class BlockMiner : IBlockMiner private CancellationTokenSource _blockCancelToken; - public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IUnconfirmedTransactionCache unconfirmedTransactionCache, IBlockRepository blockRepository, IReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) + public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IUnconfirmedTransactionPool unconfirmedTransactionPool, IBlockRepository blockRepository, IReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) { _networkParameters = networkParameters; _peerNetwork= peerNetwork; @@ -48,8 +48,8 @@ public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBui _merkleTreeBuilder = merkleTreeBuilder; _logger = loggerFactory.CreateLogger(); _blockRepository = blockRepository; - _unconfirmedTransactionCache = unconfirmedTransactionCache; - _unconfirmedTransactionCache.Changed += UnconfirmedTransactionCacheChanged; + _unconfirmedTransactionPool = unconfirmedTransactionPool; + _unconfirmedTransactionPool.Changed += UnconfirmedTransactionPoolChanged; } @@ -96,7 +96,7 @@ private async void Mine() } } - private void UnconfirmedTransactionCacheChanged(object sender, EventArgs e) + private void UnconfirmedTransactionPoolChanged(object sender, EventArgs e) { _resetEvent.WaitOne(); try @@ -114,7 +114,7 @@ private void UnconfirmedTransactionCacheChanged(object sender, EventArgs e) private async Task AssembleBlock(byte[] prevBlock, uint height, uint difficulty, CancellationToken cancellationToken) { - var targetTxns = _unconfirmedTransactionCache.Get; + var targetTxns = _unconfirmedTransactionPool.Get; targetTxns.Add(await _blockbaseBuilder.Build(_builderKeys, targetTxns)); var merkleRoot = await _merkleTreeBuilder.BuildTree(targetTxns.Select(x => x.TransactionId).ToList()); diff --git a/NBlockchain/Services/BlockchainHost.cs b/NBlockchain/Services/BlockchainHost.cs new file mode 100644 index 0000000..8aafe53 --- /dev/null +++ b/NBlockchain/Services/BlockchainHost.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using System.Collections.Concurrent; +using NBlockchain.Interfaces; +using NBlockchain.Models; + +namespace NBlockchain.Services +{ + public class BlockchainHost : IBlockchainHost + { + private readonly INetworkParameters _parameters; + private readonly IBlockRepository _blockRepository; + private readonly IBlockVerifier _blockVerifier; + private readonly ILogger _logger; + + private readonly IForkRebaser _forkRebaser; + private readonly IReceiver _receiver; + //private readonly IExpectedBlockList _expectedBlockList; + private readonly IPeerNetwork _peerNetwork; + private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); + private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; + private readonly IDifficultyCalculator _difficultyCalculator; + + private readonly Timer _pollTimer; + + + public BlockchainHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, IReceiver receiver, ILoggerFactory loggerFactory, IForkRebaser forkRebaser, INetworkParameters parameters, IUnconfirmedTransactionPool unconfirmedTransactionPool, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) + { + _blockRepository = blockRepository; + _blockVerifier = blockVerifier; + _receiver = receiver; + _parameters = parameters; + _forkRebaser = forkRebaser; + _unconfirmedTransactionPool = unconfirmedTransactionPool; + _peerNetwork = peerNetwork; + _difficultyCalculator = difficultyCalculator; + //_expectedBlockList = expectedBlockList; + _logger = loggerFactory.CreateLogger(); + + _receiver.OnReceiveBlock += OnRecieveBlock; + _receiver.OnRecieveTransaction += OnRecieveTransaction; + + _pollTimer = new Timer(GetMissingBlocks, null, TimeSpan.FromSeconds(5), _parameters.BlockTime); + } + + private async Task OnRecieveBlock(Block block) + { + var result = PeerDataResult.Ignore; + if (!_blockEvent.WaitOne(TimeSpan.FromSeconds(30))) + { + _logger.LogError($"Timeout waiting for block lock event"); + return PeerDataResult.Ignore; + } + try + { + result = await RecieveBlockUnprotected(block); + } + finally + { + _blockEvent.Set(); + } + + if (result != PeerDataResult.Demerit) + { + GetMissingBlocks(null); + } + + return result; + } + + private async Task RecieveBlockUnprotected(Block block) + { + var isTip = false; + + _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)}"); + + if (await _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + { + _logger.LogInformation("already have block"); + return PeerDataResult.Ignore; + } + + if (!await _blockVerifier.Verify(block)) + { + _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Demerit; + } + + if (!await _blockVerifier.VerifyBlockRules(block, true)) + { + _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Ignore; + } + + var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); + var bestHeader = await _blockRepository.GetBestBlockHeader(); + var isEmpty = await _blockRepository.IsEmpty(); + bool mainChain = false; + bool rebaseChain = false; + isTip = (block.Header.PreviousBlock.SequenceEqual(bestHeader?.BlockId ?? Block.HeadKey)); + _logger.LogInformation($"Is Tip {isTip}"); + + if (prevHeader != null) + { + _logger.LogInformation("Do Have previous block"); + + if (block.Header.Timestamp < prevHeader.Timestamp) + { + _logger.LogInformation("Timestamps dont match"); + return PeerDataResult.Ignore; + } + + if (block.Header.Height != (prevHeader.Height + 1)) + { + _logger.LogInformation($"Height mismatch prev: {prevHeader.Height}, this: {block.Header.Height}"); + return PeerDataResult.Ignore; + } + + if (!isTip) + { + var prevMain = await _blockRepository.GetPrimaryHeader(block.Header.Height - 1); + var isPrevOnMainChain = prevMain?.BlockId.SequenceEqual(block.Header.PreviousBlock) ?? false; + var mainExisiting = await _blockRepository.GetPrimaryHeader(block.Header.Height); + mainChain = ((mainExisiting == null) && (isPrevOnMainChain)); + } + else + { + mainChain = true; + } + + rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); + + _logger.LogInformation($"Processing block, have prev, main chain: {mainChain}, rebase: {rebaseChain}"); + + var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); + if ((mainChain) && (block.Header.Difficulty < expectedDifficulty)) + { + _logger.LogInformation("Difficulty mismatch"); + return PeerDataResult.Ignore; + } + } + else + { + _logger.LogInformation("Dont have prev block"); + if (isEmpty && !block.Header.PreviousBlock.SequenceEqual(Block.HeadKey)) + { + _logger.LogInformation("not first block but am empty"); + return PeerDataResult.Ignore; + } + mainChain = isEmpty; + _logger.LogInformation($"Processing block, missing prev, main chain: {mainChain}, rebase: {rebaseChain}"); + _peerNetwork.RequestBlock(block.Header.PreviousBlock); + } + + if (mainChain) + { + _logger.LogInformation("processing for main chain"); + if (!await _blockVerifier.VerifyTransactions(block)) + { + _logger.LogWarning($"Block txn verification failed for {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Demerit; + } + + await _blockRepository.AddBlock(block); + _unconfirmedTransactionPool.Remove(block.Transactions); + } + else + { + if (!await _blockRepository.HaveSecondaryBlock(block.Header.BlockId)) + { + _logger.LogInformation($"Adding detached block"); + await _blockRepository.AddSecondaryBlock(block); + } + if (rebaseChain) + { + _logger.LogInformation($"Searching for divergent block"); + var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId); + if (divergentHeader != null) + { + _logger.LogInformation($"Rebasing chain from {divergentHeader.Height}"); + var replayBlocks = await _forkRebaser.RebaseChain(divergentHeader.BlockId, block.Header.BlockId); + _logger.LogInformation($"Replaying {replayBlocks.Count} blocks"); + foreach (var replayBlock in replayBlocks) + { + var replayResult = await RecieveBlockUnprotected(replayBlock); + if (replayResult == PeerDataResult.Demerit) + break; + } + _logger.LogInformation($"Replay complete"); + } + else + { + _logger.LogInformation($"Divergent block not found"); + var firstForkHeader = await _forkRebaser.FindKnownForkbase(block.Header.BlockId); + if (firstForkHeader != null) + _peerNetwork.RequestBlock(firstForkHeader.PreviousBlock); + } + } + } + + if (isTip) + { + _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Relay; + } + else + { + _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); + return PeerDataResult.Ignore; + } + } + + private async Task OnRecieveTransaction(Transaction transaction) + { + _logger.LogDebug($"Recv txn {BitConverter.ToString(transaction.TransactionId)}"); + var txnResult = await _blockVerifier.VerifyTransaction(transaction, _unconfirmedTransactionPool.Get); + + if (txnResult == 0) + { + if (_unconfirmedTransactionPool.Add(transaction)) + { + _logger.LogDebug($"Accepted txn {BitConverter.ToString(transaction.TransactionId)}"); + return PeerDataResult.Relay; + } + + return PeerDataResult.Ignore; + } + _logger.LogDebug($"Rejected txn {BitConverter.ToString(transaction.TransactionId)} code: {txnResult}"); + return PeerDataResult.Ignore; + } + + + public async Task SendTransaction(Transaction transaction) + { + _logger.LogDebug("Sending txn"); + await _receiver.RecieveTransaction(transaction); + _peerNetwork.BroadcastTransaction(transaction); + } + + private async void GetMissingBlocks(object state) + { + _logger.LogInformation("GetMissingBlocks"); + + var prevHeader = await _blockRepository.GetBestBlockHeader(); + + if (prevHeader == null) + { + _logger.LogInformation("Requesting head block"); + //_expectedBlockList.ExpectNext(Block.HeadKey); + _peerNetwork.RequestNextBlock(Block.HeadKey); + return; + } + + //if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) + { + _logger.LogInformation($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); + //_expectedBlockList.ExpectNext(prevHeader.BlockId); + var cached = await _blockRepository.GetNextBlock(prevHeader.BlockId); + if (cached == null) + { + _peerNetwork.RequestNextBlock(prevHeader.BlockId); + } + else + { + _logger.LogInformation("Have cached block"); + if (await _receiver.RecieveBlock(cached) == PeerDataResult.Demerit) + { + await _blockRepository.DiscardSecondaryBlock(cached.Header.BlockId); + } + } + } + } + + } +} diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 107fc36..0968490 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -66,7 +66,7 @@ public Task AddBlock(Block block) return Task.CompletedTask; } - public Task HaveBlockMainChain(byte[] blockId) + public Task HavePrimaryBlock(byte[] blockId) { var result = MainChain.Exists(x => x.Entity.Header.BlockId == blockId); return Task.FromResult(result); @@ -117,6 +117,12 @@ private Block RehydratePersistedBlock(PersistedBlock persistedBlock) return result; } + public Task DiscardSecondaryBlock(byte[] blockId) + { + ForkChain.Delete(x => x.Entity.Header.BlockId == blockId); + return Task.CompletedTask; + } + public Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) { var startTicks = startUtc.Ticks; @@ -154,28 +160,28 @@ public async Task GetBlock(byte[] blockId) return RehydratePersistedBlock(block); } - public async Task GetMainChainHeader(uint height) + public async Task GetPrimaryHeader(uint height) { //TODO: project result var block = MainChain.Find(Query.EQ("Entity.Header.Height", Convert.ToInt64(height))).FirstOrDefault(); return block?.Entity.Header; } - public async Task GetForkHeader(byte[] forkBlockId) + public async Task GetSecondaryHeader(byte[] forkBlockId) { //TODO: project result var fork = ForkChain.Find(Query.EQ("Entity.Header.BlockId", forkBlockId)).FirstOrDefault(); return fork?.Entity.Header; } - public async Task AddDetachedBlock(Block block) + public async Task AddSecondaryBlock(Block block) { ForkChain.Insert(new PersistedOrphan(block)); } public async Task GetDivergentHeader(byte[] forkTipBlockId) { - var forkHeader = await GetForkHeader(forkTipBlockId); + var forkHeader = await GetSecondaryHeader(forkTipBlockId); if (forkHeader == null) return null; @@ -185,7 +191,7 @@ public async Task GetDivergentHeader(byte[] forkTipBlockId) if (mainParent != null) return mainParent.Entity.Header; - forkHeader = await GetForkHeader(forkHeader.PreviousBlock); + forkHeader = await GetSecondaryHeader(forkHeader.PreviousBlock); if (forkHeader == null) return null; } @@ -201,11 +207,11 @@ public async Task RewindChain(byte[] blockId) var archiveFork = MainChain .Find(x => x.Entity.Header.Height > divergent.Entity.Header.Height) .ToList() - .Select(x => RehydratePersistedBlock(x)); + .Select(RehydratePersistedBlock); foreach (var block in archiveFork.OrderByDescending(x => x.Header.Height)) { - await AddDetachedBlock(block); + await AddSecondaryBlock(block); Instructions.Delete(x => x.BlockId == block.Header.BlockId); MainChain.Delete(x => x.Entity.Header.BlockId == block.Header.BlockId); } @@ -234,7 +240,7 @@ public async Task> GetFork(byte[] forkTipBlockId) return result.OrderBy(x => x.Header.Height).ToList(); } - public Task HaveBlockForkChain(byte[] blockId) + public Task HaveSecondaryBlock(byte[] blockId) { var result = ForkChain.Exists(x => x.Entity.Header.BlockId == blockId); return Task.FromResult(result); diff --git a/NBlockchain/Services/ForkRebaser.cs b/NBlockchain/Services/ForkRebaser.cs index 079fecd..39325b5 100644 --- a/NBlockchain/Services/ForkRebaser.cs +++ b/NBlockchain/Services/ForkRebaser.cs @@ -21,40 +21,26 @@ public ForkRebaser(IBlockRepository blockRepository, ILoggerFactory loggerFactor _logger = loggerFactory.CreateLogger(); } - public async Task RebaseChain(byte[] divergentId, byte[] targetTipId) + public async Task> RebaseChain(byte[] divergentId, byte[] targetTipId) { _logger.LogInformation($"Rebasing chain from {BitConverter.ToString(divergentId)} to {BitConverter.ToString(targetTipId)}"); var currentTipHeader = await _blockRepository.GetBestBlockHeader(); await _blockRepository.RewindChain(divergentId); - //var chainFork = await _blockRepository.GetFork(targetTipId); - - //var ffwdTask = Task.Factory.StartNew(async () => - //{ - // foreach (var forkedBlock in chainFork.OrderBy(x => x.Header.Height)) - // { - // try - // { - // await _blockReceiver.RecieveBlock(forkedBlock); - // } - // catch (Exception ex) - // { - // _logger.LogError(ex.Message); - // } - // } - //}); + var chainFork = await _blockRepository.GetFork(targetTipId); + return chainFork.OrderBy(x => x.Header.Height).ToList(); } public async Task FindKnownForkbase(byte[] forkTipId) { _logger.LogInformation($"Searching for fork base"); - var header = await _blockRepository.GetForkHeader(forkTipId); + var header = await _blockRepository.GetSecondaryHeader(forkTipId); - var prevHeader = await _blockRepository.GetForkHeader(header.PreviousBlock); + var prevHeader = await _blockRepository.GetSecondaryHeader(header.PreviousBlock); while (prevHeader != null) { header = prevHeader; - prevHeader = await _blockRepository.GetForkHeader(prevHeader.PreviousBlock); + prevHeader = await _blockRepository.GetSecondaryHeader(prevHeader.PreviousBlock); } return header; } diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index 5b0c3a5..b0939de 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -30,7 +30,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly IEnumerable _discoveryServices; private readonly ILogger _logger; private readonly IOwnAddressResolver _ownAddressResolver; - private readonly IUnconfirmedTransactionCache _unconfirmedTransactionCache; + private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; private readonly ConcurrentQueue _peerRoundRobin = new ConcurrentQueue(); @@ -51,7 +51,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable public Guid NodeId { get; private set; } - public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IUnconfirmedTransactionCache unconfirmedTransactionCache, IReceiver reciever) + public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IUnconfirmedTransactionPool unconfirmedTransactionPool, IReceiver reciever) { _port = port; _reciever = reciever; @@ -59,7 +59,7 @@ public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable(); - - _receiver.OnReceiveBlock += OnRecieveBlock; - _receiver.OnRecieveTransaction += OnRecieveTransaction; - - _pollTimer = new Timer(GetMissingBlocks, null, TimeSpan.FromSeconds(5), _parameters.BlockTime); - } - - private async Task OnRecieveBlock(Block block) - { - var isTip = false; - if (!_blockEvent.WaitOne(TimeSpan.FromSeconds(30))) - { - _logger.LogError($"Timeout waiting for block lock event"); - return PeerDataResult.Ignore; - } - try - { - _logger.LogInformation($"Recv block {block.Header.Height} {BitConverter.ToString(block.Header.BlockId)}"); - - if (await _blockRepository.HaveBlockMainChain(block.Header.BlockId)) - { - _logger.LogInformation("already have block"); - return PeerDataResult.Ignore; - } - - if (!await _blockVerifier.Verify(block)) - { - _logger.LogWarning($"Block verification failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Demerit; - } - - if (!await _blockVerifier.VerifyBlockRules(block, true)) - { - _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Ignore; - } - - var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); - var bestHeader = await _blockRepository.GetBestBlockHeader(); - var isEmpty = await _blockRepository.IsEmpty(); - bool mainChain = false; - bool rebaseChain = false; - isTip = (block.Header.PreviousBlock.SequenceEqual(bestHeader?.BlockId ?? Block.HeadKey)); - _logger.LogInformation($"Is Tip {isTip}"); - - if (prevHeader != null) - { - _logger.LogInformation("Do Have previous block"); - - if (block.Header.Timestamp < prevHeader.Timestamp) - { - _logger.LogInformation("Timestamps dont match"); - return PeerDataResult.Ignore; - } - - if (block.Header.Height != (prevHeader.Height + 1)) - { - _logger.LogInformation($"Height mismatch prev: {prevHeader.Height}, this: {block.Header.Height}"); - return PeerDataResult.Ignore; - } - - if (!isTip) - { - var prevMain = await _blockRepository.GetMainChainHeader(block.Header.Height - 1); - var isPrevOnMainChain = prevMain?.BlockId.SequenceEqual(block.Header.PreviousBlock) ?? false; - var mainExisiting = await _blockRepository.GetMainChainHeader(block.Header.Height); - mainChain = ((mainExisiting == null) && (isPrevOnMainChain)); - } - else - { - mainChain = true; - } - - rebaseChain = ((block.Header.Height > bestHeader.Height) && !mainChain); - - _logger.LogInformation($"Processing block, have prev, main chain: {mainChain}, rebase: {rebaseChain}"); - - var expectedDifficulty = await _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp); - if ((mainChain) && (block.Header.Difficulty < expectedDifficulty)) - { - _logger.LogInformation("Difficulty mismatch"); - return PeerDataResult.Ignore; - } - } - else - { - _logger.LogInformation("Dont have prev block"); - if (isEmpty && !block.Header.PreviousBlock.SequenceEqual(Block.HeadKey)) - { - _logger.LogInformation("not first block but am empty"); - return PeerDataResult.Ignore; - } - mainChain = isEmpty; - _logger.LogInformation($"Processing block, missing prev, main chain: {mainChain}, rebase: {rebaseChain}"); - _peerNetwork.RequestBlock(block.Header.PreviousBlock); - } - - if (mainChain) - { - _logger.LogInformation("processing for main chain"); - if (!await _blockVerifier.VerifyTransactions(block)) - { - _logger.LogWarning($"Block txn verification failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Demerit; - } - - await _blockRepository.AddBlock(block); - _unconfirmedTransactionCache.Remove(block.Transactions); - } - else - { - if (!await _blockRepository.HaveBlockForkChain(block.Header.BlockId)) - { - _logger.LogInformation($"Adding detached block"); - await _blockRepository.AddDetachedBlock(block); - } - if (rebaseChain) - { - _logger.LogInformation($"Searching for divergent block"); - var divergentHeader = await _blockRepository.GetDivergentHeader(block.Header.BlockId); - if (divergentHeader != null) - { - _logger.LogInformation($"Rebasing chain from {divergentHeader.Height}"); - await _forkRebaser.RebaseChain(divergentHeader.BlockId, block.Header.BlockId); - } - else - { - _logger.LogInformation($"Divergent block not found"); - var firstForkHeader = await _forkRebaser.FindKnownForkbase(block.Header.BlockId); - if (firstForkHeader != null) - _peerNetwork.RequestBlock(firstForkHeader.PreviousBlock); - } - } - } - - //_expectedBlockList.Confirm(block.Header.PreviousBlock); - //_expectedBlockList.ExpectNext(block.Header.BlockId); - - } - finally - { - _blockEvent.Set(); - } - var missingBlockTask = Task.Factory.StartNew(() => GetMissingBlocks(null)); - if (isTip) - { - _logger.LogDebug($"Accepted tip block {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Relay; - } - else - { - _logger.LogDebug($"Accepted block {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Ignore; - } - } - - private async Task OnRecieveTransaction(Transaction transaction) - { - _logger.LogDebug($"Recv txn {BitConverter.ToString(transaction.TransactionId)}"); - var txnResult = await _blockVerifier.VerifyTransaction(transaction, _unconfirmedTransactionCache.Get); - - if (txnResult == 0) - { - if (_unconfirmedTransactionCache.Add(transaction)) - { - _logger.LogDebug($"Accepted txn {BitConverter.ToString(transaction.TransactionId)}"); - return PeerDataResult.Relay; - } - - return PeerDataResult.Ignore; - } - _logger.LogDebug($"Rejected txn {BitConverter.ToString(transaction.TransactionId)} code: {txnResult}"); - return PeerDataResult.Ignore; - } - - - public async Task SendTransaction(Transaction transaction) - { - _logger.LogDebug("Sending txn"); - await _receiver.RecieveTransaction(transaction); - _peerNetwork.BroadcastTransaction(transaction); - } - - private async void GetMissingBlocks(object state) - { - _logger.LogInformation("GetMissingBlocks"); - - var prevHeader = await _blockRepository.GetBestBlockHeader(); - - if (prevHeader == null) - { - _logger.LogInformation("Requesting head block"); - //_expectedBlockList.ExpectNext(Block.HeadKey); - _peerNetwork.RequestNextBlock(Block.HeadKey); - return; - } - - //if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) - { - _logger.LogInformation($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); - //_expectedBlockList.ExpectNext(prevHeader.BlockId); - var cached = await _blockRepository.GetNextBlock(prevHeader.BlockId); - if (cached == null) - { - _peerNetwork.RequestNextBlock(prevHeader.BlockId); - } - else - { - _logger.LogInformation("Have cached block"); - var recvTask = _receiver.RecieveBlock(cached); - //GetMissingBlocks(null); - } - } - } - - } -} diff --git a/NBlockchain/Services/UnconfirmedTransactionCache.cs b/NBlockchain/Services/UnconfirmedTransactionPool.cs similarity index 96% rename from NBlockchain/Services/UnconfirmedTransactionCache.cs rename to NBlockchain/Services/UnconfirmedTransactionPool.cs index 881ff90..ef48bf9 100644 --- a/NBlockchain/Services/UnconfirmedTransactionCache.cs +++ b/NBlockchain/Services/UnconfirmedTransactionPool.cs @@ -8,7 +8,7 @@ namespace NBlockchain.Services { - public class UnconfirmedTransactionCache : IUnconfirmedTransactionCache + public class UnconfirmedTransactionPool : IUnconfirmedTransactionPool { private readonly ICollection _list = new List(); private readonly AutoResetEvent _evt = new AutoResetEvent(true); diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index edadf1e..a5c4f26 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -76,13 +76,13 @@ public async Task AddBlock(Block block) await Task.Yield(); } - public Task HaveBlockMainChain(byte[] blockId) + public Task HavePrimaryBlock(byte[] blockId) { var result = MainChain.Find(x => x.Header.BlockId == blockId).Any(); return Task.FromResult(result); } - public Task HaveBlockForkChain(byte[] blockId) + public Task HaveSecondaryBlock(byte[] blockId) { var result = ForkChain.Find(x => x.Header.BlockId == blockId).Any(); return Task.FromResult(result); @@ -114,6 +114,12 @@ public Task GetNextBlock(byte[] prevBlockId) return persistedResult == null ? Task.FromResult(null) : Task.FromResult(persistedResult.ToBlock()); } + public Task DiscardSecondaryBlock(byte[] blockId) + { + ForkChain.DeleteMany(x => x.Header.BlockId == blockId); + return Task.CompletedTask; + } + public async Task GetAverageBlockTimeInSecs(DateTime startUtc, DateTime endUtc) { var startTicks = startUtc.Ticks; @@ -156,7 +162,7 @@ public Task GetBlock(byte[] blockId) return persistedResult == null ? Task.FromResult(null) : Task.FromResult(persistedResult.ToBlock()); } - public Task GetMainChainHeader(uint height) + public Task GetPrimaryHeader(uint height) { if (MainChain.Count(x => true) == 0) return Task.FromResult(null); @@ -166,14 +172,14 @@ public Task GetMainChainHeader(uint height) return Task.FromResult(result); } - public async Task AddDetachedBlock(Block block) + public async Task AddSecondaryBlock(Block block) { var persisted = new PersistedBlock(block, _addressEncoder); ForkChain.InsertOne(persisted); await Task.Yield(); } - public Task GetForkHeader(byte[] forkBlockId) + public Task GetSecondaryHeader(byte[] forkBlockId) { var forktip = ForkChain.AsQueryable().Select(x => x.Header).Where(x => x.BlockId == forkBlockId).FirstOrDefault(); return Task.FromResult(forktip); @@ -181,7 +187,7 @@ public Task GetForkHeader(byte[] forkBlockId) public async Task GetDivergentHeader(byte[] forkTipBlockId) { - var forkHeader = await GetForkHeader(forkTipBlockId); + var forkHeader = await GetSecondaryHeader(forkTipBlockId); if (forkHeader == null) return null; @@ -191,7 +197,7 @@ public async Task GetDivergentHeader(byte[] forkTipBlockId) if (mainParent != null) return mainParent; - forkHeader = await GetForkHeader(forkHeader.PreviousBlock); + forkHeader = await GetSecondaryHeader(forkHeader.PreviousBlock); if (forkHeader == null) return null; } @@ -210,7 +216,7 @@ public async Task RewindChain(byte[] blockId) .Select(x => x.ToBlock()); foreach (var block in archiveFork) - await AddDetachedBlock(block); + await AddSecondaryBlock(block); MainChain.DeleteMany(x => x.Header.Height > divergent.Header.Height); } diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 6661118..5733b1d 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -16,7 +16,7 @@ namespace DigitalCurrency { class Program { - private static INodeHost _host; + private static IBlockchainHost _host; private static IBlockMiner _miner; private static IPeerNetwork _network; private static ISignatureService _sigService; @@ -30,7 +30,7 @@ static void Main(string[] args) var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); //var serviceProvider = ConfigureForLiteDb("node.db", 10500); - _host = serviceProvider.GetService(); + _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); _network = serviceProvider.GetService(); _sigService = serviceProvider.GetService(); From effeb1846ddba4d43f3f5aa6475bea347f7062a7 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 18 Sep 2017 19:20:55 -0700 Subject: [PATCH 46/66] revert this --- Samples/DigitalCurrency/Program.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 6661118..5057f8b 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -27,8 +27,8 @@ class Program static void Main(string[] args) { - var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); - //var serviceProvider = ConfigureForLiteDb("node.db", 10500); + //var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); + var serviceProvider = ConfigureForLiteDb("node.db", 10500); _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); @@ -70,6 +70,8 @@ private static IServiceProvider ConfigureForLiteDb(string db, uint port) x.UseInstructionRepository(); x.UseTcpPeerNetwork(port); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + x.AddPeerDiscovery(sp => new StaticPeerDiscovery(new string[] { "tcp://192.168.0.13:10500" })); + x.AddInstructionType(); x.AddInstructionType(); x.AddTransactionRule(); From 0eccb30f00d60139a862ad8e038346b30d3a7d6c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Mon, 18 Sep 2017 20:16:25 -0700 Subject: [PATCH 47/66] fork walker bug --- NBlockchain/Services/Database/DefaultBlockRepository.cs | 2 +- Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NBlockchain/Services/Database/DefaultBlockRepository.cs b/NBlockchain/Services/Database/DefaultBlockRepository.cs index 0968490..0f716a4 100644 --- a/NBlockchain/Services/Database/DefaultBlockRepository.cs +++ b/NBlockchain/Services/Database/DefaultBlockRepository.cs @@ -232,7 +232,7 @@ public async Task> GetFork(byte[] forkTipBlockId) if (mainParent != null) break; - forkBlock = ForkChain.Find(x => x.Entity.Header.BlockId == forkTipBlockId).FirstOrDefault(); + forkBlock = ForkChain.Find(x => x.Entity.Header.BlockId == forkBlock.Entity.Header.PreviousBlock).FirstOrDefault(); if (forkBlock == null) break; } diff --git a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs index a5c4f26..0a712bd 100644 --- a/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs +++ b/Providers/NBlockchain.MongoDB/Services/MongoBlockRepository.cs @@ -236,7 +236,7 @@ public async Task> GetFork(byte[] forkTipBlockId) if (mainParent != null) break; - forkBlock = ForkChain.AsQueryable().FirstOrDefault(x => x.Header.BlockId == forkTipBlockId); + forkBlock = ForkChain.AsQueryable().FirstOrDefault(x => x.Header.BlockId == forkBlock.Header.PreviousBlock); if (forkBlock == null) break; } From 49af528d56112c6e60a7bc6269396b6225c47222 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Fri, 22 Sep 2017 20:33:55 -0700 Subject: [PATCH 48/66] Net refine (#9) --- NBlockchain/Interfaces/IPeerNetwork.cs | 4 +- NBlockchain/Services/BlockchainHost.cs | 11 +- NBlockchain/Services/Net/Handshake.cs | 14 ++ .../Services/Net/InProcessPeerNetwork.cs | 4 + NBlockchain/Services/Net/PeerConnection.cs | 80 +++++++++-- NBlockchain/Services/Net/TcpPeerNetwork.cs | 134 ++++++++++++------ 6 files changed, 188 insertions(+), 59 deletions(-) create mode 100644 NBlockchain/Services/Net/Handshake.cs diff --git a/NBlockchain/Interfaces/IPeerNetwork.cs b/NBlockchain/Interfaces/IPeerNetwork.cs index a3b9359..d99059e 100644 --- a/NBlockchain/Interfaces/IPeerNetwork.cs +++ b/NBlockchain/Interfaces/IPeerNetwork.cs @@ -16,7 +16,9 @@ public interface IPeerNetwork void RequestNextBlock(byte[] blockId); - void RequestBlock(byte[] blockId); + void RequestBlock(byte[] blockId); + + void RequestBlockByHeight(uint height); Task DiscoverPeers(); diff --git a/NBlockchain/Services/BlockchainHost.cs b/NBlockchain/Services/BlockchainHost.cs index 8aafe53..8a98c15 100644 --- a/NBlockchain/Services/BlockchainHost.cs +++ b/NBlockchain/Services/BlockchainHost.cs @@ -246,9 +246,9 @@ private async void GetMissingBlocks(object state) { _logger.LogInformation("GetMissingBlocks"); - var prevHeader = await _blockRepository.GetBestBlockHeader(); + var bestHeader = await _blockRepository.GetBestBlockHeader(); - if (prevHeader == null) + if (bestHeader == null) { _logger.LogInformation("Requesting head block"); //_expectedBlockList.ExpectNext(Block.HeadKey); @@ -258,12 +258,13 @@ private async void GetMissingBlocks(object state) //if ((DateTime.UtcNow.Ticks - prevHeader.Timestamp) > _parameters.BlockTime.Ticks) { - _logger.LogInformation($"Requesting missing block after {BitConverter.ToString(prevHeader.BlockId)}"); + _logger.LogInformation($"Requesting missing block after {BitConverter.ToString(bestHeader.BlockId)}"); //_expectedBlockList.ExpectNext(prevHeader.BlockId); - var cached = await _blockRepository.GetNextBlock(prevHeader.BlockId); + var cached = await _blockRepository.GetNextBlock(bestHeader.BlockId); if (cached == null) { - _peerNetwork.RequestNextBlock(prevHeader.BlockId); + //_peerNetwork.RequestNextBlock(bestHeader.BlockId); + _peerNetwork.RequestBlockByHeight(bestHeader.Height + 1); } else { diff --git a/NBlockchain/Services/Net/Handshake.cs b/NBlockchain/Services/Net/Handshake.cs new file mode 100644 index 0000000..72907dc --- /dev/null +++ b/NBlockchain/Services/Net/Handshake.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Net +{ + public class Handshake + { + public Guid NodeId { get; set; } + public int Version { get; set; } + public long Height { get; set; } + + } +} diff --git a/NBlockchain/Services/Net/InProcessPeerNetwork.cs b/NBlockchain/Services/Net/InProcessPeerNetwork.cs index 96cf80b..3276382 100644 --- a/NBlockchain/Services/Net/InProcessPeerNetwork.cs +++ b/NBlockchain/Services/Net/InProcessPeerNetwork.cs @@ -26,6 +26,10 @@ public InProcessPeerNetwork(IBlockRepository blockRepository, IReceiver reciever #pragma warning disable CS1998 + public void RequestBlockByHeight(uint height) + { + throw new NotImplementedException(); + } public async Task DiscoverPeers() { } diff --git a/NBlockchain/Services/Net/PeerConnection.cs b/NBlockchain/Services/Net/PeerConnection.cs index d9d9aee..5c0bbd8 100644 --- a/NBlockchain/Services/Net/PeerConnection.cs +++ b/NBlockchain/Services/Net/PeerConnection.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Bson; namespace NBlockchain.Services.Net { @@ -22,6 +25,10 @@ public class PeerConnection private readonly TcpClient _client; private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); private readonly Guid _localId; + private readonly int _localVersion; + private int _remoteVersion; + + private Guid _remoteId = Guid.NewGuid(); private DateTime? _lastContact; private CancellationTokenSource _cancelToken = new CancellationTokenSource(); @@ -39,22 +46,27 @@ public class PeerConnection public bool Outgoing { get; private set; } public string ConnectionString { get; private set; } public DateTime? LastContact => _lastContact; + + public long RequestCount { get; set; } = 0; + public int DemeritPoints { get; set; } = 0; + public TimeSpan QuietTimeout { get; set; } = TimeSpan.FromMinutes(10); - public PeerConnection(byte[] serviceIdentifier, Guid nodeId) + public PeerConnection(string serviceIdentifier, int version, Guid nodeId) { _client = new TcpClient(); _localId = nodeId; - _serviceIdentifier = serviceIdentifier; + _localVersion = version; + _serviceIdentifier = Encoding.UTF8.GetBytes(serviceIdentifier); Outgoing = true; } - public PeerConnection(byte[] serviceIdentifier, Guid nodeId, TcpClient client) + public PeerConnection(string serviceIdentifier, int version, Guid nodeId, TcpClient client) { _client = client; _localId = nodeId; - _serviceIdentifier = serviceIdentifier; - Outgoing = false; - Task.Factory.StartNew(Poll); + _localVersion = version; + _serviceIdentifier = Encoding.UTF8.GetBytes(serviceIdentifier); + Outgoing = false; } public async Task Connect(string connectionString) @@ -65,8 +77,12 @@ public async Task Connect(string connectionString) throw new InvalidOperationException("Only tcp connections are possible"); await _client.ConnectAsync(uri.Host, uri.Port); - var pollTask = Task.Factory.StartNew(Poll); - Send(NetworkQualifier, IdentifyCommand, _localId.ToByteArray()); + SendIdentify(); + } + + public void Run() + { + Task.Factory.StartNew(Poll); } public void Send(byte command, byte[] data) @@ -158,7 +174,7 @@ private void Maintain(object state) { if (_client.Connected) { - if (_lastContact < (DateTime.Now.AddMinutes(-10))) + if (_lastContact < (DateTime.Now.Subtract(QuietTimeout))) { OnUnresponsive?.Invoke(this); Disconnect(); @@ -175,8 +191,9 @@ private async void Poll() _pollExited = false; _cancelToken = new CancellationTokenSource(); var headerLength = _serviceIdentifier.Length + 6; - var timer = new Timer(Maintain, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(120)); - Send(NetworkQualifier, IdentifyCommand, _localId.ToByteArray()); + var timer = new Timer(Maintain, null, TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(120)); + SendIdentify(); + Send(NetworkQualifier, PingCommand, new byte[0]); while ((_client.Connected) && (!_cancelToken.IsCancellationRequested)) { try @@ -251,12 +268,24 @@ private async void Poll() _pollExited = true; } + private void SendIdentify() + { + var data = new Handshake() + { + NodeId = _localId, + Version = _localVersion + }; + Send(NetworkQualifier, IdentifyCommand, SerializeObject(data)); + } + private void ProcessNetworkCommand(byte command, byte[] data) { switch (command) { case IdentifyCommand: - _remoteId = new Guid(data); + var handshake = DeserializeObject(data); + _remoteId = handshake.NodeId; + _remoteVersion = handshake.Version; OnIdentify?.Invoke(this); break; case PingCommand: @@ -272,6 +301,33 @@ private int Recieve(byte[] msgBuffer) return actualRecv; } + + private static byte[] SerializeObject(object data) + { + using (var bw = new MemoryStream()) + { + var writer = new BsonDataWriter(bw); + var serializer = new JsonSerializer(); + serializer.TypeNameHandling = TypeNameHandling.Objects; + serializer.Serialize(writer, data); + writer.Close(); + bw.TryGetBuffer(out var result); + return result.Array; + } + } + + private static T DeserializeObject(byte[] bson) + { + using (var ms = new MemoryStream(bson)) + { + var bdr = new BsonDataReader(ms); + var serializer = new JsonSerializer(); + serializer.TypeNameHandling = TypeNameHandling.Objects; + var result = serializer.Deserialize(bdr); + bdr.Close(); + return result; + } + } } public delegate void ReceiveMessage(PeerConnection sender, byte command, byte[] data); diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index b0939de..e929beb 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -22,7 +22,8 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable //TODO: break this class up into smaller pieces private const int TargetOutgoingCount = 8; private readonly uint _port; - private byte[] _serviceId = new byte[] { 0x0, 0x1 }; + private string _serviceId = "BC"; + private int _version = 1; private readonly IReceiver _reciever; @@ -33,7 +34,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; private readonly ConcurrentQueue _peerRoundRobin = new ConcurrentQueue(); - + private readonly List _peerConnections = new List(); private CancellationTokenSource _cancelTokenSource; private TcpListener _listener; @@ -60,7 +61,7 @@ public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable GetActivePeers() @@ -90,12 +91,13 @@ public void Open() { var client = await _listener.AcceptTcpClientAsync(); _logger.LogDebug($"Client connected - {client.Client.RemoteEndPoint}"); - var peer = new PeerConnection(_serviceId, NodeId, client); + var peer = new PeerConnection(_serviceId, _version, NodeId, client); AttachEventHandlers(peer); _peerEvent.WaitOne(); try { _peerConnections.Add(peer); + peer.Run(); } finally { @@ -108,13 +110,13 @@ public void Open() _logger.LogError($"Error listening - {ex.Message}"); } }); - + DiscoverOwnConnectionStrings(); AdvertiseToPeers(); _discoveryTimer = new Timer(async (state) => { - await DiscoverPeers(); + await DiscoverPeers(); }, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30)); _sharePeersTimer = new Timer(SharePeers, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); @@ -209,6 +211,9 @@ private async void Peer_OnReceiveMessage(PeerConnection sender, byte command, by case Commands.NextBlockRequest: await ProcessBlockRequest(data, true, sender); break; + case Commands.BlockHeightRequest: + await ProcessBlockRequest(BitConverter.ToUInt32(data, 0), sender); + break; case Commands.PeerShare: if (IsSharablePeer(Encoding.UTF8.GetString(data))) AddPeer(new KnownPeer() { ConnectionString = Encoding.UTF8.GetString(data) }); @@ -241,7 +246,7 @@ private async Task ProcessBlockRequest(byte[] blockId, bool next, PeerConnection if (block != null) { _logger.LogInformation($"Responding to block request {next} {BitConverter.ToString(blockId)}"); - var data = SerializeObject(block); + var data = SerializeObject(block); SendBlock(peer, data, false); } else @@ -250,8 +255,25 @@ private async Task ProcessBlockRequest(byte[] blockId, bool next, PeerConnection } } + private async Task ProcessBlockRequest(uint height, PeerConnection peer) + { + var header = await _blockRepository.GetPrimaryHeader(height); + + if (header != null) + { + _logger.LogInformation($"Responding to block request {height} with {BitConverter.ToString(header.BlockId)}"); + var block = await _blockRepository.GetBlock(header.BlockId); + var data = SerializeObject(block); + SendBlock(peer, data, false); + } + else + { + _logger.LogInformation($"Unable to respond to block request {height}"); + } + } + private async Task ProcessBlock(byte[] data, Guid originId, bool tip) - { + { var block = DeserializeObject(data); _logger.LogDebug($"Recv block {BitConverter.ToString(block.Header.BlockId)} from {originId}"); @@ -270,6 +292,13 @@ private async Task ProcessBlock(byte[] data, Guid originId, bool tip) }); }); } + + if (result == PeerDataResult.Demerit) + { + PeerConnection peer = GetActivePeers().FirstOrDefault(x => x.RemoteId == originId); + if (peer != null) + peer.DemeritPoints++; + } } private async Task ProcessTransaction(byte[] data, Guid originId) @@ -288,6 +317,13 @@ private async Task ProcessTransaction(byte[] data, Guid originId) }); }); } + + if (result == PeerDataResult.Demerit) + { + PeerConnection peer = GetActivePeers().FirstOrDefault(x => x.RemoteId == originId); + if (peer != null) + peer.DemeritPoints++; + } } private void ProcessTxnRequest(PeerConnection peer) @@ -319,18 +355,19 @@ private void Peer_OnDisconnect(PeerConnection sender) _peerEvent.Set(); } } - + private async Task OnboardPeer(string connStr) { try { - var peer = new PeerConnection(_serviceId, NodeId); + var peer = new PeerConnection(_serviceId, _version, NodeId); await peer.Connect(connStr); AttachEventHandlers(peer); _peerEvent.WaitOne(); try { _peerConnections.Add(peer); + peer.Run(); } finally { @@ -387,7 +424,7 @@ private async void SharePeers(object state) foreach (var ds in _discoveryServices) await ds.SharePeers(_peerRoundRobin.ToList()); } - + private async Task ConnectOut() { var activePeers = GetActivePeers(); @@ -430,7 +467,7 @@ private async Task ConnectOut() _connectOutEvent.Set(); } } - + public void BroadcastTail(Block block) { var data = SerializeObject(block); @@ -460,7 +497,7 @@ private void SendBlock(PeerConnection peer, byte[] data, bool tail) public void BroadcastTransaction(Transaction transaction) { var data = SerializeObject(transaction); - + Task.Factory.StartNew(() => { var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); @@ -485,51 +522,65 @@ private void SendTxn(PeerConnection peer, byte[] data) public void RequestNextBlock(byte[] blockId) { - Task.Factory.StartNew(async () => + LoadBalancedRequest((peer) => { - var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); - //TODO: round robin - foreach (var peer in peers) - { - _logger.LogInformation($"Requesting next block {BitConverter.ToString(blockId)} from peer {peer.RemoteId}"); - peer.Send(Commands.NextBlockRequest, blockId); + _logger.LogInformation($"Requesting next block {BitConverter.ToString(blockId)} from peer {peer.RemoteId}"); + peer.Send(Commands.NextBlockRequest, blockId); + return Task.CompletedTask; + }, + async () => (await _blockRepository.GetBlockHeader(blockId)) != null); + } - await Task.Delay(TimeSpan.FromSeconds(5)); + public void RequestBlock(byte[] blockId) + { + LoadBalancedRequest((peer) => + { + _logger.LogInformation($"Requesting block {BitConverter.ToString(blockId)} from peer {peer.RemoteId}"); + peer.Send(Commands.BlockRequest, blockId); + return Task.CompletedTask; + }, + async () => (await _blockRepository.GetBlockHeader(blockId)) != null); + } - if ((await _blockRepository.GetNextBlock(blockId)) != null) - return; - } - }); + public void RequestBlockByHeight(uint height) + { + LoadBalancedRequest((peer) => + { + _logger.LogInformation($"Requesting block {height} from peer {peer.RemoteId}"); + peer.Send(Commands.BlockHeightRequest, BitConverter.GetBytes(height)); + return Task.CompletedTask; + }, + async () => (await _blockRepository.GetPrimaryHeader(height)) != null); } - public void RequestBlock(byte[] blockId) + public void LoadBalancedRequest(Func requestAction, Func> resolveAction) { Task.Factory.StartNew(async () => { - var peers = GetActivePeers().Where(x => x.RemoteId != NodeId); + var peers = GetActivePeers() + .Where(x => x.RemoteId != NodeId && x.LastContact.HasValue) + .Where(x => x.LastContact > (DateTime.Now.AddMinutes(-20))) + .OrderBy(x => x.RequestCount); //TODO: round robin foreach (var peer in peers) { - _logger.LogInformation($"Requesting block {BitConverter.ToString(blockId)} from peer {peer.RemoteId}"); - peer.Send(Commands.BlockRequest, blockId); - + peer.RequestCount++; + await requestAction(peer); await Task.Delay(TimeSpan.FromSeconds(5)); - - if ((await _blockRepository.GetBlockHeader(blockId)) != null) + if (await resolveAction()) return; } }); } - public void Dispose() { - - } + + } private void AdvertiseToPeers() { foreach (var ds in _discoveryServices) - Task.Factory.StartNew(() => + Task.Factory.StartNew(() => { try { @@ -613,7 +664,7 @@ private static bool IsSharablePeer(string connectionUri) return false; } } - + public ICollection GetPeersIn() { return GetActivePeers() @@ -629,7 +680,7 @@ public ICollection GetPeersOut() .Select(x => new ConnectedPeer(x.RemoteId, x.RemoteEndPoint?.ToString())) .ToList(); } - + } internal class Commands @@ -639,7 +690,8 @@ internal class Commands public const byte Txn = 2; public const byte BlockRequest = 3; public const byte NextBlockRequest = 4; - public const byte PeerShare = 5; - public const byte TxnRequest = 6; - } + public const byte BlockHeightRequest = 5; + public const byte PeerShare = 6; + public const byte TxnRequest = 7; + } } From 6ce04164cba7d1d5c677ce080401367c077c3755 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Sep 2017 09:42:31 -0700 Subject: [PATCH 49/66] refactor --- NBlockchain/Interfaces/IBlockNotary.cs | 11 ----- NBlockchain/Interfaces/IConsensusMethod.cs | 12 ++++++ NBlockchain/Models/BlockchainOptions.cs | 8 ++-- NBlockchain/NBlockchain.csproj | 2 +- NBlockchain/Services/BlockMiner.cs | 10 ++--- NBlockchain/Services/BlockVerifier.cs | 17 +++++--- ...BlockNotary.cs => ProofOfWorkConsensus.cs} | 11 +++-- .../NBlockchain.MongoDB.csproj | 2 +- README.md | 15 +++++-- Samples/DigitalCurrency/Program.cs | 4 +- .../Rules/CoinbaseTransactionRule.cs | 3 ++ doc/readme.md | 41 +++++++++++++++++++ 12 files changed, 100 insertions(+), 36 deletions(-) delete mode 100644 NBlockchain/Interfaces/IBlockNotary.cs create mode 100644 NBlockchain/Interfaces/IConsensusMethod.cs rename NBlockchain/Services/{ProofOfWorkBlockNotary.cs => ProofOfWorkConsensus.cs} (85%) create mode 100644 doc/readme.md diff --git a/NBlockchain/Interfaces/IBlockNotary.cs b/NBlockchain/Interfaces/IBlockNotary.cs deleted file mode 100644 index c2633d4..0000000 --- a/NBlockchain/Interfaces/IBlockNotary.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; -using System.Threading; -using NBlockchain.Models; - -namespace NBlockchain.Interfaces -{ - public interface IBlockNotary - { - Task ConfirmBlock(Block block, CancellationToken cancellationToken); - } -} \ No newline at end of file diff --git a/NBlockchain/Interfaces/IConsensusMethod.cs b/NBlockchain/Interfaces/IConsensusMethod.cs new file mode 100644 index 0000000..22ad708 --- /dev/null +++ b/NBlockchain/Interfaces/IConsensusMethod.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using System.Threading; +using NBlockchain.Models; + +namespace NBlockchain.Interfaces +{ + public interface IConsensusMethod + { + Task BuildConsensus(Block block, CancellationToken cancellationToken); + bool VerifyConsensus(Block block); + } +} \ No newline at end of file diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index e9905e9..5e2e089 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -37,10 +37,10 @@ public void UseHasher() Services.AddTransient(typeof(IHasher), typeof(T)); } - public void UseBlockNotary() - where T : IBlockNotary + public void UseConsensusMethod() + where T : IConsensusMethod { - Services.AddTransient(typeof(IBlockNotary), typeof(T)); + Services.AddTransient(typeof(IConsensusMethod), typeof(T)); } public void UseSignatureService() @@ -151,7 +151,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); - AddDefault(ServiceLifetime.Transient); + AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Transient); diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index 3978bb7..3fd9423 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.4.0-alpha + 0.5.0-alpha 0.4.0.0 0.4.0.0 https://github.com/danielgerlag/NBlockchain diff --git a/NBlockchain/Services/BlockMiner.cs b/NBlockchain/Services/BlockMiner.cs index 0566a50..c1a3fd2 100644 --- a/NBlockchain/Services/BlockMiner.cs +++ b/NBlockchain/Services/BlockMiner.cs @@ -19,7 +19,7 @@ public class BlockMiner : IBlockMiner private readonly ITransactionKeyResolver _transactionKeyResolver; private readonly IMerkleTreeBuilder _merkleTreeBuilder; private readonly IBlockbaseTransactionBuilder _blockbaseBuilder; - private readonly IBlockNotary _blockNotary; + private readonly IConsensusMethod _consensusMethod; private readonly ILogger _logger; private readonly AutoResetEvent _resetEvent = new AutoResetEvent(true); private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; @@ -36,13 +36,13 @@ public class BlockMiner : IBlockMiner private CancellationTokenSource _blockCancelToken; - public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IBlockNotary blockNotary, IUnconfirmedTransactionPool unconfirmedTransactionPool, IBlockRepository blockRepository, IReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) + public BlockMiner(ITransactionKeyResolver transactionKeyResolver, IMerkleTreeBuilder merkleTreeBuilder, INetworkParameters networkParameters, IBlockbaseTransactionBuilder blockbaseBuilder, IPeerNetwork peerNetwork, IConsensusMethod consensusMethod, IUnconfirmedTransactionPool unconfirmedTransactionPool, IBlockRepository blockRepository, IReceiver blockReciever, IDifficultyCalculator difficultyCalculator, ILoggerFactory loggerFactory) { _networkParameters = networkParameters; _peerNetwork= peerNetwork; _blockbaseBuilder = blockbaseBuilder; _blockReciever = blockReciever; - _blockNotary = blockNotary; + _consensusMethod = consensusMethod; _difficultyCalculator = difficultyCalculator; _transactionKeyResolver = transactionKeyResolver; _merkleTreeBuilder = merkleTreeBuilder; @@ -139,8 +139,8 @@ private async Task AssembleBlock(byte[] prevBlock, uint height, uint diff } }; - _logger.LogDebug($"Notarizing block {height}"); - await _blockNotary.ConfirmBlock(result, cancellationToken); + _logger.LogDebug($"Building consensus for block {height}"); + await _consensusMethod.BuildConsensus(result, cancellationToken); if (cancellationToken.IsCancellationRequested) { diff --git a/NBlockchain/Services/BlockVerifier.cs b/NBlockchain/Services/BlockVerifier.cs index edc3ec9..e4b4625 100644 --- a/NBlockchain/Services/BlockVerifier.cs +++ b/NBlockchain/Services/BlockVerifier.cs @@ -16,12 +16,12 @@ public class BlockVerifier : IBlockVerifier private readonly IEnumerable _blockRules; private readonly IInstructionRepository _instructionRepository; private readonly ISignatureService _signatureService; - private readonly IHashTester _hashTester; + private readonly IConsensusMethod _consensusMethod; private readonly IHasher _hasher; private readonly IMerkleTreeBuilder _merkleTreeBuilder; private readonly ITransactionKeyResolver _transactionKeyResolver; - public BlockVerifier(INetworkParameters parameters, ISignatureService signatureService, IEnumerable txnRules, IEnumerable blockRules, IEnumerable validTxnTypes, IMerkleTreeBuilder merkleTreeBuilder, ITransactionKeyResolver transactionKeyResolver, IHashTester hashTester, IHasher hasher, IInstructionRepository instructionRepository) + public BlockVerifier(INetworkParameters parameters, ISignatureService signatureService, IEnumerable txnRules, IEnumerable blockRules, IEnumerable validTxnTypes, IMerkleTreeBuilder merkleTreeBuilder, ITransactionKeyResolver transactionKeyResolver, IConsensusMethod consensusMethod, IHasher hasher, IInstructionRepository instructionRepository) { _parameters = parameters; _signatureService = signatureService; @@ -29,14 +29,14 @@ public BlockVerifier(INetworkParameters parameters, ISignatureService signatureS _blockRules = blockRules; _merkleTreeBuilder = merkleTreeBuilder; _transactionKeyResolver = transactionKeyResolver; - _hashTester = hashTester; + _consensusMethod = consensusMethod; _hasher = hasher; _instructionRepository = instructionRepository; } public async Task Verify(Block block) { - if (!_hashTester.TestHash(block.Header.BlockId, block.Header.Difficulty)) + if (!_consensusMethod.VerifyConsensus(block)) return false; var seed = block.Header.CombineHashableElementsWithNonce(block.Header.Nonce); @@ -85,7 +85,14 @@ public async Task VerifyTransaction(Transaction transaction, ICollection current & validator.Validate(transaction, siblings)); + foreach (var txnRule in _txnRules) + { + var txnResult = txnRule.Validate(transaction, siblings); + if (txnResult != 0) + return txnResult; + } + + return 0; } diff --git a/NBlockchain/Services/ProofOfWorkBlockNotary.cs b/NBlockchain/Services/ProofOfWorkConsensus.cs similarity index 85% rename from NBlockchain/Services/ProofOfWorkBlockNotary.cs rename to NBlockchain/Services/ProofOfWorkConsensus.cs index 27541cd..f3c769e 100644 --- a/NBlockchain/Services/ProofOfWorkBlockNotary.cs +++ b/NBlockchain/Services/ProofOfWorkConsensus.cs @@ -9,21 +9,26 @@ namespace NBlockchain.Services { - public class ProofOfWorkBlockNotary : IBlockNotary + public class ProofOfWorkConsensus: IConsensusMethod { private readonly IHasher _hasher; private readonly INetworkParameters _networkParameters; private readonly IHashTester _hashTester; private readonly AutoResetEvent _lock = new AutoResetEvent(true); - public ProofOfWorkBlockNotary(IHasher hasher, INetworkParameters networkParameters, IHashTester hashTester) + public ProofOfWorkConsensus(IHasher hasher, INetworkParameters networkParameters, IHashTester hashTester) { _hasher = hasher; _networkParameters = networkParameters; _hashTester = hashTester; } - public async Task ConfirmBlock(Block block, CancellationToken cancellationToken) + public bool VerifyConsensus(Block block) + { + return _hashTester.TestHash(block.Header.BlockId, block.Header.Difficulty); + } + + public async Task BuildConsensus(Block block, CancellationToken cancellationToken) { long counter = 0; var cancellationTokenSource = new CancellationTokenSource(); diff --git a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj index 119e106..4eea216 100644 --- a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj +++ b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.4.0-alpha + 0.5.0-alpha MongoDB persistence store for NBlockchain https://github.com/danielgerlag/NBlockchain.git https://github.com/danielgerlag/NBlockchain diff --git a/README.md b/README.md index 71c07ed..2d931ec 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Beyond this, it is meant to be highly customizable, you can switch out the defau * Signing * Hashing algorithm * Block verification - * Block notary (eg. proof of work, etc...) + * Block consensus method (eg. proof of work, etc...) ## Installation @@ -35,7 +35,7 @@ dotnet add package NBlockchain --version 0.1.0-alpha * [Digital Currency](Samples/DigitalCurrency) ## Local database stores - * In memory (mostly for testing & demo purposes) + * LiteDB (Default built-in) * [MongoDB](Providers/NBlockchain.MongoDB) ## Networking implementations @@ -47,8 +47,15 @@ dotnet add package NBlockchain --version 0.1.0-alpha * Multicast (for finding peers on the local network) * More to come.... -## Outstanding items for v1 - * Chain fork detection and resolution +## Key features +* Automatic chain fork detection and resolution +* Open, flexible transaction schema +* Customizable transaction level rules +* Customizable block level rules +* Peer discovery +* Proof of work management + +## Outstanding items for v1 * NAT traversal * More peer discovery options * Integration tests diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 4742a70..91abc46 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -27,8 +27,8 @@ class Program static void Main(string[] args) { - //var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); - var serviceProvider = ConfigureForLiteDb("node.db", 10500); + var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); + //var serviceProvider = ConfigureForLiteDb("node.db", 10500); _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); diff --git a/Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs b/Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs index 37eafd0..0de9886 100644 --- a/Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs +++ b/Samples/DigitalCurrency/Rules/CoinbaseTransactionRule.cs @@ -14,6 +14,9 @@ public class CoinbaseTransactionRule : ITransactionRule { public int Validate(Transaction transaction, ICollection siblings) { + if (transaction.Instructions.OfType().Count() == 0) + return 0; + var coinbaseTotal = transaction.Instructions.OfType().Sum(x => x.Amount); if (coinbaseTotal != -50) diff --git a/doc/readme.md b/doc/readme.md new file mode 100644 index 0000000..de379bb --- /dev/null +++ b/doc/readme.md @@ -0,0 +1,41 @@ +# NBlockchain + +NBlockchain is a .NET standard library for building blockchain applications. + +## Block content + +A block consists of a collection of transactions, and transactions in turn are simply a container for instructions. +You may define your own schema for each of your own instruction types by inheriting off the `Instruction` abstract class. + +Once you have defined your own instruction types, you can implement an instruction repository in order to query accepted instructions within your custom rule sets. Do this by inheriting off the `InstructionRepository` class for LiteDb or the `MongoInstructionRepository` for MongoDB. + +You may then define transaction level rules that can inspect the enclosed instructions by implementing the `ITransactionRule` interface. +At this point you may inject your custom instruction repositories into these rule classes via the constructor. + +Furthermore, you may define block level rules that can inspect all the transactions within a block by implementing the `IBlockRule` interface. + +## Node configuration + +You must configure the IoC container with the various pieces you wish to include. eg. + +``` +IServiceCollection services = new ServiceCollection(); +services.AddBlockchain(blockchain => +{ + blockchain.UseDataConnection("node.db"); + blockchain.UseInstructionRepository(); + blockchain.UseTcpPeerNetwork(port); + blockchain.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + blockchain.AddInstructionType(); + blockchain.AddInstructionType(); + blockchain.AddTransactionRule(); + blockchain.AddTransactionRule(); + blockchain.AddBlockRule(); + blockchain.UseBlockbaseProvider(); + blockchain.UseParameters(new StaticNetworkParameters() + { + BlockTime = TimeSpan.FromSeconds(120), + HeaderVersion = 1 + }); +}); +``` \ No newline at end of file From c9b6f58b83fe8863c0073dec41f0605649c5119e Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Sep 2017 09:44:05 -0700 Subject: [PATCH 50/66] typo --- Samples/DigitalCurrency/Program.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 91abc46..9265ece 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -69,9 +69,7 @@ private static IServiceProvider ConfigureForLiteDb(string db, uint port) x.UseDataConnection("node.db"); x.UseInstructionRepository(); x.UseTcpPeerNetwork(port); - x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); - x.AddPeerDiscovery(sp => new StaticPeerDiscovery(new string[] { "tcp://192.168.0.13:10500" })); - + x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); x.AddInstructionType(); x.AddInstructionType(); x.AddTransactionRule(); From 73c9a9ec2a0c350ace66016734400e1d220a1b00 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Sep 2017 09:48:25 -0700 Subject: [PATCH 51/66] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2d931ec..f3c52e5 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,9 @@ dotnet add package NBlockchain --version 0.1.0-alpha * Peer discovery * Proof of work management +## Documentation +https://github.com/danielgerlag/NBlockchain/tree/master/doc + ## Outstanding items for v1 * NAT traversal * More peer discovery options From 6b5b5ab07576d9aab2ad713ba0ff33040f2edbd5 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Sep 2017 09:49:55 -0700 Subject: [PATCH 52/66] Update readme.md --- doc/readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/readme.md b/doc/readme.md index de379bb..54b45a7 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -1,5 +1,7 @@ # NBlockchain +**This documentation is still a work in progress!!!** + NBlockchain is a .NET standard library for building blockchain applications. ## Block content @@ -38,4 +40,4 @@ services.AddBlockchain(blockchain => HeaderVersion = 1 }); }); -``` \ No newline at end of file +``` From 2454d1e2f95b03831251247cd0f08deeb639f31d Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Sep 2017 09:51:18 -0700 Subject: [PATCH 53/66] Update readme.md --- doc/readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/readme.md b/doc/readme.md index 54b45a7..d23ae56 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -20,8 +20,7 @@ Furthermore, you may define block level rules that can inspect all the transacti You must configure the IoC container with the various pieces you wish to include. eg. -``` -IServiceCollection services = new ServiceCollection(); +```c# services.AddBlockchain(blockchain => { blockchain.UseDataConnection("node.db"); From 66c7d92688f889d6c07d2f0c4a7164edd3096ced Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Sep 2017 09:52:24 -0700 Subject: [PATCH 54/66] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f3c52e5..61c26a9 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ Beyond this, it is meant to be highly customizable, you can switch out the defau Using Nuget package console ``` -PM> Install-Package NBlockchain -Version 0.1.0-alpha +PM> Install-Package NBlockchain -Version 0.5.0-alpha ``` Using .NET CLI ``` -dotnet add package NBlockchain --version 0.1.0-alpha +dotnet add package NBlockchain --version 0.5.0-alpha ``` ## Samples From 8679134649a76a7ef522e796630debc31c105f92 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 23 Sep 2017 09:52:50 -0700 Subject: [PATCH 55/66] Update README.md --- Providers/NBlockchain.MongoDB/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Providers/NBlockchain.MongoDB/README.md b/Providers/NBlockchain.MongoDB/README.md index dd9990b..af2b3f7 100644 --- a/Providers/NBlockchain.MongoDB/README.md +++ b/Providers/NBlockchain.MongoDB/README.md @@ -4,11 +4,11 @@ Using Nuget package console ``` -PM> Install-Package NBlockchain.MongoDB -Version 0.1.0-alpha +PM> Install-Package NBlockchain.MongoDB -Version 0.5.0-alpha ``` Using .NET CLI ``` -dotnet add package NBlockchain.MongoDB --version 0.1.0-alpha +dotnet add package NBlockchain.MongoDB --version 0.5.0-alpha ``` ## Usage From 7ffa14613207044b6ad579f13e2e5b07e06889f1 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 24 Sep 2017 08:13:59 -0700 Subject: [PATCH 56/66] tests --- NBlockchain/Interfaces/IBlockchainHost.cs | 10 -- NBlockchain/Interfaces/IBlockchainNode.cs | 12 ++ NBlockchain/Models/BlockchainOptions.cs | 2 +- NBlockchain/NBlockchain.csproj | 6 +- .../{BlockchainHost.cs => BlockchainNode.cs} | 22 +-- .../NBlockchain.MongoDB.csproj | 6 +- Samples/DigitalCurrency/Program.cs | 4 +- .../Services/BlockchainNodeTests.cs | 146 ++++++++++++++++++ 8 files changed, 178 insertions(+), 30 deletions(-) delete mode 100644 NBlockchain/Interfaces/IBlockchainHost.cs create mode 100644 NBlockchain/Interfaces/IBlockchainNode.cs rename NBlockchain/Services/{BlockchainHost.cs => BlockchainNode.cs} (93%) create mode 100644 Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs diff --git a/NBlockchain/Interfaces/IBlockchainHost.cs b/NBlockchain/Interfaces/IBlockchainHost.cs deleted file mode 100644 index a1e643b..0000000 --- a/NBlockchain/Interfaces/IBlockchainHost.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Threading.Tasks; -using NBlockchain.Models; - -namespace NBlockchain.Interfaces -{ - public interface IBlockchainHost - { - Task SendTransaction(Transaction transaction); - } -} \ No newline at end of file diff --git a/NBlockchain/Interfaces/IBlockchainNode.cs b/NBlockchain/Interfaces/IBlockchainNode.cs new file mode 100644 index 0000000..8729467 --- /dev/null +++ b/NBlockchain/Interfaces/IBlockchainNode.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using NBlockchain.Models; + +namespace NBlockchain.Interfaces +{ + public interface IBlockchainNode + { + Task SendTransaction(Transaction transaction); + Task RecieveBlock(Block block); + Task RecieveTransaction(Transaction transaction); + } +} \ No newline at end of file diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 5e2e089..2371bff 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -165,7 +165,7 @@ internal void FillDefaults() //AddDefault(ServiceLifetime.Singleton); - AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index 3fd9423..7517762 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -2,9 +2,9 @@ netstandard2.0 - 0.5.0-alpha - 0.4.0.0 - 0.4.0.0 + 0.6.0-alpha + 0.6.0.0 + 0.6.0.0 https://github.com/danielgerlag/NBlockchain https://github.com/danielgerlag/NBlockchain.git git diff --git a/NBlockchain/Services/BlockchainHost.cs b/NBlockchain/Services/BlockchainNode.cs similarity index 93% rename from NBlockchain/Services/BlockchainHost.cs rename to NBlockchain/Services/BlockchainNode.cs index 8a98c15..6613d54 100644 --- a/NBlockchain/Services/BlockchainHost.cs +++ b/NBlockchain/Services/BlockchainNode.cs @@ -11,7 +11,7 @@ namespace NBlockchain.Services { - public class BlockchainHost : IBlockchainHost + public class BlockchainNode : IBlockchainNode { private readonly INetworkParameters _parameters; private readonly IBlockRepository _blockRepository; @@ -26,10 +26,10 @@ public class BlockchainHost : IBlockchainHost private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; private readonly IDifficultyCalculator _difficultyCalculator; - private readonly Timer _pollTimer; + public readonly Timer PollTimer; - public BlockchainHost(IBlockRepository blockRepository, IBlockVerifier blockVerifier, IReceiver receiver, ILoggerFactory loggerFactory, IForkRebaser forkRebaser, INetworkParameters parameters, IUnconfirmedTransactionPool unconfirmedTransactionPool, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator, IExpectedBlockList expectedBlockList) + public BlockchainNode(IBlockRepository blockRepository, IBlockVerifier blockVerifier, IReceiver receiver, ILoggerFactory loggerFactory, IForkRebaser forkRebaser, INetworkParameters parameters, IUnconfirmedTransactionPool unconfirmedTransactionPool, IPeerNetwork peerNetwork, IDifficultyCalculator difficultyCalculator) { _blockRepository = blockRepository; _blockVerifier = blockVerifier; @@ -40,15 +40,15 @@ public BlockchainHost(IBlockRepository blockRepository, IBlockVerifier blockVeri _peerNetwork = peerNetwork; _difficultyCalculator = difficultyCalculator; //_expectedBlockList = expectedBlockList; - _logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); - _receiver.OnReceiveBlock += OnRecieveBlock; - _receiver.OnRecieveTransaction += OnRecieveTransaction; + _receiver.OnReceiveBlock += RecieveBlock; + _receiver.OnRecieveTransaction += RecieveTransaction; - _pollTimer = new Timer(GetMissingBlocks, null, TimeSpan.FromSeconds(5), _parameters.BlockTime); + PollTimer = new Timer(GetMissingBlocks, null, TimeSpan.FromSeconds(5), _parameters.BlockTime); } - private async Task OnRecieveBlock(Block block) + public async Task RecieveBlock(Block block) { var result = PeerDataResult.Ignore; if (!_blockEvent.WaitOne(TimeSpan.FromSeconds(30))) @@ -215,7 +215,7 @@ private async Task RecieveBlockUnprotected(Block block) } } - private async Task OnRecieveTransaction(Transaction transaction) + public async Task RecieveTransaction(Transaction transaction) { _logger.LogDebug($"Recv txn {BitConverter.ToString(transaction.TransactionId)}"); var txnResult = await _blockVerifier.VerifyTransaction(transaction, _unconfirmedTransactionPool.Get); @@ -238,8 +238,8 @@ private async Task OnRecieveTransaction(Transaction transaction) public async Task SendTransaction(Transaction transaction) { _logger.LogDebug("Sending txn"); - await _receiver.RecieveTransaction(transaction); - _peerNetwork.BroadcastTransaction(transaction); + if (await _receiver.RecieveTransaction(transaction) == PeerDataResult.Relay) + _peerNetwork.BroadcastTransaction(transaction); } private async void GetMissingBlocks(object state) diff --git a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj index 4eea216..e6e6d9f 100644 --- a/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj +++ b/Providers/NBlockchain.MongoDB/NBlockchain.MongoDB.csproj @@ -2,14 +2,14 @@ netstandard2.0 - 0.5.0-alpha + 0.6.0-alpha MongoDB persistence store for NBlockchain https://github.com/danielgerlag/NBlockchain.git https://github.com/danielgerlag/NBlockchain git Blockchain - 0.4.0.0 - 0.4.0.0 + 0.6.0.0 + 0.6.0.0 true diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 9265ece..884ed33 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -16,7 +16,7 @@ namespace DigitalCurrency { class Program { - private static IBlockchainHost _host; + private static IBlockchainNode _host; private static IBlockMiner _miner; private static IPeerNetwork _network; private static ISignatureService _sigService; @@ -30,7 +30,7 @@ static void Main(string[] args) var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); //var serviceProvider = ConfigureForLiteDb("node.db", 10500); - _host = serviceProvider.GetService(); + _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); _network = serviceProvider.GetService(); _sigService = serviceProvider.GetService(); diff --git a/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs b/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs new file mode 100644 index 0000000..250b0a6 --- /dev/null +++ b/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs @@ -0,0 +1,146 @@ +using FakeItEasy; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; +using NBlockchain.Models; +using NBlockchain.Services; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace NBlockchain.Tests.Services +{ + public class BlockchainNodeTests + { + private readonly BlockchainNode _subject; + private readonly IReceiver _receiver; + + private readonly INetworkParameters _parameters; + private readonly IBlockRepository _blockRepository; + private readonly IBlockVerifier _blockVerifier; + private readonly ILoggerFactory _loggerFactory; + private readonly IForkRebaser _forkRebaser; + private readonly IPeerNetwork _peerNetwork; + private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; + private readonly IDifficultyCalculator _difficultyCalculator; + + public BlockchainNodeTests() + { + _receiver = A.Fake(); + _parameters = new StaticNetworkParameters() { BlockTime = TimeSpan.FromSeconds(1) }; + _loggerFactory = A.Fake(); + _blockRepository = A.Fake(); + _blockVerifier = A.Fake(); + _forkRebaser = A.Fake(); + _peerNetwork = A.Fake(); + _unconfirmedTransactionPool = A.Fake(); + _difficultyCalculator = A.Fake(); + + _subject = new BlockchainNode(_blockRepository, _blockVerifier, _receiver, _loggerFactory, _forkRebaser, _parameters, _unconfirmedTransactionPool, _peerNetwork, _difficultyCalculator); + _subject.PollTimer.Dispose(); + } + + + [Fact] + public async void should_verify_incoming_transaction() + { + await _subject.RecieveTransaction(new Transaction() { TransactionId = new byte[0] }); + + A.CallTo(() => _blockVerifier.VerifyTransaction(A.Ignored, A>.Ignored)) + .MustHaveHappened(); + } + + [Fact] + public async void should_ignore_invalid_incoming_transaction() + { + //arrange + A.CallTo(() => _blockVerifier.VerifyTransaction(A.Ignored, A>.Ignored)) + .Returns(-1); + + //act + var result = await _subject.RecieveTransaction(new Transaction() { TransactionId = new byte[0] }); + + //assert + A.CallTo(() => _blockVerifier.VerifyTransaction(A.Ignored, A>.Ignored)) + .MustHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Add(A.Ignored)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Ignore); + } + + [Fact] + public async void should_add_valid_incoming_transaction_to_unconfirmed_pool() + { + //arrange + A.CallTo(() => _blockVerifier.VerifyTransaction(A.Ignored, A>.Ignored)) + .Returns(0); + + A.CallTo(() => _unconfirmedTransactionPool.Add(A.Ignored)) + .Returns(true); + + //act + var result = await _subject.RecieveTransaction(new Transaction() { TransactionId = new byte[0] }); + + //assert + A.CallTo(() => _blockVerifier.VerifyTransaction(A.Ignored, A>.Ignored)) + .MustHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Add(A.Ignored)) + .MustHaveHappened(); + + result.Should().Be(PeerDataResult.Relay); + } + + [Fact] + public async void should_locally_process_outcoming_transaction() + { + //arrange + var txn = new Transaction() { TransactionId = new byte[0] }; + + //act + await _subject.SendTransaction(txn); + + //assert + A.CallTo(() => _receiver.RecieveTransaction(txn)) + .MustHaveHappened(); + } + + [Fact] + public async void should_not_relay_invalid_outcoming_transaction() + { + //arrange + var txn = new Transaction() { TransactionId = new byte[0] }; + A.CallTo(() => _receiver.RecieveTransaction(txn)) + .Returns(PeerDataResult.Ignore); + + //act + await _subject.SendTransaction(txn); + + //assert + A.CallTo(() => _peerNetwork.BroadcastTransaction(txn)) + .MustNotHaveHappened(); + } + + [Fact] + public async void should_relay_valid_outcoming_transaction() + { + //arrange + var txn = new Transaction() { TransactionId = new byte[0] }; + A.CallTo(() => _receiver.RecieveTransaction(txn)) + .Returns(PeerDataResult.Relay); + + //act + await _subject.SendTransaction(txn); + + //assert + A.CallTo(() => _peerNetwork.BroadcastTransaction(txn)) + .MustHaveHappened(); + } + + + //TODO: receive block tests + } +} From b79b8fa06cf5eacb12eed4f9242989ef7100bbb3 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 24 Sep 2017 11:52:09 -0700 Subject: [PATCH 57/66] tests --- NBlockchain/Services/BlockchainNode.cs | 8 +- .../Services/BlockchainNodeTests.cs | 159 +++++++++++++++++- 2 files changed, 153 insertions(+), 14 deletions(-) diff --git a/NBlockchain/Services/BlockchainNode.cs b/NBlockchain/Services/BlockchainNode.cs index 6613d54..6dbad1d 100644 --- a/NBlockchain/Services/BlockchainNode.cs +++ b/NBlockchain/Services/BlockchainNode.cs @@ -16,11 +16,9 @@ public class BlockchainNode : IBlockchainNode private readonly INetworkParameters _parameters; private readonly IBlockRepository _blockRepository; private readonly IBlockVerifier _blockVerifier; - private readonly ILogger _logger; - + private readonly ILogger _logger; private readonly IForkRebaser _forkRebaser; - private readonly IReceiver _receiver; - //private readonly IExpectedBlockList _expectedBlockList; + private readonly IReceiver _receiver; private readonly IPeerNetwork _peerNetwork; private readonly AutoResetEvent _blockEvent = new AutoResetEvent(true); private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; @@ -94,7 +92,7 @@ private async Task RecieveBlockUnprotected(Block block) if (!await _blockVerifier.VerifyBlockRules(block, true)) { _logger.LogWarning($"Block rules failed for {BitConverter.ToString(block.Header.BlockId)}"); - return PeerDataResult.Ignore; + return PeerDataResult.Demerit; } var prevHeader = await _blockRepository.GetBlockHeader(block.Header.PreviousBlock); diff --git a/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs b/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs index 250b0a6..9f73fac 100644 --- a/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs +++ b/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; using Xunit; namespace NBlockchain.Tests.Services @@ -20,7 +21,7 @@ public class BlockchainNodeTests private readonly IBlockRepository _blockRepository; private readonly IBlockVerifier _blockVerifier; private readonly ILoggerFactory _loggerFactory; - private readonly IForkRebaser _forkRebaser; + private readonly IForkRebaser _forkRebaser; private readonly IPeerNetwork _peerNetwork; private readonly IUnconfirmedTransactionPool _unconfirmedTransactionPool; private readonly IDifficultyCalculator _difficultyCalculator; @@ -36,15 +37,15 @@ public BlockchainNodeTests() _peerNetwork = A.Fake(); _unconfirmedTransactionPool = A.Fake(); _difficultyCalculator = A.Fake(); - + _subject = new BlockchainNode(_blockRepository, _blockVerifier, _receiver, _loggerFactory, _forkRebaser, _parameters, _unconfirmedTransactionPool, _peerNetwork, _difficultyCalculator); _subject.PollTimer.Dispose(); } - + #region Incoming Transaction [Fact] public async void should_verify_incoming_transaction() - { + { await _subject.RecieveTransaction(new Transaction() { TransactionId = new byte[0] }); A.CallTo(() => _blockVerifier.VerifyTransaction(A.Ignored, A>.Ignored)) @@ -93,9 +94,11 @@ public async void should_add_valid_incoming_transaction_to_unconfirmed_pool() result.Should().Be(PeerDataResult.Relay); } + #endregion + #region Outgoing Transaction [Fact] - public async void should_locally_process_outcoming_transaction() + public async void should_locally_process_outgoing_transaction() { //arrange var txn = new Transaction() { TransactionId = new byte[0] }; @@ -109,7 +112,7 @@ public async void should_locally_process_outcoming_transaction() } [Fact] - public async void should_not_relay_invalid_outcoming_transaction() + public async void should_not_relay_invalid_outgoing_transaction() { //arrange var txn = new Transaction() { TransactionId = new byte[0] }; @@ -125,7 +128,7 @@ public async void should_not_relay_invalid_outcoming_transaction() } [Fact] - public async void should_relay_valid_outcoming_transaction() + public async void should_relay_valid_outgoing_transaction() { //arrange var txn = new Transaction() { TransactionId = new byte[0] }; @@ -139,8 +142,146 @@ public async void should_relay_valid_outcoming_transaction() A.CallTo(() => _peerNetwork.BroadcastTransaction(txn)) .MustHaveHappened(); } + #endregion + + //TODO: complete receive block tests + + #region Incoming Block + + [Fact] + public async void should_ignore_duplicate_blocks() + { + //arrange + var block = BuildBlock(); + A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + .Returns(true); + A.CallTo(() => _blockRepository.GetBestBlockHeader()) + .Returns(Task.FromResult(null)); - //TODO: receive block tests - } + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + .MustHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Ignore); + } + + [Fact] + public async void should_verify_incoming_block() + { + //arrange + var block = BuildBlock(); + A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + .Returns(false); + + A.CallTo(() => _blockVerifier.Verify(block)) + .Returns(false); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockVerifier.Verify(block)) + .MustHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Demerit); + } + + [Fact] + public async void should_execute_block_rules_for_incoming_block() + { + //arrange + var block = BuildBlock(); + A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + .Returns(false); + + A.CallTo(() => _blockVerifier.Verify(block)) + .Returns(true); + + A.CallTo(() => _blockVerifier.VerifyBlockRules(block, A.Ignored)) + .Returns(false); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockVerifier.VerifyBlockRules(block, A.Ignored)) + .MustHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Demerit); + } + + [Fact] + public async void should_request_previous_block_if_missing() + { + //arrange + var block = BuildBlock(); + Given_Verification_Passes(block); + + A.CallTo(() => _blockRepository.GetBlockHeader(block.Header.PreviousBlock)) + .Returns(Task.FromResult(null)); + + A.CallTo(() => _blockRepository.GetBestBlockHeader()) + .Returns(Task.FromResult(null)); + + A.CallTo(() => _blockRepository.IsEmpty()) + .Returns(false); + + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _peerNetwork.RequestBlock(block.Header.PreviousBlock)) + .MustHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + } + + #endregion + + #region Setup Helpers + + private Block BuildBlock() + { + return BuildBlock(new byte[] { 0x1, 0x2 }, new byte[] { 0x1, 0x1 }); + } + + private Block BuildBlock(byte[] blockId, byte[] previousBlock) + { + var result = new Block(); + result.Header.BlockId = blockId; + result.Header.PreviousBlock = previousBlock; + + return result; + } + + private void Given_Verification_Passes(Block block) + { + A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + .Returns(false); + + A.CallTo(() => _blockVerifier.Verify(block)) + .Returns(true); + + A.CallTo(() => _blockVerifier.VerifyBlockRules(block, A.Ignored)) + .Returns(true); + } + + + #endregion +} } From 65a7002e87b014c73c9707054aaf9ae49d2cd97b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 24 Sep 2017 15:32:00 -0700 Subject: [PATCH 58/66] tests --- .../Services/BlockchainNodeTests.cs | 299 +++++++++++++++++- 1 file changed, 294 insertions(+), 5 deletions(-) diff --git a/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs b/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs index 9f73fac..25859da 100644 --- a/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs +++ b/Tests/NBlockchain.Tests/Services/BlockchainNodeTests.cs @@ -228,7 +228,7 @@ public async void should_request_previous_block_if_missing() { //arrange var block = BuildBlock(); - Given_Verification_Passes(block); + GivenVerificationPasses(block); A.CallTo(() => _blockRepository.GetBlockHeader(block.Header.PreviousBlock)) .Returns(Task.FromResult(null)); @@ -251,6 +251,218 @@ public async void should_request_previous_block_if_missing() .MustNotHaveHappened(); } + [Fact] + public async void should_verify_transactions_when_block_is_tip() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsNextTip(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty); + + A.CallTo(() => _blockVerifier.VerifyTransactions(block)) + .Returns(false); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockVerifier.VerifyTransactions(block)) + .MustHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Demerit); + } + + [Fact] + public async void should_ignore_block_with_unexpected_height() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsNextTip(block, block.Header.Height, block.Header.Timestamp - 1, block.Header.Difficulty); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Ignore); + } + + [Fact] + public async void should_ignore_block_with_unexpected_timestamp() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsNextTip(block, block.Header.Height - 1, block.Header.Timestamp + 1, block.Header.Difficulty); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Ignore); + } + + [Fact] + public async void should_ignore_block_with_unexpected_difficulty() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsNextTip(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty - 1); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + + result.Should().Be(PeerDataResult.Ignore); + } + + [Fact] + public async void should_not_verify_transaction_rules_on_fork_block() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsFork(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockVerifier.VerifyTransactions(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + } + + [Fact] + public async void should_save_block_in_orphan_pool_when_no_parent_in_mainchain() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsFork(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty); + + A.CallTo(() => _blockRepository.HaveSecondaryBlock(block.Header.BlockId)) + .Returns(false); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockRepository.AddSecondaryBlock(block)) + .MustHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + } + + [Fact] + public async void should_rebase_chain_when_path_to_better_tip_exists() + { + //arrange + var block = BuildBlock(); + var divergentHeader = new BlockHeader() { BlockId = new byte[] { 0xA } }; + GivenVerificationPasses(block); + GivenBlockIsFork(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty); + + A.CallTo(() => _blockRepository.GetDivergentHeader(block.Header.BlockId)) + .Returns(divergentHeader); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _forkRebaser.RebaseChain(divergentHeader.BlockId, block.Header.BlockId)) + .MustHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + } + + [Fact] + public async void should_not_rebase_chain_when_no_path_to_better_tip_exists() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsFork(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty); + + A.CallTo(() => _blockRepository.GetDivergentHeader(block.Header.BlockId)) + .Returns(Task.FromResult(null)); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _forkRebaser.RebaseChain(A.Ignored, A.Ignored)) + .MustNotHaveHappened(); + + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustNotHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustNotHaveHappened(); + } + + [Fact] + public async void should_add_block_if_valid() + { + //arrange + var block = BuildBlock(); + GivenVerificationPasses(block); + GivenBlockIsNextTip(block, block.Header.Height - 1, block.Header.Timestamp - 1, block.Header.Difficulty); + + A.CallTo(() => _blockVerifier.VerifyTransactions(block)) + .Returns(true); + + //act + var result = await _subject.RecieveBlock(block); + + //assert + A.CallTo(() => _blockRepository.AddBlock(block)) + .MustHaveHappened(); + + A.CallTo(() => _unconfirmedTransactionPool.Remove(block.Transactions)) + .MustHaveHappened(); + + result.Should().Be(PeerDataResult.Relay); + } + #endregion #region Setup Helpers @@ -265,11 +477,12 @@ private Block BuildBlock(byte[] blockId, byte[] previousBlock) var result = new Block(); result.Header.BlockId = blockId; result.Header.PreviousBlock = previousBlock; + result.Header.Height = 100; return result; } - private void Given_Verification_Passes(Block block) + private void GivenVerificationPasses(Block block) { A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) .Returns(false); @@ -280,8 +493,84 @@ private void Given_Verification_Passes(Block block) A.CallTo(() => _blockVerifier.VerifyBlockRules(block, A.Ignored)) .Returns(true); } - - #endregion -} + private void GivenBlockIsNextTip(Block block, uint prevHeight, long prevTimestamp, uint prevDifficulty) + { + var prevHeader = new BlockHeader() + { + BlockId = block.Header.PreviousBlock, + Height = prevHeight, + Timestamp = prevTimestamp, + Difficulty = prevDifficulty + }; + + A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + .Returns(false); + + A.CallTo(() => _blockRepository.GetBlockHeader(block.Header.PreviousBlock)) + .Returns(prevHeader); + + A.CallTo(() => _blockRepository.GetBestBlockHeader()) + .Returns(prevHeader); + + A.CallTo(() => _blockRepository.IsEmpty()) + .Returns(false); + + A.CallTo(() => _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp)) + .Returns(prevDifficulty); + + } + + private void GivenBlockIsFork(Block block, uint prevHeight, long prevTimestamp, uint prevDifficulty) + { + var prevHeader = new BlockHeader() + { + BlockId = block.Header.PreviousBlock, + Height = prevHeight, + Timestamp = prevTimestamp, + Difficulty = prevDifficulty + }; + + var mainTip = new BlockHeader() + { + BlockId = new byte[] { 0xFF, 0x2, 0x3 }, + Height = block.Header.Height - 1, + Timestamp = block.Header.Timestamp - 1, + Difficulty = prevDifficulty + }; + + var prevMain = new BlockHeader() + { + BlockId = new byte[] { 0xFF, 0x2, 0x3, 0x4 }, + Height = block.Header.Height - 2, + Timestamp = block.Header.Timestamp - 2, + Difficulty = prevDifficulty + }; + + A.CallTo(() => _blockRepository.HavePrimaryBlock(block.Header.BlockId)) + .Returns(false); + + A.CallTo(() => _blockRepository.GetBlockHeader(block.Header.PreviousBlock)) + .Returns(prevHeader); + + A.CallTo(() => _blockRepository.GetBestBlockHeader()) + .Returns(mainTip); + + A.CallTo(() => _blockRepository.GetPrimaryHeader(block.Header.Height)) + .Returns(mainTip); + + A.CallTo(() => _blockRepository.GetPrimaryHeader(block.Header.Height - 1)) + .Returns(prevMain); + + A.CallTo(() => _blockRepository.IsEmpty()) + .Returns(false); + + A.CallTo(() => _difficultyCalculator.CalculateDifficulty(prevHeader.Timestamp)) + .Returns(prevDifficulty); + + } + + + #endregion + } } From 80f919e35d1abbdf4e58819eb93820b41b9710ec Mon Sep 17 00:00:00 2001 From: Peter Dowdy Date: Mon, 2 Oct 2017 23:39:23 -0700 Subject: [PATCH 59/66] Rough pass at NAT traversal This passes smoke tests but none of the providers know what to do with it yet. --- NBlockChain/Interfaces/INatTraversal.cs | 12 +++++ NBlockChain/Interfaces/IProvideUpnpDevice.cs | 44 ++++++++++++++++++ .../Services/NatTraversal/NoTraversal.cs | 13 ++++++ .../NatTraversal/StaticPortForwarding.cs | 22 +++++++++ .../UpnpAutodetectPortForwarding.cs | 46 +++++++++++++++++++ .../NatTraversal/UpnpStaticPortForwarding.cs | 42 +++++++++++++++++ NBlockchain/Models/BlockchainOptions.cs | 26 ++++++++++- NBlockchain/NBlockchain.csproj | 1 + NBlockchain/Services/Net/TcpPeerNetwork.cs | 22 +++++---- Samples/DigitalCurrency/Program.cs | 1 + 10 files changed, 218 insertions(+), 11 deletions(-) create mode 100644 NBlockChain/Interfaces/INatTraversal.cs create mode 100644 NBlockChain/Interfaces/IProvideUpnpDevice.cs create mode 100644 NBlockChain/Services/NatTraversal/NoTraversal.cs create mode 100644 NBlockChain/Services/NatTraversal/StaticPortForwarding.cs create mode 100644 NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs create mode 100644 NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs diff --git a/NBlockChain/Interfaces/INatTraversal.cs b/NBlockChain/Interfaces/INatTraversal.cs new file mode 100644 index 0000000..15304c6 --- /dev/null +++ b/NBlockChain/Interfaces/INatTraversal.cs @@ -0,0 +1,12 @@ +using System.Net; +using System.Threading.Tasks; +using NBlockchain.Services.Net; +using Open.Nat; + +namespace NBlockchain.Interfaces +{ + public interface INatTraversal + { + string ConfigureNatTraversal(IPAddress ownAddress, int internalPort); + } +} \ No newline at end of file diff --git a/NBlockChain/Interfaces/IProvideUpnpDevice.cs b/NBlockChain/Interfaces/IProvideUpnpDevice.cs new file mode 100644 index 0000000..c6971b2 --- /dev/null +++ b/NBlockChain/Interfaces/IProvideUpnpDevice.cs @@ -0,0 +1,44 @@ +using System.Collections; +using System.Collections.Generic; +using System.Net; +using Open.Nat; + +namespace NBlockchain.Interfaces +{ + public interface IProvideUpnpDevice + { + IPAddress GetExternalIp(); + void CreateMapping(int internalPort, int externalPort, string mappingIdentifier); + IEnumerable GetAllMappings(); + } + + public class OpenNatUpnpProvider : IProvideUpnpDevice + { + private readonly NatDevice _device; + + public OpenNatUpnpProvider() + { + var devicetask = new NatDiscoverer().DiscoverDeviceAsync(); + devicetask.Wait(); + _device = devicetask.Result; + } + public IPAddress GetExternalIp() + { + var ipTask = _device.GetExternalIPAsync(); + ipTask.Wait(); + return ipTask.Result; + } + + public void CreateMapping(int internalPort, int externalPort, string mappingIdentifier) + { + _device.CreatePortMapAsync(new Mapping(Protocol.Tcp, internalPort, externalPort, mappingIdentifier)); + } + + public IEnumerable GetAllMappings() + { + var mappingsTask = _device.GetAllMappingsAsync(); + mappingsTask.Wait(); + return mappingsTask.Result; + } + } +} \ No newline at end of file diff --git a/NBlockChain/Services/NatTraversal/NoTraversal.cs b/NBlockChain/Services/NatTraversal/NoTraversal.cs new file mode 100644 index 0000000..9ab890f --- /dev/null +++ b/NBlockChain/Services/NatTraversal/NoTraversal.cs @@ -0,0 +1,13 @@ +using System.Net; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class NoTraversal : INatTraversal + { + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + return $"{ownAddress}:{internalPort}"; + } + } +} \ No newline at end of file diff --git a/NBlockChain/Services/NatTraversal/StaticPortForwarding.cs b/NBlockChain/Services/NatTraversal/StaticPortForwarding.cs new file mode 100644 index 0000000..e114918 --- /dev/null +++ b/NBlockChain/Services/NatTraversal/StaticPortForwarding.cs @@ -0,0 +1,22 @@ +using System.Net; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class StaticPortForwarding : INatTraversal + { + private readonly int _staticExternalPort; + private readonly IProvideUpnpDevice _upnpDeviceProvider; + + public StaticPortForwarding(int staticExternalPort, IProvideUpnpDevice upnpDeviceProvider) + { + _staticExternalPort = staticExternalPort; + _upnpDeviceProvider = upnpDeviceProvider; + } + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + var ip = _upnpDeviceProvider.GetExternalIp(); + return $"{ip}:{_staticExternalPort}"; + } + } +} \ No newline at end of file diff --git a/NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs b/NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs new file mode 100644 index 0000000..f77161b --- /dev/null +++ b/NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs @@ -0,0 +1,46 @@ +using System; +using System.Linq; +using System.Net; +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class UpnpAutodetectPortForwarding : INatTraversal + { + private readonly string _description; + private readonly IProvideUpnpDevice _upnpDeviceProvider; + private readonly ILogger _logger; + + public UpnpAutodetectPortForwarding(string description, ILoggerFactory loggerFactory, IProvideUpnpDevice upnpDeviceProvider) + { + _description = description; + _upnpDeviceProvider = upnpDeviceProvider; + _logger = loggerFactory.CreateLogger(); + } + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + var ip = _upnpDeviceProvider.GetExternalIp(); + var allMappings = _upnpDeviceProvider.GetAllMappings(); + + var existingMapping = allMappings.SingleOrDefault(m => m.PrivatePort == internalPort && m.Description == _description); + if (existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) + { + return $"{existingMapping.PublicIP}:{existingMapping.PublicPort}"; + } + if (!existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) + { + _logger.LogError($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + throw new Exception($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + } + for (var nextPort = 49151; nextPort < 65535; nextPort++) + { + if (allMappings.Any(m => m.PublicPort == nextPort && !m.PrivateIP.Equals(ownAddress))) continue; + _upnpDeviceProvider.CreateMapping(internalPort, nextPort, _description); + return $"{ip}:{nextPort}"; + } + _logger.LogError("No available ports found."); + throw new Exception("No available ports found."); + } + } +} \ No newline at end of file diff --git a/NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs b/NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs new file mode 100644 index 0000000..48c4ce6 --- /dev/null +++ b/NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs @@ -0,0 +1,42 @@ +using System; +using System.Linq; +using System.Net; +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class UpnpStaticPortForwarding : INatTraversal + { + private readonly string _description; + private readonly int _externalPort; + private readonly IProvideUpnpDevice _upnpDeviceProvider; + private readonly ILogger _logger; + + public UpnpStaticPortForwarding(string description, int externalPort, ILoggerFactory loggerFactory, IProvideUpnpDevice upnpDeviceProvider) + { + _description = description; + _externalPort = externalPort; + _upnpDeviceProvider = upnpDeviceProvider; + _logger = loggerFactory.CreateLogger(); + } + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + var ip = _upnpDeviceProvider.GetExternalIp(); + var allMappings = _upnpDeviceProvider.GetAllMappings(); + + var existingMapping = allMappings.SingleOrDefault(m =>m.PrivatePort == internalPort && m.Description == _description); + if (existingMapping?.PublicIP?.Equals(ownAddress) ?? false) + { + return $"{existingMapping.PublicIP}:{existingMapping.PublicPort}"; + } + if (!existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) + { + _logger.LogError($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + throw new Exception($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + } + _upnpDeviceProvider.CreateMapping(internalPort, _externalPort, _description); + return $"{ip}:{_externalPort}"; + } + } +} \ No newline at end of file diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index 2371bff..a034daf 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -11,6 +11,8 @@ using NBlockchain.Services.Net; using NBlockchain.Services.PeerDiscovery; using NBlockchain.Rules; +using NBlockchain.Services.NatTraversal; +using Org.BouncyCastle.Bcpg.OpenPgp; namespace NBlockchain.Models { @@ -68,9 +70,28 @@ public void UseBlockRepository(Func factory) public void UseTcpPeerNetwork(uint port) { - Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); + Services.AddSingleton(sp => new TcpPeerNetwork(port, sp.GetService(), sp.GetService(), sp.GetServices(), sp.GetService(), sp.GetService(), sp.GetService(), sp.GetService())); } + public void UseStaticNatTraversal(uint staticPort) + { + Services.AddSingleton(sp => new StaticPortForwarding((int)staticPort, sp.GetService())); + } + + public void UseNoNatTraversal() + { + Services.AddSingleton(); + } + + public void UseUpnpAutoNatTraversal(string mappingIdentifier) + { + Services.AddSingleton(sp => new UpnpAutodetectPortForwarding(mappingIdentifier, sp.GetService(), sp.GetService())); + } + + public void UsePnpStaticNatTraversal(uint staticPort, string mappingIdentifier) + { + Services.AddSingleton(sp => new UpnpStaticPortForwarding(mappingIdentifier, (int)staticPort, sp.GetService(), sp.GetService())); + } public void UseDataConnection(string connectionString) { Services.AddSingleton(sp => new DataConnection(connectionString)); @@ -157,6 +178,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Transient); AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); @@ -167,7 +189,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); - + AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); diff --git a/NBlockchain/NBlockchain.csproj b/NBlockchain/NBlockchain.csproj index 7517762..e6887b9 100644 --- a/NBlockchain/NBlockchain.csproj +++ b/NBlockchain/NBlockchain.csproj @@ -26,6 +26,7 @@ + diff --git a/NBlockchain/Services/Net/TcpPeerNetwork.cs b/NBlockchain/Services/Net/TcpPeerNetwork.cs index e929beb..3f22a90 100644 --- a/NBlockchain/Services/Net/TcpPeerNetwork.cs +++ b/NBlockchain/Services/Net/TcpPeerNetwork.cs @@ -22,6 +22,7 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable //TODO: break this class up into smaller pieces private const int TargetOutgoingCount = 8; private readonly uint _port; + private readonly INatTraversal _natTraversal; private string _serviceId = "BC"; private int _version = 1; @@ -45,16 +46,17 @@ public class TcpPeerNetwork : IPeerNetwork, IDisposable private Timer _sharePeersTimer; private Timer _discoveryTimer; - private string _internalConnsctionString; - private string _externalConnsctionString; + private string _internalConnectionString; + private string _externalConnectionString; private object _duplicateLock = new object(); public Guid NodeId { get; private set; } - public TcpPeerNetwork(uint port, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IUnconfirmedTransactionPool unconfirmedTransactionPool, IReceiver reciever) + public TcpPeerNetwork(uint port, INatTraversal natTraversal, IBlockRepository blockRepository, IEnumerable discoveryServices, ILoggerFactory loggerFactory, IOwnAddressResolver ownAddressResolver, IUnconfirmedTransactionPool unconfirmedTransactionPool, IReceiver reciever) { _port = port; + _natTraversal = natTraversal; _reciever = reciever; _logger = loggerFactory.CreateLogger(); _blockRepository = blockRepository; @@ -584,11 +586,11 @@ private void AdvertiseToPeers() { try { - if (_internalConnsctionString != null) - ds.AdvertiseLocal(_internalConnsctionString); + if (_internalConnectionString != null) + ds.AdvertiseLocal(_internalConnectionString); - if (_externalConnsctionString != null) - ds.AdvertiseGlobal(_externalConnsctionString); + if (_externalConnectionString != null) + ds.AdvertiseGlobal(_externalConnectionString); } catch (Exception ex) { @@ -601,9 +603,11 @@ private void DiscoverOwnConnectionStrings() { var ownAddress = _ownAddressResolver.ResolvePreferredLocalAddress(); if (ownAddress != null) - _internalConnsctionString = $"tcp://{ownAddress}:{_port}"; + _internalConnectionString = $"tcp://{ownAddress}:{_port}"; - //TODO: external addresses + var externalConnectionString = _natTraversal.ConfigureNatTraversal(ownAddress, (int)_port); + if (externalConnectionString != null) + _externalConnectionString = $"tcp://{externalConnectionString}"; } private static byte[] SerializeObject(object data) diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 884ed33..530239e 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -100,6 +100,7 @@ private static IServiceProvider ConfigureForMongoDB(string db, uint port) services.AddBlockchain(x => { x.UseTcpPeerNetwork(port); + x.UseUpnpAutoNatTraversal("My Currency"); x.UseMongoDB(@"mongodb://localhost:27017", db) .UseInstructionRepository(); //x.AddPeerDiscovery(sp => new StaticPeerDiscovery("tcp://localhost:503")); From 0d7948365c459b72c77bf07d067aee4847813c56 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 7 Jan 2018 14:00:17 -0800 Subject: [PATCH 60/66] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61c26a9..4fb2e16 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ NBlockchain is a .NET standard library for building blockchain applications. **This project is currently in alpha status and any contributions are welcome.** -The idea is that the developer would only need to focus on the data on rules for a blockchain and not worry about having to build all the infrastructure to facilitate a blockchain. +The idea is that the developer would only need to focus on the data and rules for a blockchain and not worry about having to build all the infrastructure to facilitate a blockchain. The developer would need to * Define the schema of data / transactions they would like to store on the blockchain From 9d5636e771be41fc4a80455a5734acd20c54cb6c Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 8 Apr 2018 08:14:39 -0700 Subject: [PATCH 61/66] fix dir name --- NBlockChain/Interfaces/INatTraversal.cs | 12 ----- NBlockChain/Interfaces/IProvideUpnpDevice.cs | 44 ---------------- .../Services/Database/BlockStatistics.cs | 13 ----- .../Services/Database/PersistedBlock.cs | 52 ------------------- .../Services/Database/PersistedEntity.cs | 40 -------------- .../Services/NatTraversal/NoTraversal.cs | 13 ----- .../NatTraversal/StaticPortForwarding.cs | 22 -------- .../UpnpAutodetectPortForwarding.cs | 46 ---------------- .../NatTraversal/UpnpStaticPortForwarding.cs | 42 --------------- 9 files changed, 284 deletions(-) delete mode 100644 NBlockChain/Interfaces/INatTraversal.cs delete mode 100644 NBlockChain/Interfaces/IProvideUpnpDevice.cs delete mode 100644 NBlockChain/Services/Database/BlockStatistics.cs delete mode 100644 NBlockChain/Services/Database/PersistedBlock.cs delete mode 100644 NBlockChain/Services/Database/PersistedEntity.cs delete mode 100644 NBlockChain/Services/NatTraversal/NoTraversal.cs delete mode 100644 NBlockChain/Services/NatTraversal/StaticPortForwarding.cs delete mode 100644 NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs delete mode 100644 NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs diff --git a/NBlockChain/Interfaces/INatTraversal.cs b/NBlockChain/Interfaces/INatTraversal.cs deleted file mode 100644 index 15304c6..0000000 --- a/NBlockChain/Interfaces/INatTraversal.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Net; -using System.Threading.Tasks; -using NBlockchain.Services.Net; -using Open.Nat; - -namespace NBlockchain.Interfaces -{ - public interface INatTraversal - { - string ConfigureNatTraversal(IPAddress ownAddress, int internalPort); - } -} \ No newline at end of file diff --git a/NBlockChain/Interfaces/IProvideUpnpDevice.cs b/NBlockChain/Interfaces/IProvideUpnpDevice.cs deleted file mode 100644 index c6971b2..0000000 --- a/NBlockChain/Interfaces/IProvideUpnpDevice.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Net; -using Open.Nat; - -namespace NBlockchain.Interfaces -{ - public interface IProvideUpnpDevice - { - IPAddress GetExternalIp(); - void CreateMapping(int internalPort, int externalPort, string mappingIdentifier); - IEnumerable GetAllMappings(); - } - - public class OpenNatUpnpProvider : IProvideUpnpDevice - { - private readonly NatDevice _device; - - public OpenNatUpnpProvider() - { - var devicetask = new NatDiscoverer().DiscoverDeviceAsync(); - devicetask.Wait(); - _device = devicetask.Result; - } - public IPAddress GetExternalIp() - { - var ipTask = _device.GetExternalIPAsync(); - ipTask.Wait(); - return ipTask.Result; - } - - public void CreateMapping(int internalPort, int externalPort, string mappingIdentifier) - { - _device.CreatePortMapAsync(new Mapping(Protocol.Tcp, internalPort, externalPort, mappingIdentifier)); - } - - public IEnumerable GetAllMappings() - { - var mappingsTask = _device.GetAllMappingsAsync(); - mappingsTask.Wait(); - return mappingsTask.Result; - } - } -} \ No newline at end of file diff --git a/NBlockChain/Services/Database/BlockStatistics.cs b/NBlockChain/Services/Database/BlockStatistics.cs deleted file mode 100644 index 6511c3a..0000000 --- a/NBlockChain/Services/Database/BlockStatistics.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Services.Database -{ - public class BlockStatistics - { - public int BlockTime { get; set; } - - public DateTime TimeStamp { get; set; } - } -} diff --git a/NBlockChain/Services/Database/PersistedBlock.cs b/NBlockChain/Services/Database/PersistedBlock.cs deleted file mode 100644 index 109ae11..0000000 --- a/NBlockChain/Services/Database/PersistedBlock.cs +++ /dev/null @@ -1,52 +0,0 @@ -using LiteDB; -using NBlockchain.Models; -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Services.Database -{ - public class PersistedBlock : PersistedEntity - { - public PersistedBlock() - { - } - - public PersistedBlock(Block block) - { - Entity = new BlockInfo(block); - Statistics = new BlockStatistics(); - Statistics.TimeStamp = new DateTime(block.Header.Timestamp); - } - } - - public class PersistedOrphan : PersistedEntity - { - public PersistedOrphan() - { - } - - public PersistedOrphan(Block block) - { - Entity = block; - } - } - - - public class BlockInfo - { - public BlockHeader Header { get; set; } - public MerkleNode MerkleRootNode { get; set; } - - public BlockInfo() - { - } - - public BlockInfo(Block block) - { - Header = block.Header; - MerkleRootNode = block.MerkleRootNode; - } - } - -} diff --git a/NBlockChain/Services/Database/PersistedEntity.cs b/NBlockChain/Services/Database/PersistedEntity.cs deleted file mode 100644 index 1cd1f17..0000000 --- a/NBlockChain/Services/Database/PersistedEntity.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace NBlockchain.Services.Database -{ - public class PersistedEntity - where TStats : new() - { - public TKey Id { get; set; } - public TEntity Entity { get; set; } - public TStats Statistics { get; set; } - - public PersistedEntity() - { - Statistics = new TStats(); - } - - public PersistedEntity(TEntity entity) - { - Entity = entity; - Statistics = new TStats(); - } - } - - public class PersistedEntity - { - public TKey Id { get; set; } - public TEntity Entity { get; set; } - - public PersistedEntity() - { - } - - public PersistedEntity(TEntity entity) - { - Entity = entity; - } - } -} diff --git a/NBlockChain/Services/NatTraversal/NoTraversal.cs b/NBlockChain/Services/NatTraversal/NoTraversal.cs deleted file mode 100644 index 9ab890f..0000000 --- a/NBlockChain/Services/NatTraversal/NoTraversal.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Net; -using NBlockchain.Interfaces; - -namespace NBlockchain.Services.NatTraversal -{ - public class NoTraversal : INatTraversal - { - public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) - { - return $"{ownAddress}:{internalPort}"; - } - } -} \ No newline at end of file diff --git a/NBlockChain/Services/NatTraversal/StaticPortForwarding.cs b/NBlockChain/Services/NatTraversal/StaticPortForwarding.cs deleted file mode 100644 index e114918..0000000 --- a/NBlockChain/Services/NatTraversal/StaticPortForwarding.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Net; -using NBlockchain.Interfaces; - -namespace NBlockchain.Services.NatTraversal -{ - public class StaticPortForwarding : INatTraversal - { - private readonly int _staticExternalPort; - private readonly IProvideUpnpDevice _upnpDeviceProvider; - - public StaticPortForwarding(int staticExternalPort, IProvideUpnpDevice upnpDeviceProvider) - { - _staticExternalPort = staticExternalPort; - _upnpDeviceProvider = upnpDeviceProvider; - } - public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) - { - var ip = _upnpDeviceProvider.GetExternalIp(); - return $"{ip}:{_staticExternalPort}"; - } - } -} \ No newline at end of file diff --git a/NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs b/NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs deleted file mode 100644 index f77161b..0000000 --- a/NBlockChain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using Microsoft.Extensions.Logging; -using NBlockchain.Interfaces; - -namespace NBlockchain.Services.NatTraversal -{ - public class UpnpAutodetectPortForwarding : INatTraversal - { - private readonly string _description; - private readonly IProvideUpnpDevice _upnpDeviceProvider; - private readonly ILogger _logger; - - public UpnpAutodetectPortForwarding(string description, ILoggerFactory loggerFactory, IProvideUpnpDevice upnpDeviceProvider) - { - _description = description; - _upnpDeviceProvider = upnpDeviceProvider; - _logger = loggerFactory.CreateLogger(); - } - public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) - { - var ip = _upnpDeviceProvider.GetExternalIp(); - var allMappings = _upnpDeviceProvider.GetAllMappings(); - - var existingMapping = allMappings.SingleOrDefault(m => m.PrivatePort == internalPort && m.Description == _description); - if (existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) - { - return $"{existingMapping.PublicIP}:{existingMapping.PublicPort}"; - } - if (!existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) - { - _logger.LogError($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); - throw new Exception($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); - } - for (var nextPort = 49151; nextPort < 65535; nextPort++) - { - if (allMappings.Any(m => m.PublicPort == nextPort && !m.PrivateIP.Equals(ownAddress))) continue; - _upnpDeviceProvider.CreateMapping(internalPort, nextPort, _description); - return $"{ip}:{nextPort}"; - } - _logger.LogError("No available ports found."); - throw new Exception("No available ports found."); - } - } -} \ No newline at end of file diff --git a/NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs b/NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs deleted file mode 100644 index 48c4ce6..0000000 --- a/NBlockChain/Services/NatTraversal/UpnpStaticPortForwarding.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using Microsoft.Extensions.Logging; -using NBlockchain.Interfaces; - -namespace NBlockchain.Services.NatTraversal -{ - public class UpnpStaticPortForwarding : INatTraversal - { - private readonly string _description; - private readonly int _externalPort; - private readonly IProvideUpnpDevice _upnpDeviceProvider; - private readonly ILogger _logger; - - public UpnpStaticPortForwarding(string description, int externalPort, ILoggerFactory loggerFactory, IProvideUpnpDevice upnpDeviceProvider) - { - _description = description; - _externalPort = externalPort; - _upnpDeviceProvider = upnpDeviceProvider; - _logger = loggerFactory.CreateLogger(); - } - public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) - { - var ip = _upnpDeviceProvider.GetExternalIp(); - var allMappings = _upnpDeviceProvider.GetAllMappings(); - - var existingMapping = allMappings.SingleOrDefault(m =>m.PrivatePort == internalPort && m.Description == _description); - if (existingMapping?.PublicIP?.Equals(ownAddress) ?? false) - { - return $"{existingMapping.PublicIP}:{existingMapping.PublicPort}"; - } - if (!existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) - { - _logger.LogError($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); - throw new Exception($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); - } - _upnpDeviceProvider.CreateMapping(internalPort, _externalPort, _description); - return $"{ip}:{_externalPort}"; - } - } -} \ No newline at end of file From 2e8194431a7c951931212e9a47eceb78031a4247 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sun, 8 Apr 2018 08:16:53 -0700 Subject: [PATCH 62/66] fix dir name --- NBlockchain/Interfaces/INatTraversal.cs | 12 +++++ NBlockchain/Interfaces/IProvideUpnpDevice.cs | 44 ++++++++++++++++ .../Services/Database/BlockStatistics.cs | 13 +++++ .../Services/Database/PersistedBlock.cs | 52 +++++++++++++++++++ .../Services/Database/PersistedEntity.cs | 40 ++++++++++++++ .../Services/NatTraversal/NoTraversal.cs | 13 +++++ .../NatTraversal/StaticPortForwarding.cs | 22 ++++++++ .../UpnpAutodetectPortForwarding.cs | 46 ++++++++++++++++ .../NatTraversal/UpnpStaticPortForwarding.cs | 42 +++++++++++++++ 9 files changed, 284 insertions(+) create mode 100644 NBlockchain/Interfaces/INatTraversal.cs create mode 100644 NBlockchain/Interfaces/IProvideUpnpDevice.cs create mode 100644 NBlockchain/Services/Database/BlockStatistics.cs create mode 100644 NBlockchain/Services/Database/PersistedBlock.cs create mode 100644 NBlockchain/Services/Database/PersistedEntity.cs create mode 100644 NBlockchain/Services/NatTraversal/NoTraversal.cs create mode 100644 NBlockchain/Services/NatTraversal/StaticPortForwarding.cs create mode 100644 NBlockchain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs create mode 100644 NBlockchain/Services/NatTraversal/UpnpStaticPortForwarding.cs diff --git a/NBlockchain/Interfaces/INatTraversal.cs b/NBlockchain/Interfaces/INatTraversal.cs new file mode 100644 index 0000000..15304c6 --- /dev/null +++ b/NBlockchain/Interfaces/INatTraversal.cs @@ -0,0 +1,12 @@ +using System.Net; +using System.Threading.Tasks; +using NBlockchain.Services.Net; +using Open.Nat; + +namespace NBlockchain.Interfaces +{ + public interface INatTraversal + { + string ConfigureNatTraversal(IPAddress ownAddress, int internalPort); + } +} \ No newline at end of file diff --git a/NBlockchain/Interfaces/IProvideUpnpDevice.cs b/NBlockchain/Interfaces/IProvideUpnpDevice.cs new file mode 100644 index 0000000..c6971b2 --- /dev/null +++ b/NBlockchain/Interfaces/IProvideUpnpDevice.cs @@ -0,0 +1,44 @@ +using System.Collections; +using System.Collections.Generic; +using System.Net; +using Open.Nat; + +namespace NBlockchain.Interfaces +{ + public interface IProvideUpnpDevice + { + IPAddress GetExternalIp(); + void CreateMapping(int internalPort, int externalPort, string mappingIdentifier); + IEnumerable GetAllMappings(); + } + + public class OpenNatUpnpProvider : IProvideUpnpDevice + { + private readonly NatDevice _device; + + public OpenNatUpnpProvider() + { + var devicetask = new NatDiscoverer().DiscoverDeviceAsync(); + devicetask.Wait(); + _device = devicetask.Result; + } + public IPAddress GetExternalIp() + { + var ipTask = _device.GetExternalIPAsync(); + ipTask.Wait(); + return ipTask.Result; + } + + public void CreateMapping(int internalPort, int externalPort, string mappingIdentifier) + { + _device.CreatePortMapAsync(new Mapping(Protocol.Tcp, internalPort, externalPort, mappingIdentifier)); + } + + public IEnumerable GetAllMappings() + { + var mappingsTask = _device.GetAllMappingsAsync(); + mappingsTask.Wait(); + return mappingsTask.Result; + } + } +} \ No newline at end of file diff --git a/NBlockchain/Services/Database/BlockStatistics.cs b/NBlockchain/Services/Database/BlockStatistics.cs new file mode 100644 index 0000000..6511c3a --- /dev/null +++ b/NBlockchain/Services/Database/BlockStatistics.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Database +{ + public class BlockStatistics + { + public int BlockTime { get; set; } + + public DateTime TimeStamp { get; set; } + } +} diff --git a/NBlockchain/Services/Database/PersistedBlock.cs b/NBlockchain/Services/Database/PersistedBlock.cs new file mode 100644 index 0000000..109ae11 --- /dev/null +++ b/NBlockchain/Services/Database/PersistedBlock.cs @@ -0,0 +1,52 @@ +using LiteDB; +using NBlockchain.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Database +{ + public class PersistedBlock : PersistedEntity + { + public PersistedBlock() + { + } + + public PersistedBlock(Block block) + { + Entity = new BlockInfo(block); + Statistics = new BlockStatistics(); + Statistics.TimeStamp = new DateTime(block.Header.Timestamp); + } + } + + public class PersistedOrphan : PersistedEntity + { + public PersistedOrphan() + { + } + + public PersistedOrphan(Block block) + { + Entity = block; + } + } + + + public class BlockInfo + { + public BlockHeader Header { get; set; } + public MerkleNode MerkleRootNode { get; set; } + + public BlockInfo() + { + } + + public BlockInfo(Block block) + { + Header = block.Header; + MerkleRootNode = block.MerkleRootNode; + } + } + +} diff --git a/NBlockchain/Services/Database/PersistedEntity.cs b/NBlockchain/Services/Database/PersistedEntity.cs new file mode 100644 index 0000000..1cd1f17 --- /dev/null +++ b/NBlockchain/Services/Database/PersistedEntity.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBlockchain.Services.Database +{ + public class PersistedEntity + where TStats : new() + { + public TKey Id { get; set; } + public TEntity Entity { get; set; } + public TStats Statistics { get; set; } + + public PersistedEntity() + { + Statistics = new TStats(); + } + + public PersistedEntity(TEntity entity) + { + Entity = entity; + Statistics = new TStats(); + } + } + + public class PersistedEntity + { + public TKey Id { get; set; } + public TEntity Entity { get; set; } + + public PersistedEntity() + { + } + + public PersistedEntity(TEntity entity) + { + Entity = entity; + } + } +} diff --git a/NBlockchain/Services/NatTraversal/NoTraversal.cs b/NBlockchain/Services/NatTraversal/NoTraversal.cs new file mode 100644 index 0000000..9ab890f --- /dev/null +++ b/NBlockchain/Services/NatTraversal/NoTraversal.cs @@ -0,0 +1,13 @@ +using System.Net; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class NoTraversal : INatTraversal + { + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + return $"{ownAddress}:{internalPort}"; + } + } +} \ No newline at end of file diff --git a/NBlockchain/Services/NatTraversal/StaticPortForwarding.cs b/NBlockchain/Services/NatTraversal/StaticPortForwarding.cs new file mode 100644 index 0000000..e114918 --- /dev/null +++ b/NBlockchain/Services/NatTraversal/StaticPortForwarding.cs @@ -0,0 +1,22 @@ +using System.Net; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class StaticPortForwarding : INatTraversal + { + private readonly int _staticExternalPort; + private readonly IProvideUpnpDevice _upnpDeviceProvider; + + public StaticPortForwarding(int staticExternalPort, IProvideUpnpDevice upnpDeviceProvider) + { + _staticExternalPort = staticExternalPort; + _upnpDeviceProvider = upnpDeviceProvider; + } + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + var ip = _upnpDeviceProvider.GetExternalIp(); + return $"{ip}:{_staticExternalPort}"; + } + } +} \ No newline at end of file diff --git a/NBlockchain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs b/NBlockchain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs new file mode 100644 index 0000000..f77161b --- /dev/null +++ b/NBlockchain/Services/NatTraversal/UpnpAutodetectPortForwarding.cs @@ -0,0 +1,46 @@ +using System; +using System.Linq; +using System.Net; +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class UpnpAutodetectPortForwarding : INatTraversal + { + private readonly string _description; + private readonly IProvideUpnpDevice _upnpDeviceProvider; + private readonly ILogger _logger; + + public UpnpAutodetectPortForwarding(string description, ILoggerFactory loggerFactory, IProvideUpnpDevice upnpDeviceProvider) + { + _description = description; + _upnpDeviceProvider = upnpDeviceProvider; + _logger = loggerFactory.CreateLogger(); + } + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + var ip = _upnpDeviceProvider.GetExternalIp(); + var allMappings = _upnpDeviceProvider.GetAllMappings(); + + var existingMapping = allMappings.SingleOrDefault(m => m.PrivatePort == internalPort && m.Description == _description); + if (existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) + { + return $"{existingMapping.PublicIP}:{existingMapping.PublicPort}"; + } + if (!existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) + { + _logger.LogError($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + throw new Exception($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + } + for (var nextPort = 49151; nextPort < 65535; nextPort++) + { + if (allMappings.Any(m => m.PublicPort == nextPort && !m.PrivateIP.Equals(ownAddress))) continue; + _upnpDeviceProvider.CreateMapping(internalPort, nextPort, _description); + return $"{ip}:{nextPort}"; + } + _logger.LogError("No available ports found."); + throw new Exception("No available ports found."); + } + } +} \ No newline at end of file diff --git a/NBlockchain/Services/NatTraversal/UpnpStaticPortForwarding.cs b/NBlockchain/Services/NatTraversal/UpnpStaticPortForwarding.cs new file mode 100644 index 0000000..48c4ce6 --- /dev/null +++ b/NBlockchain/Services/NatTraversal/UpnpStaticPortForwarding.cs @@ -0,0 +1,42 @@ +using System; +using System.Linq; +using System.Net; +using Microsoft.Extensions.Logging; +using NBlockchain.Interfaces; + +namespace NBlockchain.Services.NatTraversal +{ + public class UpnpStaticPortForwarding : INatTraversal + { + private readonly string _description; + private readonly int _externalPort; + private readonly IProvideUpnpDevice _upnpDeviceProvider; + private readonly ILogger _logger; + + public UpnpStaticPortForwarding(string description, int externalPort, ILoggerFactory loggerFactory, IProvideUpnpDevice upnpDeviceProvider) + { + _description = description; + _externalPort = externalPort; + _upnpDeviceProvider = upnpDeviceProvider; + _logger = loggerFactory.CreateLogger(); + } + public string ConfigureNatTraversal(IPAddress ownAddress, int internalPort) + { + var ip = _upnpDeviceProvider.GetExternalIp(); + var allMappings = _upnpDeviceProvider.GetAllMappings(); + + var existingMapping = allMappings.SingleOrDefault(m =>m.PrivatePort == internalPort && m.Description == _description); + if (existingMapping?.PublicIP?.Equals(ownAddress) ?? false) + { + return $"{existingMapping.PublicIP}:{existingMapping.PublicPort}"; + } + if (!existingMapping?.PrivateIP?.Equals(ownAddress) ?? false) + { + _logger.LogError($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + throw new Exception($"The port {internalPort} is in use by another IP: {existingMapping.PrivateIP}"); + } + _upnpDeviceProvider.CreateMapping(internalPort, _externalPort, _description); + return $"{ip}:{_externalPort}"; + } + } +} \ No newline at end of file From 4a4ec19c96ebc740087af5f22ed751e79446a29a Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Wed, 11 Jul 2018 19:17:11 -0700 Subject: [PATCH 63/66] Fix typo in readme --- Samples/DigitalCurrency/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples/DigitalCurrency/readme.md b/Samples/DigitalCurrency/readme.md index 6dc8c73..d2b8ab9 100644 --- a/Samples/DigitalCurrency/readme.md +++ b/Samples/DigitalCurrency/readme.md @@ -1,7 +1,7 @@ # Digital currency sample for NBlockchain This example demonstrates how to implement a very basic digital currency with NBlockchain. -(This does not follow the flexible input/output locking script scheme that Bitcoin uses but it just meant to illustrate an application of NBlockchin) +(This does not follow the flexible input/output locking script scheme that Bitcoin uses but it just meant to illustrate an application of NBlockchain) ## Define our instruction types From 258d61099b6b0f9586ef261e7178bb026599d7f6 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 13 Apr 2019 17:53:47 -0700 Subject: [PATCH 64/66] fix sample --- NBlockchain/Models/BlockchainOptions.cs | 1 + Samples/DigitalCurrency/Program.cs | 6 ++++-- Samples/DigitalCurrency/readme.md | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NBlockchain/Models/BlockchainOptions.cs b/NBlockchain/Models/BlockchainOptions.cs index a034daf..5bbd15f 100644 --- a/NBlockchain/Models/BlockchainOptions.cs +++ b/NBlockchain/Models/BlockchainOptions.cs @@ -183,6 +183,7 @@ internal void FillDefaults() AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); AddDefault(ServiceLifetime.Singleton); + AddDefault(ServiceLifetime.Singleton); //AddDefault(ServiceLifetime.Singleton); //AddDefault(ServiceLifetime.Singleton); diff --git a/Samples/DigitalCurrency/Program.cs b/Samples/DigitalCurrency/Program.cs index 530239e..3e45178 100644 --- a/Samples/DigitalCurrency/Program.cs +++ b/Samples/DigitalCurrency/Program.cs @@ -27,8 +27,8 @@ class Program static void Main(string[] args) { - var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); - //var serviceProvider = ConfigureForLiteDb("node.db", 10500); + //var serviceProvider = ConfigureForMongoDB("DigitalCurrency", 10500); + var serviceProvider = ConfigureForLiteDb("node.db", 10500); _host = serviceProvider.GetService(); _miner = serviceProvider.GetService(); @@ -69,6 +69,7 @@ private static IServiceProvider ConfigureForLiteDb(string db, uint port) x.UseDataConnection("node.db"); x.UseInstructionRepository(); x.UseTcpPeerNetwork(port); + x.UseNoNatTraversal(); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); x.AddInstructionType(); x.AddInstructionType(); @@ -105,6 +106,7 @@ private static IServiceProvider ConfigureForMongoDB(string db, uint port) .UseInstructionRepository(); //x.AddPeerDiscovery(sp => new StaticPeerDiscovery("tcp://localhost:503")); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + x.UseNoNatTraversal(); x.AddInstructionType(); x.AddInstructionType(); x.AddTransactionRule(); diff --git a/Samples/DigitalCurrency/readme.md b/Samples/DigitalCurrency/readme.md index d2b8ab9..f69bf01 100644 --- a/Samples/DigitalCurrency/readme.md +++ b/Samples/DigitalCurrency/readme.md @@ -168,6 +168,7 @@ services.AddBlockchain(x => x.UseInstructionRepository(); x.UseTcpPeerNetwork(port); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + x.UseNoNatTraversal(); x.AddInstructionType(); x.AddInstructionType(); x.AddTransactionRule(); @@ -192,6 +193,7 @@ services.AddBlockchain(x => x.UseMongoDB(@"mongodb://localhost:27017", db) .UseInstructionRepository(); x.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + x.UseNoNatTraversal(); x.AddInstructionType(); x.AddInstructionType(); x.AddTransactionRule(); From 5afd4706891633599e36165abed5ac037bbacf56 Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 13 Apr 2019 17:58:11 -0700 Subject: [PATCH 65/66] Update readme.md --- doc/readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/readme.md b/doc/readme.md index d23ae56..c2ea0d4 100644 --- a/doc/readme.md +++ b/doc/readme.md @@ -27,6 +27,7 @@ services.AddBlockchain(blockchain => blockchain.UseInstructionRepository(); blockchain.UseTcpPeerNetwork(port); blockchain.UseMulticastDiscovery("My Currency", "224.100.0.1", 8088); + blockchain.UseNoNatTraversal(); blockchain.AddInstructionType(); blockchain.AddInstructionType(); blockchain.AddTransactionRule(); From a53447c2482bce7ea226fa1b7568286589b2535b Mon Sep 17 00:00:00 2001 From: Daniel Gerlag Date: Sat, 18 Jan 2025 08:01:32 -0800 Subject: [PATCH 66/66] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4fb2e16..cca73ac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # NBlockchain +[](https://api.gitsponsors.com/api/badge/link?p=z/lqjMTxNXS+Grw5EvE2h1VgbJs53TSeZowLI8QCrFtKRepan+g1DGIo9DBtV0XQDpvYV9xJb96BKxxNCOYsWQ==) + NBlockchain is a .NET standard library for building blockchain applications. **This project is currently in alpha status and any contributions are welcome.**