get block by txid.

This commit is contained in:
Christopher Jeffrey 2014-12-03 12:14:23 -08:00
parent 9f52c538dd
commit 9ce652ff09
2 changed files with 231 additions and 2 deletions

View File

@ -481,6 +481,11 @@ Bitcoin.prototype.__defineGetter__('chainHeight', function() {
return this.getChainHeight();
});
Bitcoin.prototype.getBlockByTxid =
Bitcoin.prototype.getBlockByTx = function(txid, callback) {
return bitcoindjs.getBlockByTx(txid, callback);
};
Bitcoin.prototype.log =
Bitcoin.prototype.info = function() {
if (this.options.silent) return;

View File

@ -213,6 +213,7 @@ NAN_METHOD(GetMiningInfo);
NAN_METHOD(GetAddrTransactions);
NAN_METHOD(GetBestBlock);
NAN_METHOD(GetChainHeight);
NAN_METHOD(GetBlockByTx);
NAN_METHOD(GetBlockHex);
NAN_METHOD(GetTxHex);
@ -344,6 +345,12 @@ async_rescan(uv_work_t *req);
static void
async_rescan_after(uv_work_t *req);
static void
async_block_tx(uv_work_t *req);
static void
async_block_tx_after(uv_work_t *req);
static inline void
cblock_to_jsblock(const CBlock& cblock, CBlockIndex* cblock_index, Local<Object> jsblock, bool is_new);
@ -445,6 +452,18 @@ struct async_tx_data {
Persistent<Function> callback;
};
/**
* async_block_tx_data
*/
struct async_block_tx_data {
std::string err_msg;
std::string txid;
CBlock cblock;
CBlockIndex* cblock_index;
Persistent<Function> callback;
};
/**
* async_addrtx_data
*/
@ -553,6 +572,9 @@ static ctx_list *
read_addr(const std::string addr);
#endif
static bool
get_block_by_tx(const std::string itxhash, CBlock& rcblock, CBlockIndex **rcblock_index);
/**
* Functions
*/
@ -2089,6 +2111,116 @@ NAN_METHOD(GetChainHeight) {
NanReturnValue(NanNew<Number>((int)chainActive.Height())->ToInt32());
}
/**
* GetBlockByTx()
* bitcoindjs.getBlockByTx()
* Get block by tx hash (requires -txindex)
*/
NAN_METHOD(GetBlockByTx) {
NanScope();
if (args.Length() < 2
|| !args[0]->IsString()
|| !args[1]->IsFunction()) {
return NanThrowError(
"Usage: bitcoindjs.getBlockByTx(txid, callback)");
}
async_block_tx_data *data = new async_block_tx_data();
uv_work_t *req = new uv_work_t();
req->data = data;
String::Utf8Value hash_(args[0]->ToString());
std::string hash = std::string(*hash_);
data->err_msg = std::string("");
data->txid = hash;
Local<Function> callback = Local<Function>::Cast(args[1]);
data->callback = Persistent<Function>::New(callback);
int status = uv_queue_work(uv_default_loop(),
req, async_block_tx,
(uv_after_work_cb)async_block_tx_after);
assert(status == 0);
NanReturnValue(Undefined());
}
static void
async_block_tx(uv_work_t *req) {
async_block_tx_data* data = static_cast<async_block_tx_data*>(req->data);
CBlock cblock;
CBlockIndex *cblock_index;
if (!g_txindex) {
parse:
int64_t i = 0;
int64_t height = chainActive.Height();
for (; i <= height; i++) {
CBlockIndex* pblockindex = chainActive[i];
CBlock cblock;
if (ReadBlockFromDisk(cblock, pblockindex)) {
BOOST_FOREACH(const CTransaction& ctx, cblock.vtx) {
if (ctx.GetHash().GetHex() == data->txid) {
data->cblock = cblock;
data->cblock_index = pblockindex;
return;
}
}
}
}
data->err_msg = std::string("Block not found.");
return;
}
if (get_block_by_tx(data->txid, cblock, &cblock_index)) {
data->cblock = cblock;
data->cblock_index = cblock_index;
} else {
goto parse;
}
}
static void
async_block_tx_after(uv_work_t *req) {
NanScope();
async_block_tx_data* data = static_cast<async_block_tx_data*>(req->data);
if (data->err_msg != "") {
Local<Value> err = Exception::Error(NanNew<String>(data->err_msg));
const unsigned argc = 1;
Local<Value> argv[argc] = { err };
TryCatch try_catch;
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
} else {
const CBlock& cblock = data->cblock;
CBlockIndex* cblock_index = data->cblock_index;
Local<Object> jsblock = NanNew<Object>();
cblock_to_jsblock(cblock, cblock_index, jsblock, false);
const unsigned argc = 2;
Local<Value> argv[argc] = {
Local<Value>::New(Null()),
Local<Value>::New(jsblock)
};
TryCatch try_catch;
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
data->callback.Dispose();
delete data;
delete req;
}
/**
* GetBlockHex()
* bitcoindjs.getBlockHex(callback)
@ -6051,18 +6183,109 @@ found:
}
delete pcursor;
return head;
error:
head->err_msg = std::string("Deserialize Error.");
delete pcursor;
return head;
}
#endif
static bool
get_block_by_tx(const std::string itxhash, CBlock& rcblock, CBlockIndex **rcblock_index) {
bool found = false;
leveldb::Iterator* pcursor = pblocktree->pdb->NewIterator(pblocktree->iteroptions);
pcursor->SeekToFirst();
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
try {
leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
char type;
ssKey >> type;
// Blockchain Index Structure:
// http://bitcoin.stackexchange.com/questions/28168
// Transaction Structure:
// CDiskBlockPos.nFile - block file
// CDiskBlockPos.nPos - block pos
// CDiskTxPos.nTxOffset - offset from top of block
if (type == 't') {
uint256 txhash;
ssKey >> txhash;
if (txhash.GetHex() == itxhash) {
leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
ssValue >> blockPos.nFile;
ssValue >> blockPos.nPos;
CDiskTxPos txPos;
// ssValue >> txPos.nFile;
// ssValue >> txPos.nPos;
txPos.nFile = blockPos.nFile;
txPos.nPos = blockPos.nPos;
ssValue >> txPos.nTxOffset;
CTransaction ctx;
uint256 blockhash;
if (!pblocktree->ReadTxIndex(txhash, txPos)) {
goto found;
}
CAutoFile file(OpenBlockFile(txPos, true), SER_DISK, CLIENT_VERSION);
CBlockHeader header;
try {
file >> header;
fseek(file.Get(), txPos.nTxOffset, SEEK_CUR);
file >> ctx;
} catch (std::exception &e) {
goto ret;
}
if (ctx.GetHash() != txhash) {
goto ret;
}
blockhash = header.GetHash();
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
if (ReadBlockFromDisk(rcblock, pblockindex)) {
*rcblock_index = pblockindex;
found = true;
}
goto ret;
}
}
found:
pcursor->Next();
} catch (std::exception &e) {
leveldb::Slice lastKey = pcursor->key();
std::string lastKeyHex = HexStr(lastKey.ToString());
head->err_msg = std::string(e.what()
+ std::string(" : Not found. Key: ")
+ lastKeyHex);
delete pcursor;
return found;
}
}
ret:
delete pcursor;
return found;
}
static int64_t
SatoshiFromAmount(const CAmount& amount) {
return (int64_t)amount;
@ -6100,6 +6323,7 @@ init(Handle<Object> target) {
NODE_SET_METHOD(target, "getAddrTransactions", GetAddrTransactions);
NODE_SET_METHOD(target, "getBestBlock", GetBestBlock);
NODE_SET_METHOD(target, "getChainHeight", GetChainHeight);
NODE_SET_METHOD(target, "getBlockByTx", GetBlockByTx);
NODE_SET_METHOD(target, "getBlockHex", GetBlockHex);
NODE_SET_METHOD(target, "getTxHex", GetTxHex);
NODE_SET_METHOD(target, "blockFromHex", BlockFromHex);