MetacoSA / NBitcoin.Litecoin

Litecoin extensions for NBitcoin
MIT License
21 stars 24 forks source link

Some operations do not work at Litecoin Mainnet #1

Closed vonzepp closed 7 years ago

vonzepp commented 7 years ago
  1. I tried to create simple SPV client for Litecoin Main network (Test network does not work completely).

            NBitcoin.Litecoin.Networks.Register();
            var parameters = new NodeConnectionParameters();
            parameters.TemplateBehaviors.Add(new AddressManagerBehavior(new AddressManager()));
            var chain = new ConcurrentChain();
            parameters.TemplateBehaviors.Add(new ChainBehavior(chain));
            var group = new NodesGroup(NBitcoin.Litecoin.Networks.Mainnet, parameters, new NodeRequirement
            {
                RequiredServices = NodeServices.Network,
                SupportSPV = true,
            });
            group.MaximumNodeConnection = 6;
            group.Connect();

    Later I checked chain.Height by timer... The latest block I've got was 12095, but it seems that ChainBehavior stucks on that block. chain.Tip.Height = 12095 The block is valid ( http://ltc.blockr.io/block/info/12095 ), but I wait for several minutes and chain.Tip / chain.Height were NOT updated anymore.

    Block 12095
    {
    Height = 12095
    Hash = 812c8cdf824da11cfbb31453e8284d1b9cdf8c9269bcbf7640d614f43cd7eac0
    MerkleRoot = 265919990cfcf2e8c75fd4732dbdcc0f3294683dfabb36b04aca61a0160dcb13
    Time = 16.10.2011 12:47:18 +00:00
    }
  2. Then I tried to disable ChainBehavior and download the chain manually:

    var cts = new CancellationTokenSource();
    var chain = node.GetChain(null, cts.Token);

    I've got the same exception from all connected nodes:

    NBitcoin.Protocol.ProtocolException: An header which does not pass proof of work verification has been received
    at NBitcoin.Protocol.Node.SynchronizeChain(ChainBase chain, uint256 hashStop, CancellationToken cancellationToken) in E:\Sources\NBitcoin\NBitcoin\Protocol\Node.cs:line 1252
    at NBitcoin.Protocol.Node.GetChain(uint256 hashStop, CancellationToken cancellationToken) in E:\Sources\NBitcoin\NBitcoin\Protocol\Node.cs:line 1167

    Nodes were:

    Node HandShaked ([::ffff:91.240.141.175]:9333) ping time is: 00:00:00.0320018
    Node HandShaked ([::ffff:46.101.6.204]:9333) ping time is: 00:00:00.0430025
    Node HandShaked ([::ffff:176.31.126.191]:9336) ping time is: 00:00:00.1240071
    Node HandShaked ([::ffff:108.31.15.154]:9333) ping time is: 00:00:00.1240071
    Node HandShaked ([::ffff:98.201.3.182]:9333) ping time is: 00:00:00.1900109
    Node HandShaked ([::ffff:216.164.138.13]:9333) ping time is: 00:00:00.2050117
    Node HandShaked ([::ffff:96.95.217.1]:9333) ping time is: 00:00:00.2160124
    Node HandShaked ([::ffff:219.113.244.52]:9333) ping time is: 00:00:00.2310132
    Node HandShaked ([::ffff:52.76.154.206]:9333) ping time is: 00:00:00.3800218
    Node HandShaked ([::ffff:62.106.7.62]:9333) ping time is: 00:00:00.8690497

    I attached sample project with my code.

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NBitcoin.Protocol;
using NBitcoin.Protocol.Behaviors;

namespace Test
{
    class Program
    {
        public static void Main(string[] args)
        {
            int nodesCount = 10;

            NBitcoin.Litecoin.Networks.Register();
            var parameters = new NodeConnectionParameters();
            parameters.TemplateBehaviors.Add(new AddressManagerBehavior(new AddressManager()));
            //var chain = new ConcurrentChain();
            //parameters.TemplateBehaviors.Add(new ChainBehavior(chain));
            var group = new NodesGroup(NBitcoin.Litecoin.Networks.Mainnet, parameters, new NodeRequirement
            {
                RequiredServices = NodeServices.Network, //Needed for SPV
                SupportSPV = true,
            });
            group.MaximumNodeConnection = nodesCount;
            group.Connect();

            PrintStatistics(group);

            //Waiting for connection with all nodes
            while (group.ConnectedNodes.Count != nodesCount)
            {
                Console.WriteLine($"{group.ConnectedNodes.Count} nodes connected");
                Thread.Sleep(TimeSpan.FromSeconds(3));
            }

            CancellationTokenSource cts = new CancellationTokenSource();
            DownloadChainFromAllNodes(group, cts);

            Console.WriteLine("Press Enter to abort all tasks");
            Console.ReadLine();
            cts.Cancel();
        }

        private static async void PrintStatistics(NodesGroup group)
        {
            while(true)
            { 
                Console.WriteLine($"{group.ConnectedNodes.Count} nodes connected");
                await Task.Delay(TimeSpan.FromSeconds(5));
            }
        }

        private static async void DownloadChainFromAllNodes(NodesGroup group, CancellationTokenSource cts)
        {
            int failedNodes = 0, goodNodes = 0;

            var tasks = group.ConnectedNodes.Select(node => Task.Run(() =>
            {
                try
                {
                    if (!node.IsConnected) return;

                    var time = node.PingPong(cts.Token);
                    Console.WriteLine($"Node {node} ping time is: {time}");
                    var ch = node.GetChain(null, cts.Token);
                    if (ch != null)
                    {
                        Console.WriteLine($"Last block is {ch.Tip?.Height}");
                        Interlocked.Increment(ref goodNodes);
                    }
                    Console.WriteLine($"Download from {node} completed ({goodNodes}/{failedNodes})");
                }
                catch (Exception ex)
                {
                    Interlocked.Increment(ref failedNodes);
                    Console.WriteLine($"Failed to download chain from node {node} ({goodNodes}/{failedNodes}): {ex}");
                }
            })).ToArray();

            await Task.Run(() =>
            {
                try
                {
                    Task.WaitAll(tasks, cts.Token);
                    Console.WriteLine("Download completed");
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("Download canceled");
                }
            });
        }
    }
}
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <PropertyGroup>
    <TreatWarningsAsErrors>True</TreatWarningsAsErrors>
    <TreatSpecificWarningsAsErrors />
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="NBitcoin" Version="3.0.2.18" />
    <PackageReference Include="NBitcoin.Litecoin" Version="1.0.0.1" />
  </ItemGroup>

</Project>
vonzepp commented 7 years ago

This code works on Bitcoin networks. Problem is only with Litecoin.

vonzepp commented 7 years ago

node.GetBlocks works fine on Litecoin. I'm able to get some of the latest blocks: http://ltc.blockr.io/block/info/1199773

                    var block = node.GetBlocks(new []{ new uint256("cf0a4d2bae4d5cf18e657d9347cfa0fe8b141ca037930f05bbb1687c40cea2b6") }, cts.Token)
                                .FirstOrDefault();

It seems that problem is only with gettting chain of BlockHeaders.

NicolasDorier commented 7 years ago

sorry for late reply, I have not seen your issue, I am checking it now.

NicolasDorier commented 7 years ago

I just fixed it, turned out Litecoin calculate work differently than bitcoin. Thanks for report, sorry for late reply. Update the package.

hawthornetr commented 6 years ago

chain = connector.Node.GetChain(); <-- with the latest nuget's for nbitcoin and ltc this call just never comes back. Works fine with BTC, not so fine with LTC, any help?

NicolasDorier commented 6 years ago

@hawthornetr it should work. The LTC chain is like 4 times bigger than Bitcoin so you need to wait way more.

rywem commented 6 years ago

Hey Nicolas,

I wanted to point out that there was a pretty significant performance difference between these versions, when running against NBitcoin.Litecoin: v0.0.59
v0.0.65

File in primary project: https://github.com/MetacoSA/NBitcoin/blob/master/NBitcoin/Protocol/Node.cs

Code Line

public IEnumerable<ChainedBlock> SynchronizeChain(ChainBase chain, uint256 hashStop = null, CancellationToken cancellationToken = default(CancellationToken))
{
    ...
    var headers = GetHeadersFromFork(oldTip, options, cancellationToken).ToList(); //<-- this line took a huge performance hit on v0.65,  The performance his was not an issue when run against v0.0.59
    ....
NicolasDorier commented 6 years ago

@cybervoid I indeed made some changes recently in this area. Can you try to pass options and disable PoWCheck and see if it is better?