Add importprunedfunds rpc call
This commit is contained in:
@@ -95,7 +95,7 @@ void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const st
|
||||
}
|
||||
}
|
||||
|
||||
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch) {
|
||||
uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex) {
|
||||
if (nBitsUsed >= vBits.size()) {
|
||||
// overflowed the bits array - failure
|
||||
fBad = true;
|
||||
@@ -110,14 +110,16 @@ uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, uns
|
||||
return uint256();
|
||||
}
|
||||
const uint256 &hash = vHash[nHashUsed++];
|
||||
if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid
|
||||
if (height==0 && fParentOfMatch) { // in case of height 0, we have a matched txid
|
||||
vMatch.push_back(hash);
|
||||
vnIndex.push_back(pos);
|
||||
}
|
||||
return hash;
|
||||
} else {
|
||||
// otherwise, descend into the subtrees to extract matched txids and hashes
|
||||
uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right;
|
||||
uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch, vnIndex), right;
|
||||
if (pos*2+1 < CalcTreeWidth(height-1)) {
|
||||
right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch);
|
||||
right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch, vnIndex);
|
||||
if (right == left) {
|
||||
// The left and right branches should never be identical, as the transaction
|
||||
// hashes covered by them must each be unique.
|
||||
@@ -147,7 +149,7 @@ CPartialMerkleTree::CPartialMerkleTree(const std::vector<uint256> &vTxid, const
|
||||
|
||||
CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {}
|
||||
|
||||
uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
|
||||
uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex) {
|
||||
vMatch.clear();
|
||||
// An empty set will not work
|
||||
if (nTransactions == 0)
|
||||
@@ -167,7 +169,7 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
|
||||
nHeight++;
|
||||
// traverse the partial tree
|
||||
unsigned int nBitsUsed = 0, nHashUsed = 0;
|
||||
uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch);
|
||||
uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch, vnIndex);
|
||||
// verify that no problems occurred during the tree traversal
|
||||
if (fBad)
|
||||
return uint256();
|
||||
|
||||
@@ -75,9 +75,9 @@ protected:
|
||||
|
||||
/**
|
||||
* recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild.
|
||||
* it returns the hash of the respective node.
|
||||
* it returns the hash of the respective node and its respective index.
|
||||
*/
|
||||
uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch);
|
||||
uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
|
||||
|
||||
public:
|
||||
|
||||
@@ -110,10 +110,11 @@ public:
|
||||
CPartialMerkleTree();
|
||||
|
||||
/**
|
||||
* extract the matching txid's represented by this partial merkle tree.
|
||||
* extract the matching txid's represented by this partial merkle tree
|
||||
* and their respective indices within the partial tree.
|
||||
* returns the merkle root, or 0 in case of failure
|
||||
*/
|
||||
uint256 ExtractMatches(std::vector<uint256> &vMatch);
|
||||
uint256 ExtractMatches(std::vector<uint256> &vMatch, std::vector<unsigned int> &vnIndex);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -303,7 +303,8 @@ UniValue verifytxoutproof(const UniValue& params, bool fHelp)
|
||||
UniValue res(UniValue::VARR);
|
||||
|
||||
vector<uint256> vMatch;
|
||||
if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot)
|
||||
vector<unsigned int> vIndex;
|
||||
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) != merkleBlock.header.hashMerkleRoot)
|
||||
return res;
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
@@ -204,7 +204,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 8);
|
||||
|
||||
vector<uint256> vMatched;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
vector<unsigned int> vIndex;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -221,7 +222,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_1)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 7);
|
||||
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -249,7 +250,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_2)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
|
||||
|
||||
vector<uint256> vMatched;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
vector<unsigned int> vIndex;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -275,7 +277,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[3].first == 3);
|
||||
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -303,7 +305,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
|
||||
|
||||
vector<uint256> vMatched;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
vector<unsigned int> vIndex;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -326,7 +329,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256S("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 3);
|
||||
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -353,7 +356,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
|
||||
|
||||
vector<uint256> vMatched;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
vector<unsigned int> vIndex;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -392,7 +396,8 @@ BOOST_AUTO_TEST_CASE(merkle_block_4)
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 6);
|
||||
|
||||
vector<uint256> vMatched;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
vector<unsigned int> vIndex;
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
@@ -409,7 +414,7 @@ BOOST_AUTO_TEST_CASE(merkle_block_4)
|
||||
|
||||
BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
|
||||
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched, vIndex) == block.hashMerkleRoot);
|
||||
BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
|
||||
for (unsigned int i = 0; i < vMatched.size(); i++)
|
||||
BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
|
||||
|
||||
@@ -88,7 +88,8 @@ BOOST_AUTO_TEST_CASE(pmt_test1)
|
||||
|
||||
// extract merkle root and matched txids from copy
|
||||
std::vector<uint256> vMatchTxid2;
|
||||
uint256 merkleRoot2 = pmt2.ExtractMatches(vMatchTxid2);
|
||||
std::vector<unsigned int> vIndex;
|
||||
uint256 merkleRoot2 = pmt2.ExtractMatches(vMatchTxid2, vIndex);
|
||||
|
||||
// check that it has the same merkle root as the original, and a valid one
|
||||
BOOST_CHECK(merkleRoot1 == merkleRoot2);
|
||||
@@ -102,7 +103,7 @@ BOOST_AUTO_TEST_CASE(pmt_test1)
|
||||
CPartialMerkleTreeTester pmt3(pmt2);
|
||||
pmt3.Damage();
|
||||
std::vector<uint256> vMatchTxid3;
|
||||
uint256 merkleRoot3 = pmt3.ExtractMatches(vMatchTxid3);
|
||||
uint256 merkleRoot3 = pmt3.ExtractMatches(vMatchTxid3, vIndex);
|
||||
BOOST_CHECK(merkleRoot3 != merkleRoot1);
|
||||
}
|
||||
}
|
||||
@@ -122,7 +123,8 @@ BOOST_AUTO_TEST_CASE(pmt_malleability)
|
||||
|
||||
CPartialMerkleTree tree(vTxid, vMatch);
|
||||
std::vector<uint256> vTxid2;
|
||||
BOOST_CHECK(tree.ExtractMatches(vTxid).IsNull());
|
||||
std::vector<unsigned int> vIndex;
|
||||
BOOST_CHECK(tree.ExtractMatches(vTxid, vIndex).IsNull());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "util.h"
|
||||
#include "utiltime.h"
|
||||
#include "wallet.h"
|
||||
#include "merkleblock.h"
|
||||
#include "core_io.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <stdint.h>
|
||||
@@ -243,6 +245,71 @@ UniValue importaddress(const UniValue& params, bool fHelp)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
UniValue importprunedfunds(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 2 || params.size() > 3)
|
||||
throw runtime_error(
|
||||
"importprunedfunds\n"
|
||||
"\nImports funds without rescan. Corresponding address or script must previously be included in wallet. Aimed towards pruned wallets. The end-user is responsible to import additional transactions that subsequently spend the imported outputs or rescan after the point in the blockchain the transaction is included.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"rawtransaction\" (string, required) A raw transaction in hex funding an already-existing address in wallet\n"
|
||||
"2. \"txoutproof\" (string, required) The hex output from gettxoutproof that contains the transaction\n"
|
||||
"3. \"label\" (string, optional) An optional label\n"
|
||||
);
|
||||
|
||||
CTransaction tx;
|
||||
if (!DecodeHexTx(tx, params[0].get_str()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
uint256 hashTx = tx.GetHash();
|
||||
CWalletTx wtx(pwalletMain,tx);
|
||||
|
||||
CDataStream ssMB(ParseHexV(params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
CMerkleBlock merkleBlock;
|
||||
ssMB >> merkleBlock;
|
||||
|
||||
string strLabel = "";
|
||||
if (params.size() == 3)
|
||||
strLabel = params[2].get_str();
|
||||
|
||||
//Search partial merkle tree in proof for our transaction and index in valid block
|
||||
vector<uint256> vMatch;
|
||||
vector<unsigned int> vIndex;
|
||||
unsigned int txnIndex = 0;
|
||||
if (merkleBlock.txn.ExtractMatches(vMatch, vIndex) == merkleBlock.header.hashMerkleRoot) {
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()]))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
|
||||
|
||||
vector<uint256>::const_iterator it;
|
||||
if ((it = std::find(vMatch.begin(), vMatch.end(), hashTx))==vMatch.end()) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction given doesn't exist in proof");
|
||||
}
|
||||
|
||||
txnIndex = vIndex[it - vMatch.begin()];
|
||||
}
|
||||
else {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Something wrong with merkleblock");
|
||||
}
|
||||
|
||||
wtx.nIndex = txnIndex;
|
||||
wtx.hashBlock = merkleBlock.header.GetHash();
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
if (pwalletMain->IsMine(tx)) {
|
||||
CWalletDB walletdb(pwalletMain->strWalletFile, "r+", false);
|
||||
pwalletMain->AddToWallet(wtx, false, &walletdb);
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No addresses in wallet correspond to included transaction");
|
||||
}
|
||||
|
||||
UniValue importpubkey(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
|
||||
@@ -2503,6 +2503,7 @@ extern UniValue importaddress(const UniValue& params, bool fHelp);
|
||||
extern UniValue importpubkey(const UniValue& params, bool fHelp);
|
||||
extern UniValue dumpwallet(const UniValue& params, bool fHelp);
|
||||
extern UniValue importwallet(const UniValue& params, bool fHelp);
|
||||
extern UniValue importprunedfunds(const UniValue& params, bool fHelp);
|
||||
|
||||
const CRPCCommand vWalletRPCCommands[] =
|
||||
{ // category name actor (function) okSafeMode
|
||||
@@ -2529,6 +2530,7 @@ const CRPCCommand vWalletRPCCommands[] =
|
||||
{ "wallet", "importprivkey", &importprivkey, true },
|
||||
{ "wallet", "importwallet", &importwallet, true },
|
||||
{ "wallet", "importaddress", &importaddress, true },
|
||||
{ "wallet", "importprunedfunds", &importprunedfunds, true },
|
||||
{ "wallet", "importpubkey", &importpubkey, true },
|
||||
{ "wallet", "keypoolrefill", &keypoolrefill, true },
|
||||
{ "wallet", "listaccounts", &listaccounts, false },
|
||||
|
||||
Reference in New Issue
Block a user