rpc: gettxoutproof and verifytxoutproof.
This commit is contained in:
parent
b50f8cc2bb
commit
a0d109f52d
@ -1157,11 +1157,108 @@ RPC.prototype.gettxout = function gettxout(args, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
RPC.prototype.gettxoutproof = function gettxoutproof(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) {
|
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) {
|
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;
|
lookup = args.length > 0 ? Number(args[0]) : 120;
|
||||||
height = args.length > 1 ? Number(args[1]) : -1;
|
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);
|
return this._hashps(lookup, height, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1461,7 +1561,7 @@ RPC.prototype.submitblock = function submitblock(args, callback) {
|
|||||||
+ ' ( "jsonparametersobject" )'));
|
+ ' ( "jsonparametersobject" )'));
|
||||||
}
|
}
|
||||||
|
|
||||||
block = bcoin.block.fromRaw(args[0], 'hex');
|
block = bcoin.block.fromRaw(String(args[0]), 'hex');
|
||||||
|
|
||||||
this.chain.add(block, function(err, total) {
|
this.chain.add(block, function(err, total) {
|
||||||
if (err)
|
if (err)
|
||||||
|
|||||||
@ -448,6 +448,59 @@ MerkleBlock.fromJSON = function fromJSON(json) {
|
|||||||
|
|
||||||
MerkleBlock.fromBlock = function fromBlock(block, filter) {
|
MerkleBlock.fromBlock = function fromBlock(block, filter) {
|
||||||
var matches = [];
|
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 txs = [];
|
||||||
var leaves = [];
|
var leaves = [];
|
||||||
var bits = [];
|
var bits = [];
|
||||||
@ -456,12 +509,8 @@ MerkleBlock.fromBlock = function fromBlock(block, filter) {
|
|||||||
|
|
||||||
for (i = 0; i < block.txs.length; i++) {
|
for (i = 0; i < block.txs.length; i++) {
|
||||||
tx = block.txs[i];
|
tx = block.txs[i];
|
||||||
if (tx.isWatched(filter)) {
|
if (matches[i])
|
||||||
matches.push(1);
|
|
||||||
txs.push(tx);
|
txs.push(tx);
|
||||||
} else {
|
|
||||||
matches.push(0);
|
|
||||||
}
|
|
||||||
leaves.push(tx.hash());
|
leaves.push(tx.hash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user