使用C# .NET查询本地比特币区块链

180

我试图仅使用本地存储的区块链(通过比特币核心下载)来检查给定比特币地址的余额。类似于这种方法(使用 NBitCoin 和/或 QBitNinja),但无需访问网络:

private static readonly QBitNinjaClient client = new QBitNinjaClient(Network.Main);

public decimal CheckBalance(BitcoinPubKeyAddress address)
{
    var balanceModel = client.GetBalance(address, true).Result;
    decimal balance = 0;

    if (balanceModel.Operations.Count > 0)
    {
        var unspentCoins = new List<Coin>();
        foreach (var operation in balanceModel.Operations)
            unspentCoins.AddRange(operation.ReceivedCoins.Select(coin => coin as Coin));
        balance = unspentCoins.Sum(x => x.Amount.ToDecimal(MoneyUnit.BTC));
    }
    return balance;
}

以上示例需要访问网络。我需要在离线情况下执行相同的操作。我想到了类似于这样的东西,但显然它不起作用:

public decimal CheckBalanceLocal(BitcoinPubKeyAddress address)
{
    var node = Node.ConnectToLocal(Network.Main);
    node.VersionHandshake();
    var chain = node.GetChain();

    var store = new BlockStore(@"F:\Program Files\Bitcoin\Cache\blocks", Network.Main);

    var index = new IndexedBlockStore(new InMemoryNoSqlRepository(), store);
    index.ReIndex();

    var headers = chain.ToEnumerable(false).ToArray();

    var balance = (
        from header in headers
        select index.Get(header.HashBlock) 
        into block
        from tx in block.Transactions
        from txout in tx.Outputs
        where txout.ScriptPubKey.GetDestinationAddress(Network.Main) == address
        select txout.Value.ToDecimal(MoneyUnit.BTC)).Sum();

    return balance;
}
  1. 查询期间挂起
  2. 我想要一些东西来代替InMemoryNoSqlRepository,将其存储到文件中,以防止使用ReIndex()导致所有内容变慢。

我的要求是通过查询存储在我的磁盘上的块来以与第一种方法相同的方式检查余额

实际上,我需要的可能只是对这个问题的答案:


1
理想情况下,我希望像第二种方法那样仅使用NBitcoin,但它不起作用,我不知道为什么。IIS上的QBitNinja服务器是我的第二选择,但正如您提到的BitcoinLib,我可能会在最后尝试一下。这里的余额只是一个例子,我想查询本地区块链的许多有趣的事情。虽然我认为BitcoinLib已经不再更新了。我感激任何帮助,看起来你是唯一一个迄今为止正在尝试的人:)。所以如果没有人给出更好的答案,我会给你50个声望值。 - rvnlord
12
您好,这是一个老的问题,但我昨天看到了它。我使用NBitcoin尝试了您的代码。只有在我注释掉这行代码index.ReIndex();和这行代码var chain = node.GetChain();之后,您的代码才能在我的本地节点上部分运行。我还稍微修改了一下我的代码,用GetBlocks代替了GetChain,并做了类似的更改使其正常工作。我不想被踩踏,所以我发表了这个评论:)。希望对您有所帮助。 - Hey24sheep
19
你的代码能运行吗?那就把它发布为答案。我相信没有人会因为一个可行的解决方案而对你投反对票。 - Martin Braun
5
为什么人们害怕发表解决方案并遭到负评?这是否意味着比特币不像我们想象的那样可靠?作为比特币投机者,我是否不应该担心?这是针对@Hey24sheep和其他人的评论所提出的问题。是否应该警惕? - Rich Bianco
1
@rvnlord 你好,很抱歉我没有代码和节点可以检查。当我得到回应时,我尝试了再次运行它,但是无法获得之前的结果。那是很久以前的事情了。 - Hey24sheep
显示剩余10条评论
2个回答

4
你的问题中的第一和第二次尝试完全不同。第一种方法使用json rpc向守护进程请求余额(我敢说这是正确的方式——你不需要亲自拆开汽车引擎并手动推动活塞来实现前进,对吧?),第二种方法试图直接打开守护进程的数据库并直接计算余额。
你不需要“需要一个网络”才能查询本地主机,你只需要安装了TCP/IP支持的机器,因此可以通过任何方式使用第一种方法——通过使用将相关json-rpc数据写入http请求的库,或通过自己形成相关的http请求。
BitcoinLib可能已经不再维护(我不确定),但这不会使其无法查询您的本地守护程序;据我所知,GetBalance没有被移除或重构,我使用最新的bitcoind与BitcoinLib进行各种操作,包括GetBalance。

1
我完全忘记了这个问题。@Hey24sheep解决了这个问题,尽管我现在没有完整的本地节点可用。当时我需要尝试查询本地和离线数据。我需要比使用第三方API更快地查询它。 - rvnlord

1

如果想要使用C# .NET查询本地存储在计算机上的比特币区块链,并检查一个比特币地址的余额,可以使用NBitcoin库以及自定义的块数据存储解决方案。

using NBitcoin;
using NBitcoin.Protocol;
using NBitcoin.Protocol.Behaviors;
using System;
using System.IO;
using System.Linq;

public class LocalBlockchainBalanceChecker
{
    private readonly Network network;
    private readonly string blocksFolderPath;

    public LocalBlockchainBalanceChecker(Network network, string blocksFolderPath)
    {
        this.network = network;
        this.blocksFolderPath = blocksFolderPath;
    }

    public decimal CheckBalanceLocal(BitcoinPubKeyAddress address)
    {
        using (var node = CreateNode())
        {
            var chain = node.GetChain();
            var blockStore = CreateBlockStore();

            var balance = (
                from header in chain.ToEnumerable(false)
                let block = blockStore.Get(header.HashBlock)
                from tx in block.Transactions
                from txout in tx.Outputs
                where txout.ScriptPubKey.GetDestinationAddress(network) == address
                select txout.Value.ToDecimal(MoneyUnit.BTC)
            ).Sum();

            return balance;
        }
    }

    private Node CreateNode()
    {
        var node = Node.ConnectToLocal(network);
        node.VersionHandshake();
        return node;
    }

    private BlockStore CreateBlockStore()
    {
        var blockRepository = new FileSystemBlockRepository(blocksFolderPath);
        var blockStore = new BlockStore(blockRepository, network);
        return blockStore;
    }
}

public class FileSystemBlockRepository : NoSqlRepository
{
    private readonly string blocksFolderPath;

    public FileSystemBlockRepository(string blocksFolderPath)
    {
        this.blocksFolderPath = blocksFolderPath;
    }

    public override void Put(byte[] key, byte[] value)
    {
        var blockFilePath = GetBlockFilePath(key);
        File.WriteAllBytes(blockFilePath, value);
    }

    public override byte[] Get(byte[] key)
    {
        var blockFilePath = GetBlockFilePath(key);
        return File.Exists(blockFilePath) ? File.ReadAllBytes(blockFilePath) : null;
    }

    private string GetBlockFilePath(byte[] key)
    {
        var fileName = BitConverter.ToString(key).Replace("-", string.Empty);
        return Path.Combine(blocksFolderPath, fileName);
    }
}

要使用此代码,请创建LocalBlockchainBalanceChecker类的实例,并传递所需的网络(例如,Bitcoin主网的Network.Main)和存储块数据的文件夹路径。然后,您可以调用CheckBalanceLocal方法,提供要检查余额的比特币地址。
以下是一个示例用法:
var checker = new LocalBlockchainBalanceChecker(Network.Main, @"F:\Program Files\Bitcoin\Cache\blocks");
var address = new BitcoinPubKeyAddress("your_bitcoin_address", Network.Main);
decimal balance = checker.CheckBalanceLocal(address);
Console.WriteLine($"Balance: {balance} BTC");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接