专栏名称: 区块链技术学习
致力于区块链技术的学习和普及,对区块链技术和相关企业事件进行深度分析和研判,探索去中心化账本技术应用领域。
目录
相关文章推荐
FM1007福建交通广播  ·  北京一男子独自登山失联8天后遗体被找到,事发 ... ·  2 天前  
FM1007福建交通广播  ·  北京一男子独自登山失联8天后遗体被找到,事发 ... ·  2 天前  
沈阳网  ·  游客在知名景区失联,官方通报→ ·  2 天前  
沈阳网  ·  游客在知名景区失联,官方通报→ ·  2 天前  
51好读  ›  专栏  ›  区块链技术学习

如何将Bitcoin比特币区块链数据导入关系数据库

区块链技术学习  · 公众号  ·  · 2019-06-21 12:30

正文

来自:博客园,作者:深蓝居

链接:https://www.cnblogs.com/studyzy/p/export-bitcoin-blockchain-to-database.html


在接触了比特币和区块链后,我一直有一个想法,就是把所有比特币的区块链数据放入到关系数据库(比如SQL Server)中,然后当成一个数据仓库,做做比特币交易数据的各种分析。想法已经很久了,但是一直没有实施。最近正好有点时间,于是写了一个比特币区块链的导出导入程序。


之前我的一篇博客:在区块链上表白——使用C#将一句话放入比特币的区块链上 (链接:https://www.cnblogs.com/studyzy/p/write-string-to-blockchain.html)


介绍了怎么发起一笔比特币的交易,今天我们仍然是使用C#+NBitcoin,读取比特币钱包Bitcoin Core下载到本地的全量区块链数据,并将这些数据写入数据库。如果有和我一样想法的朋友,可以参考下面是我的操作过程:


一、准备


我们要解析的是存储在本地硬盘上的Bitcoin Core钱包的全量比特币数据,那么首先就是要下载并安装好Bitcoin Core,下载地址: https://bitcoin.org/en/download


然后就等着这个软件同步区块链数据吧。


目前比特币的区块链数据大概130G,所以可能需要好几天,甚至一个星期才能将所有区块链数据同步到本地。当然如果你很早就安装了这个软件,那么就太好了,毕竟要等好几天甚至一个星期,真的很痛苦。


二、建立比特币区块链数据模型


要进行区块链数据的分析,那么必须得对区块链的数据模型了解才行。我大概研究了一下,可以总结出4个实体:区块、交易、输入、输出。而其中的关系是,一个区块对应多个交易,一个交易对应多个输入和多个输出。除了Coinbase的输入外,一笔输入对应另一笔交易中的输出。于是我们可以得出这样的数据模型:


需要特别说明几点的是:


1.TxId是自增的int,我没有用TxHash做Transaction的PK,那是因为 TxHash根本就不唯一 啊!有好几个不同区块里面的第一笔交易,也就是Coinbase交易是相同的。这其实应该是异常数据,因为相同的TxHash将导致只能花费一次,所以这个矿工杯具了。


2.对于一笔Coinbase 的Transaction,其输入的PreOutTxId是0000000000000000000000000000000000000000000000000000000000000000,而其PreOutIndex是-1,这是一条不存在的TxOutput,所以我并没有建立TXInput和TxOutput的外键关联。


3.对于Block,PreId就是上一个Block的ID,而创世区块的PreId是0000000000000000000000000000000000000000000000000000000000000000,也是一个不存在的BlockId,所以我没有建立Block的自引用外键。


4.有很多字段其实并不是区块链数据结构中的,这些字段是我添加为了接下来方便分析用的。在导入的时候并没有值,需要经过一定的SQL运算才能得到。比如Trans里面的TotalInAmount,TransFee等。

我用的是PowerDesigner,建模完成后,生成SQL语句,即可。这是我的建表SQL:


create table Block ( 
   Height               int                  not null
   BlkId                char(64)             not null
   TxCount              int                  not null
   Size                 int                  not null
   PreId                char(64)             not null
   Timestamp            datetime             not null
   Nonce                bigint               not null
   Difficulty           double precision     not null
   Bits                 char(64)             not null
   Version              int                  not null
   TxMerkleRoot         char(64)             not null
   constraint PK_BLOCK primary key nonclustered (BlkId) 

go

/*==============================================================*/ 
/* Index: Block_Height                                          */ 
/*==============================================================*/ 
create unique clustered index Block_Height on Block ( 
Height ASC 

go

/*==============================================================*/ 
/* Table: Trans                                                 */ 
/*==============================================================*/ 
create table  Trans ( 
   TxId                 int                  not null
   BlkId                char(64)             not null
   TxHash               char(64)             not null
   Version              int                  not null
   InputCount           int                  not null
   OutputCount          int                  not null
   TotalOutAmount       bigint               not null
   TotalInAmount        bigint               not null
   TransFee             bigint               not null
   IsCoinbase           bit                  not null
   IsHeightLock         bit                  not null
   IsTimeLock           bit                  not null
   LockTimeValue        int                  not null
   Size                 int                  not null
   TransTime            datetime             not null
   constraint PK_TRANS primary key (TxId) 

go

/*==============================================================*/ 
/* Index: Relationship_1_FK                                     */ 
/*==============================================================*/ 
create index Relationship_1_FK on Trans ( 
BlkId ASC 

go

/*==============================================================*/ 
/* Index: Trans_Hash                                            */ 
/*==============================================================*/ 
create index Trans_Hash on Trans ( 
TxHash ASC 

go

/*==============================================================*/ 
/* Table: TxInput                                               */ 
/*==============================================================*/ 
create table TxInput ( 
   TxId                 int                  not null
   Idx                  int                  not null
   Amount               bigint               not null
   PrevOutTxId          char(64)             not null
   PrevOutIndex         int                  not null
   PaymentScriptLen     int                  not null
   PaymentScript        varchar(8000)        not null
   Address              char(58)             null
   constraint PK_TXINPUT primary key (TxId, Idx) 

go

/*==============================================================*/ 
/* Index: Relationship_2_FK                                     */  
/*==============================================================*/ 
create index Relationship_2_FK on TxInput ( 
TxId ASC 

go

/*==============================================================*/ 
/* Table: TxOutput                                              */ 
/*==============================================================*/ 
create table TxOutput ( 
   TxId                 int                  not null
   Idx                  int                  not null
   Amount               bigint               not null
   ScriptPubKeyLen      int                  not null
   ScriptPubKey         varchar(8000)        not null
   Address              char(58)             null
   IsUnspendable        bit                  not null
   IsPayToScriptHash    bit                  not null
   IsValid              bit                  not null
   IsSpent              bit                  not null
   constraint PK_TXOUTPUT primary key (TxId, Idx) 

go

/*==============================================================*/ 
/* Index: Relationship_3_FK                                     */ 
/*==============================================================*/ 
create index Relationship_3_FK on TxOutput ( 
TxId ASC 

go

alter table Trans 
   add constraint FK_TRANS_RELATIONS_BLOCK foreign key (BlkId) 
      references Block (BlkId) 
go

alter table TxInput 
   add constraint FK_TXINPUT_RELATIONS_TRANS foreign key (TxId) 
      references Trans (TxId) 
go

alter table TxOutput 
   add constraint FK_TXOUTPUT_RELATIONS_TRANS foreign key (TxId) 
      references Trans (TxId) 
go


三、导出区块链数据为CSV


数据模型有了,接下来我们就是建立对应的表,然后写程序将比特币的Block写入到数据库中。


我本来用的是EntityFramework来实现插入数据库的操作。但是后来发现实在太慢,插入一个Block甚至要等10多20秒,这要等到何年何月才能插入完啊!我试了各种方案,比如写原生的SQL,用事务,用LINQToSQL等,性能都很不理想。最后终于找到了一个好办法,那就是直接导出为文本文件(比如CSV格式),然后用SQL Server的Bulk Insert命令来实现批量导入,这是我已知的最快的写入数据库的方法。


解析Bitcoin Core下载下来的所有比特币区块链数据用的还是NBitcoin这个开源库。只需要用到其中的BlockStore 类,即可轻松实现区块链数据的解析。


以下是我将区块链数据解析为我们的Block对象的代码:


private static void LoadBlock2DB(string localPath, int start

    var store = new BlockStore(localPath, Network.Main); 
    int i = -1
    BlockToCsvHelper helper = new BlockToCsvHelper(height);

    foreach (var block in store.Enumerate(false)) 
    { 
        i++; 
        if (i         { 
            continue
        }

        try 
         { 
            log.Debug("Start load Block " + i + ": " + block.Item.Header + " from file:" +  block.BlockPosition.ToString()); 
            var blk = LoadBlock(block, i);//将NBitcoin的Block转换为我们建模的Block对象 
            helper.WriteBitcoin2Csv(blk);//将我们的Block对象转换为CSV保存 
        } 
        catch (Exception ex) 
         { 
            log.Error("保存Block到数据库时异常,请手动载入,i=" + i, ex); 
        }

    } 
    Console.WriteLine("--------End-----------"); 
    Console.ReadLine(); 
}


private static Block LoadBlock(StoredBlock block, int i

    var blk = new Block() 
    { 
         BlkId = block.Item.Header.ToString(), 
        Difficulty = block.Item.Header.Bits.Difficulty, 
        Bits = block.Item.Header.Bits.ToString(), 
        Height = i, 
        Nonce = block.Item.Header.Nonce, 
        PreId = block.Item.Header.HashPrevBlock.ToString(), 
        TxMerkleRoot = block.Item.GetMerkleRoot().ToString(), 
        Size = block.Item.GetSerializedSize(), 
        Version = block.Item.Header.Version, 
        Timestamp = block.Item.Header.BlockTime.UtcDateTime, 
        TxCount = block.Item.Transactions.Count 
    }; 
    log.Debug("Transaction Count="






请到「今天看啥」查看全文