最近体验了blockexplorer.comblockchain.infoqukuai.com等基于区块链的查询工具,通过读代码发现
基本架构如下:前端web基于socket.io或者REST实现,后端加一层mongodb/mysql等数据库来代替单机leveldb做数据存储
目的应该是:1. 加速查询2. 做更高层的数据分析3.做分布式数据库
思考:这些online的查询固然可以方便我们的日常用, 那如何与相关应用集成呢? 我们是否可以通过简单的rpc命令实现同等的效果?有几个用处:1 . 大家都可以做自己的qukuai.com或blockchain.info的查询:)2.  集成RPC命令到自己的店铺,收款后查询用3.  集成到钱包应用4.  其他应用场景
cmd分析:
根据高度height查block hash
./bitcoin-cli getblockhash 19999
00000000ba36eb929dc90170a96ee3efb76cbebee0e0e5c4da9eb0b6e74d9124
2. 然后根据block hash查block 信息./bitcoin-cli getblock 00000000ba36eb929dc90170a96ee3efb76cbebee0e0e5c4da9eb0b6e74d9124{    "hash" : "00000000ba36eb929dc90170a96ee3efb76cbebee0e0e5c4da9eb0b6e74d9124",    "confirmations" : 263032,    "size" : 215,    "height" : 19999,    "version" : 1,    "merkleroot" : "c1b09fa6bdc0b12b15cc1400d598ffed29dd33b2e282093a48646d1b7b380c98",    "tx" : [        "c1b09fa6bdc0b12b15cc1400d598ffed29dd33b2e282093a48646d1b7b380c98"    ],    "time" : 1248291140,    "nonce" : 1085206531,    "bits" : "1d00ffff",    "difficulty" : 1.00000000,    "chainwork" : "00000000000000000000000000000000000000000000000000004e204e204e20",    "previousblockhash" : "000000006eb5c2799b0f5fafab6435daeecef8e7f609b731c9879c3f74f28c73",    "nextblockhash" : "00000000770ebe897270ca5f6d539d8afb4ea4f4e757761a34ca82e17207d886"}

3. 根据tx查询单笔交易的信息:没建index时,只能查询自己钱包的信息,若不是钱包的交易,则返回如下:./bitcoin-cli getrawtransaction c1b09fa6bdc0b12b15cc1400d598ffed29dd33b2e282093a48646d1b7b380c98
error: {"code":-5,"message":"Invalid or non-wallet transaction id"}
那怎么办呢? 直接分析代码找原因:// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlockbool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow){    CBlockIndex *pindexSlow = NULL;    {        LOCK(cs_main);        {            if (mempool.lookup(hash, txOut))            {                return true;            }        }
        if (fTxIndex) {            CDiskTxPos postx;            if (pblocktree->ReadTxIndex(hash, postx)) {                CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);                CBlockHeader header;                try {                    file >> header;                    fseek(file, postx.nTxOffset, SEEK_CUR);                    file >> txOut;                } catch (std::exception &e) {                    return error("%s : Deserialize or I/O error - %s", __func__, e.what());                }                hashBlock = header.GetHash();                if (txOut.GetHash() != hash)                    return error("%s : txid mismatch", __func__);                return true;            }        }
        if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it            int nHeight = -1;            {                CCoinsViewCache &view = *pcoinsTip;                CCoins coins;                if (view.GetCoins(hash, coins))                    nHeight = coins.nHeight;            }            if (nHeight > 0)                pindexSlow = chainActive[nHeight];        }    }
    if (pindexSlow) {        CBlock block;        if (ReadBlockFromDisk(block, pindexSlow)) {            BOOST_FOREACH(const CTransaction &tx, block.vtx) {                if (tx.GetHash() == hash) {                    txOut = tx;                    hashBlock = pindexSlow->GetBlockHash();                    return true;                }            }        }    }
    return false;}

我们可以看出若fTxIndex为true,则可以直接搜索index获取block信息通过-reindex -txindex建立索引,调用:./bitcoind -reindex -txindex这个过程在我的mac上跑了数个小时。。。。。。-txindex               Maintain a full transaction index (default: 0)-reindex               Rebuild block chain index from current blk000??.dat files再次查询./bitcoin-cli getrawtransaction c1b09fa6bdc0b12b15cc1400d598ffed29dd33b2e282093a48646d1b7b380c98
01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0168ffffffff0100f2052a01000000434104cab13751ae7f0e0e49f8fb345e931bc6a6349502da0cbcad98e9d95110ebde5ca7af9eb09639c022ac251b44d0fa200b54011198a405984a8ff92ea9028d6d60ac00000000
解码交易数据./bitcoin-cli decoderawtransaction 01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0168ffffffff0100f2052a01000000434104cab13751ae7f0e0e49f8fb345e931bc6a6349502da0cbcad98e9d95110ebde5ca7af9eb09639c022ac251b44d0fa200b54011198a405984a8ff92ea9028d6d60ac00000000{    "txid" : "c1b09fa6bdc0b12b15cc1400d598ffed29dd33b2e282093a48646d1b7b380c98",    "version" : 1,    "locktime" : 0,    "vin" : [        {            "coinbase" : "04ffff001d0168",            "sequence" : 4294967295        }    ],    "vout" : [        {            "value" : 50.00000000,            "n" : 0,            "scriptPubKey" : {                "asm" : "04cab13751ae7f0e0e49f8fb345e931bc6a6349502da0cbcad98e9d95110ebde5ca7af9eb09639c022ac251b44d0fa200b54011198a405984a8ff92ea9028d6d60 OP_CHECKSIG",                "hex" : "4104cab13751ae7f0e0e49f8fb345e931bc6a6349502da0cbcad98e9d95110ebde5ca7af9eb09639c022ac251b44d0fa200b54011198a405984a8ff92ea9028d6d60ac",                "reqSigs" : 1,                "type" : "pubkey",                "addresses" : [                    "1KWj3Jk8xvS6fDdhQBsfmerscSGsS6CMiS"            }        }
}
以上过程基本满足了大部分的查询需求:输入交易ID、区块高度或哈希值(BTC)至于通过"地址"查询,需要通过搜集这个地址对应的交易输入输出存入数据库部分代码如下:function updateKeys($hash160,$pubkey,$blockhash){        global $db;        $address=hash160ToAddress($hash160);        $result=pg_fetch_assoc(pg_query_params($db,"SELECT pubkey,encode(hash160,'hex') AS hash160 FROM keys WHERE hash160=decode($1,'hex')",array($hash160)));        if(!$result && !is_null($pubkey))        {                pg_query_params($db, "INSERT INTO keys VALUES (decode($1,'hex'),$2,decode($3,'hex'),decode($4,'hex'));",array($hash160,$address,$pubkey,$blockhash));        }        else if(!$result)        {                pg_query_params($db, "INSERT INTO keys(hash160,address,firstseen) VALUES (decode($1,'hex'),$2,decode($3,'hex'));",array($hash160,$address,$blockhash));        }        else if($result && !is_null($pubkey) && is_null($result["pubkey"]))        {                if($result["hash160"]!=strtolower(hash160($pubkey)))                {                        sleep(10);                        die("Hashes don't match");                }                pg_query_params($db, "UPDATE keys SET pubkey = decode($1,'hex') WHERE hash160=decode($2,'hex');",array($pubkey,$hash160));        }}
案例分析:如何获取一笔交易的输入地址?(此case可用在获取打款地址上)如何根据txid获取打款地址呢?其实可以基于blockchain的链式结构逆向推导
首先 gettransaction  txid    [vin] => Array        (            [0] => Array                (                    [txid] => 63876d10a13f3810a1d568c6ac7154f9b8a590cfc91cf8a17756fb099addf2b5                    [vout] => 1获取到此次的tx得信息详细,根据vin里的txid逆向查找, txid二次查询,gettransaction  vin->txid然后根据vout索引得到输入address    [vout] => Array        (            [0] => Array                (                    [value] => 0.16928006                    [n] => 0                    [scriptPubKey] => Array                        (                            [asm] => OP_DUP OP_HASH160 1715447427ac1cdfb7c5ba359154c37c5e9caa2b OP_EQUALVERIFY OP_CHECKSIG                            [hex] => 76a9141715447427ac1cdfb7c5ba359154c37c5e9caa2b88ac                            [reqSigs] => 1                            [type] => pubkeyhash                            [addresses] => Array                                (                                    [0] => LML1HJvP8jfeiwgSVmYfNGsedYfDrzKmq3                                )                        )



本文纯属偶然,一个朋友要做个店铺应用支持BTC付款,欲根据txid获取打款地址,花了点时间研究了下,记录下来。
如有雷同,纯属巧合。
作者:btcrobot