From 41c0cb5a4eb43e44849476214c7526dfa12ca134 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 26 Sep 2014 11:23:21 -0700 Subject: [PATCH] bitcoindjs.verifyBlock. --- example/index.js | 2 +- lib/bitcoind.js | 124 +++++++++++++++++++++++++++++++++++++++++++++- src/bitcoindjs.cc | 30 ++++++++++- 3 files changed, 152 insertions(+), 4 deletions(-) diff --git a/example/index.js b/example/index.js index 227399d9..5073070c 100755 --- a/example/index.js +++ b/example/index.js @@ -69,7 +69,7 @@ function getBlocks(bitcoind) { return bitcoind.getBlock(hash, function(err, block) { if (err) return print(err.message); - // print(block); + print(block); if (argv['get-tx'] && block.tx.length && block.tx[0].txid) { var txid = block.tx[0].txid; diff --git a/lib/bitcoind.js b/lib/bitcoind.js index f4499755..d1a596a2 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -9,6 +9,7 @@ var EventEmitter = require('events').EventEmitter; var bitcoindjs = require('../build/Release/bitcoindjs.node'); var util = require('util'); var net = require('net'); +var assert = require('assert'); var bn = require('bn.js'); /** @@ -322,6 +323,83 @@ function Block(data) { }); } +Block.prototype.verify = function() { + return this._verified = this._verified || bitcoindjs.verifyBlock(this.toHex()); +}; + +Block.prototype.toBinary = function() { + return Block.toBinary(this); +}; + +Block.toBinary = function(block, type) { + var p = []; + var off = 0; + + // version + off += utils.writeU32(p, block.nVersion || block.version, off); + + // prev_block + utils.toArray(block.previousblockhash, 'hex').forEach(function(ch) { + p[off++] = ch; + }); + + // merkle_root + utils.toArray(block.merkleroot, 'hex').forEach(function(ch) { + p[off++] = ch; + }); + + // timestamp + off += utils.writeU32(p, block.time, off); + + // bits + off += utils.writeU32(p, block.bits, off); + + // nonce + off += utils.writeU32(p, block.nonce, off); + + assert.equal(off, 80); + + if (type === 'merkle') { + // txn_count + off += utils.writeU32(p, block.txn_count, off); + // hash count + off += utils.varint(p, block.hash_count, off); + // hashes + block.hashes.forEach(function(hash) { + utils.toArray(hash, 'hex').forEach(function(ch) { + p[off++] = ch; + }); + }); + // flag count + off += utils.varint(p, block.flags.length, off); + // flags + block.flags.forEach(function(flag) { + p[off++] = flag; + }); + } else { + // txn_count + off += utils.varint(p, block.tx.length, off); + // txs + block.tx.forEach(function(tx) { + tx = bitcoin.tx(tx); + tx.toHex(); + utils.toArray(tx.hex, 'hex').forEach(function(ch) { + p[off++] = ch; + }); + }); + } + + return new Buffer(p); +}; + +Block.prototype.toHex = function() { + return this._hex = this._hex || Block.toHex(this); +}; + +Block.toHex = function(block) { + return Block.toBinary(block).toString('hex'); +}; + /** * Transaction */ @@ -420,7 +498,7 @@ Transaction.prototype.toHex = function() { }; Transaction.toHex = function(tx) { - return new bn(Transaction.toBinary(tx)).toString('hex'); + return Transaction.toBinary(tx).toString('hex'); }; Transaction.toBinary = function(tx) { @@ -470,7 +548,7 @@ Transaction.toBinary = function(tx) { } off += utils.writeU32(p, tx.nLockTime || tx.locktime, off); - return p; + return new Buffer(p); }; Transaction.prototype.toBinary = function() { @@ -644,6 +722,48 @@ utils.copy = function copy(src, dst, off, force) { return i; }; +function toArray(msg, enc) { + if (Array.isArray(msg)) + return msg.slice(); + if (!msg) + return []; + var res = []; + if (typeof msg === 'string') { + if (!enc) { + for (var i = 0; i < msg.length; i++) { + var c = msg.charCodeAt(i); + var hi = c >> 8; + var lo = c & 0xff; + if (hi) + res.push(hi, lo); + else + res.push(lo); + } + } else if (enc === 'hex') { + msg = msg.replace(/[^a-z0-9]+/ig, ''); + if (msg.length % 2 !== 0) + msg = '0' + msg; + for (var i = 0; i < msg.length; i += 8) { + var slice = msg.slice(i, i + 8); + var num = parseInt(slice, 16); + + if (slice.length === 8) + res.push((num >>> 24) & 0xff); + if (slice.length >= 6) + res.push((num >>> 16) & 0xff); + if (slice.length >= 4) + res.push((num >>> 8) & 0xff); + res.push(num & 0xff); + } + } + } else { + for (var i = 0; i < msg.length; i++) + res[i] = msg[i] | 0; + } + return res; +} +utils.toArray = toArray; + utils.NOOP = function() {}; /** diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index af0ebc02..bd08c5a2 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -126,6 +126,7 @@ NAN_METHOD(GetTx); NAN_METHOD(PollBlocks); NAN_METHOD(PollMempool); NAN_METHOD(BroadcastTx); +NAN_METHOD(VerifyBlock); static void async_start_node_work(uv_work_t *req); @@ -636,7 +637,7 @@ async_get_block_after(uv_work_t *req) { NAN_METHOD(GetTx) { NanScope(); - if (args.Length() < 2 + if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsString() || !args[2]->IsFunction()) { @@ -1084,6 +1085,32 @@ async_broadcast_tx_after(uv_work_t *req) { delete req; } +/** + * VerifyBlock + */ + +NAN_METHOD(VerifyBlock) { + NanScope(); + + if (args.Length() < 1 || !args[0]->IsString()) { + return NanThrowError( + "Usage: bitcoindjs.verifyBlock(blockHex)"); + } + + String::Utf8Value blockHex_(args[0]->ToString()); + std::string blockHex = std::string(*blockHex_); + + CBlock block; + CDataStream ssData(ParseHex(blockHex), SER_NETWORK, PROTOCOL_VERSION); + CTransaction tx; + ssData >> block; + + CValidationState state; + bool valid = CheckBlock(block, state); + + NanReturnValue(NanNew(valid)); +} + /** * Conversions */ @@ -1315,6 +1342,7 @@ init(Handle target) { NODE_SET_METHOD(target, "pollBlocks", PollBlocks); NODE_SET_METHOD(target, "pollMempool", PollMempool); NODE_SET_METHOD(target, "broadcastTx", BroadcastTx); + NODE_SET_METHOD(target, "verifyBlock", VerifyBlock); } NODE_MODULE(bitcoindjs, init)