rpc: gettxoutproof and verifytxoutproof.

This commit is contained in:
Christopher Jeffrey 2016-08-10 19:33:32 -07:00
parent b50f8cc2bb
commit a0d109f52d
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 157 additions and 8 deletions

View File

@ -1157,11 +1157,108 @@ RPC.prototype.gettxout = function gettxout(args, callback) {
};
RPC.prototype.gettxoutproof = function gettxoutproof(args, callback) {
callback(new Error('Not implemented.'));
var self = this;
var uniq = {};
var i, txids, block, hash, last;
if (args.help || (args.length !== 1 && args.length !== 2)) {
return callback(new RPCError('gettxoutproof'
+ ' ["txid",...] ( blockhash )'));
}
txids = args[0];
block = args[1];
if (!Array.isArray(txids) || txids.length === 0)
return callback(new RPCError('Invalid parameter.'));
if (block) {
if (!utils.isHex(block) || block.length !== 64)
return callback(new RPCError('Invalid parameter.'));
block = utils.revHex(block);
}
for (i = 0; i < txids.length; i++) {
hash = txids[i];
if (!utils.isHex(hash) || hash.length !== 64)
return callback(new RPCError('Invalid parameter.'));
hash = utils.revHex(hash);
if (uniq[hash])
return callback(new RPCError('Duplicate txid.'));
uniq[hash] = true;
txids[i] = hash;
last = hash;
}
function getBlock(callback) {
if (hash)
return self.chain.db.getBlock(hash, callback);
if (self.chain.options.indexTX) {
return self.chain.db.getTX(last, function(err, tx) {
if (err)
return callback(err);
if (!tx)
return callback();
self.chain.db.getBlock(tx.block, callback);
});
}
self.chain.db.getCoins(last, function(err, coins) {
if (err)
return callback(err);
if (!coins)
return callback();
self.chain.db.getBlock(coins.height, callback);
});
}
getBlock(function(err, block) {
if (err)
return callback(err);
if (!block)
return callback(new RPCError('Block not found.'));
for (i = 0; i < txids.length; i++) {
if (!block.hasTX(txids[i]))
return callback(new RPCError('Block does not contain all txids.'));
}
block = bcoin.merkleblock.fromHashes(block, txids);
callback(null, block.toRaw().toString('hex'));
});
};
RPC.prototype.verifytxoutproof = function verifytxoutproof(args, callback) {
callback(new Error('Not implemented.'));
var res = [];
var i, block, hash;
if (args.help || args.length !== 1)
return callback(new RPCError('verifytxoutproof "proof"'));
block = bcoin.merkleblock.fromRaw(String(args[0]), 'hex');
if (!block.verify())
return callback(null, res);
this.chain.db.get(block.hash('hex'), function(err, entry) {
if (err)
return callback(err);
if (!entry)
return callback(new RPCError('Block not found in chain.'));
for (i = 0; i < block.matches.length; i++) {
hash = block.matches[i];
res.push(utils.revHex(hash));
}
callback(null, res);
});
};
RPC.prototype.gettxoutsetinfo = function gettxoutsetinfo(args, callback) {
@ -1446,6 +1543,9 @@ RPC.prototype.getnetworkhashps = function getnetworkhashps(args, callback) {
lookup = args.length > 0 ? Number(args[0]) : 120;
height = args.length > 1 ? Number(args[1]) : -1;
if (!utils.isNumber(lookup) || !utils.isNumber(height))
return callback(new RPCError('Invalid parameter.'));
return this._hashps(lookup, height, callback);
};
@ -1461,7 +1561,7 @@ RPC.prototype.submitblock = function submitblock(args, callback) {
+ ' ( "jsonparametersobject" )'));
}
block = bcoin.block.fromRaw(args[0], 'hex');
block = bcoin.block.fromRaw(String(args[0]), 'hex');
this.chain.add(block, function(err, total) {
if (err)

View File

@ -448,6 +448,59 @@ MerkleBlock.fromJSON = function fromJSON(json) {
MerkleBlock.fromBlock = function fromBlock(block, filter) {
var matches = [];
var i, tx;
for (i = 0; i < block.txs.length; i++) {
tx = block.txs[i];
if (tx.isWatched(filter))
matches.push(1);
else
matches.push(0);
}
return MerkleBlock.fromMatches(block, matches);
};
/**
* Create a merkleblock from an array of txids.
* This will build the partial merkle tree.
* @param {Block} block
* @param {Hash[]} hashes
* @returns {MerkleBlock}
*/
MerkleBlock.fromHashes = function fromHashes(block, hashes) {
var filter = {};
var matches = [];
var i, tx, hash;
for (i = 0; i < hashes.length; i++) {
hash = hashes[i];
if (Buffer.isBuffer(hash))
hash = hash.toString('hex');
filter[hash] = true;
}
for (i = 0; i < block.txs.length; i++) {
tx = block.txs[i];
if (filter[tx.hash('hex')])
matches.push(1);
else
matches.push(0);
}
return MerkleBlock.fromMatches(block, matches);
};
/**
* Create a merkleblock from an array of matches.
* This will build the partial merkle tree.
* @param {Block} block
* @param {Number[]} matches
* @returns {MerkleBlock}
*/
MerkleBlock.fromMatches = function fromMatches(block, matches) {
var txs = [];
var leaves = [];
var bits = [];
@ -456,12 +509,8 @@ MerkleBlock.fromBlock = function fromBlock(block, filter) {
for (i = 0; i < block.txs.length; i++) {
tx = block.txs[i];
if (tx.isWatched(filter)) {
matches.push(1);
if (matches[i])
txs.push(tx);
} else {
matches.push(0);
}
leaves.push(tx.hash());
}