Make new actually working master blinding calls, test

This commit is contained in:
Gregory Sanders 2019-03-29 11:21:15 -04:00
parent 09e20ab819
commit 9acbd24360
4 changed files with 136 additions and 18 deletions

View file

@ -190,7 +190,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 8 , "output_assets" },
{ "sendmany", 9 , "ignoreblindfail" },
{ "sendtoaddress", 9 , "ignoreblindfail" },
{ "importblindingkey", 2, "key_is_master"},
{ "createrawtransaction", 4, "output_assets" },
};

View file

@ -764,7 +764,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
}
// ELEMENTS: Dump the master blinding key in hex as well
if (!pwallet->blinding_derivation_key.IsNull()) {
file << ("# Master private blinding key: " + pwallet->blinding_derivation_key.GetHex() + "\n\n");
file << ("# Master private blinding key: " + HexStr(pwallet->blinding_derivation_key) + "\n\n");
}
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
@ -1343,14 +1343,13 @@ UniValue importblindingkey(const JSONRPCRequest& request)
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
if (request.fHelp || request.params.size() != 2)
throw std::runtime_error(
"importblindingkey \"address\" \"blindinghex\"\n"
"\nImports a private blinding key in hex for a CT address."
"\nArguments:\n"
"1. \"address\" (string, required) The CT address\n"
"2. \"hexkey\" (string, required) The blinding key in hex\n"
"3. \"key_is_master\" (bool, optional, default=false) If the `hexkey` is a master blinding key. Note: wallets can only have one master blinding key at a time. Funds could be permanently lost if user doesn't know what they are doing. Recommended use is only for wallet recovery using this in conjunction with `sethdseed`.\n"
"\nExample:\n"
+ HelpExampleCli("importblindingkey", "\"my blinded CT address\" <blindinghex>")
);
@ -1373,11 +1372,6 @@ UniValue importblindingkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length");
}
bool key_is_master = false;
if (!request.params[2].isNull()) {
key_is_master = request.params[2].get_bool();
}
CKey key;
key.Set(keydata.begin(), keydata.end(), true);
if (!key.IsValid() || key.GetPubKey() != GetDestinationBlindingKey(dest)) {
@ -1386,20 +1380,56 @@ UniValue importblindingkey(const JSONRPCRequest& request)
uint256 keyval;
memcpy(keyval.begin(), &keydata[0], 32);
if (key_is_master) {
if (pwallet->SetMasterBlindingKey(keyval)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import master blinding key");
}
} else {
if (!pwallet->AddSpecificBlindingKey(CScriptID(GetScriptForDestination(dest)), keyval)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import blinding key");
}
if (!pwallet->AddSpecificBlindingKey(CScriptID(GetScriptForDestination(dest)), keyval)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import blinding key");
}
pwallet->MarkDirty();
return NullUniValue;
}
UniValue importmasterblindingkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
throw std::runtime_error(
"importblindingkey \"address\" \"blindinghex\"\n"
"\nImports a master private blinding key in hex for a CT address."
"Note: wallets can only have one master blinding key at a time. Funds could be permanently lost if user doesn't know what they are doing. Recommended use is only for wallet recovery using this in conjunction with `sethdseed`.\n"
"\nArguments:\n"
"1. \"hexkey\" (string, required) The blinding key in hex\n"
"\nExample:\n"
+ HelpExampleCli("importmasterblindingkey", "<hexkey>")
);
LOCK2(cs_main, pwallet->cs_wallet);
if (!IsHex(request.params[0].get_str())) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key");
}
std::vector<unsigned char> keydata = ParseHex(request.params[0].get_str());
if (keydata.size() != 32) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length");
}
uint256 keyval;
memcpy(keyval.begin(), &keydata[0], 32);
if (!pwallet->SetMasterBlindingKey(keyval)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import master blinding key");
}
pwallet->MarkDirty();
return NullUniValue;
}
UniValue importissuanceblindingkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
@ -1519,6 +1549,34 @@ UniValue dumpblindingkey(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Blinding key for address is unknown");
}
UniValue dumpmasterblindingkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
CWallet* const pwallet = wallet.get();
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 0)
throw std::runtime_error(
"dumpmasterblindingkey\n"
"\nDumps the master private blinding key in hex."
"\nResult:\n"
"\"blindingkey\" (string) The master blinding key\n"
"\nExample:\n"
+ HelpExampleCli("dumpmasterblindingkey", "")
);
LOCK2(cs_main, pwallet->cs_wallet);
if (!pwallet->blinding_derivation_key.IsNull()) {
return HexStr(pwallet->blinding_derivation_key);
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Master blinding key is uninitialized.");
}
}
UniValue dumpissuanceblindingkey(const JSONRPCRequest& request)
{
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);

View file

@ -6199,8 +6199,10 @@ UniValue getpegoutkeys(const JSONRPCRequest& request)
UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue importblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue importmasterblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue importissuanceblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue dumpblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue dumpmasterblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue dumpissuanceblindingkey(const JSONRPCRequest& request); // in rpcdump.cpp
UniValue importprivkey(const JSONRPCRequest& request);
UniValue importaddress(const JSONRPCRequest& request);
@ -6281,9 +6283,11 @@ static const CRPCCommand commands[] =
{ "wallet", "sendtomainchain", &sendtomainchain, {"address", "amount", "subtractfeefromamount"} },
{ "wallet", "initpegoutwallet", &initpegoutwallet, {"bitcoin_descriptor", "bip32_counter", "liquid_pak"} },
{ "wallet", "getwalletpakinfo", &getwalletpakinfo, {} },
{ "wallet", "importblindingkey", &importblindingkey, {"address", "hexkey", "key_is_master"}},
{ "wallet", "importblindingkey", &importblindingkey, {"address", "hexkey"}},
{ "wallet", "importmasterblindingkey", &importmasterblindingkey, {"hexkey"}},
{ "wallet", "importissuanceblindingkey", &importissuanceblindingkey, {"txid", "vin", "blindingkey"}},
{ "wallet", "dumpblindingkey", &dumpblindingkey, {"address"}},
{ "wallet", "dumpmasterblindingkey", &dumpmasterblindingkey, {}},
{ "wallet", "dumpissuanceblindingkey", &dumpissuanceblindingkey, {"txid", "vin"}},
{ "wallet", "signblock", &signblock, {"blockhex"}},
{ "wallet", "listissuances", &listissuances, {"asset"}},

View file

@ -7,6 +7,8 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import connect_nodes_bi, assert_equal
from test_framework.authproxy import JSONRPCException
from decimal import Decimal
import os
import re
class CTTest (BitcoinTestFramework):
@ -28,8 +30,63 @@ class CTTest (BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def test_wallet_recovery(self):
file_path = "/tmp/blind_details"
try:
os.remove(file_path)
except OSError:
pass
# Wallet recovery requires more than just seed, but also master blinding key
# which currently is not derived from seed, see
# https://github.com/ElementsProject/elements/pull/232
blind_addr = self.nodes[0].getnewaddress()
self.nodes[0].dumpwallet(file_path)
found_seed = False
found_blind = False
with open(file_path, encoding="utf8") as f:
for line in f:
if "hdseed=1" in line:
split = re.split(" ", line)
found_seed = split[0]
split = re.split("Master private blinding key: ", line)
if len(split) == 2:
assert_equal(len(split[1].rstrip()), 64)
found_blind = split[1].rstrip()
assert_equal(found_blind, self.nodes[0].dumpmasterblindingkey())
# Create new wallet
self.nodes[0].createwallet("recover")
rec = self.nodes[0].get_wallet_rpc("recover")
wrong_info = rec.getaddressinfo(blind_addr)
assert("pubkey" not in wrong_info)
assert_equal(wrong_info["ismine"], False)
# Setting seed should get us more info, still not "ours" until blinding key
rec.generatetoaddress(1, rec.getnewaddress()) # get out of IBD
rec.sethdseed(True, found_seed)
wrong_blind_info = rec.getaddressinfo(blind_addr)
assert("pubkey" in wrong_blind_info)
assert_equal(wrong_blind_info["ismine"], False)
# Now import master blinding key
rec.importmasterblindingkey(found_blind)
assert_equal(rec.dumpmasterblindingkey(), found_blind)
blind_info = rec.getaddressinfo(blind_addr)
assert("pubkey" in blind_info)
assert_equal(blind_info["ismine"], True)
assert_equal(rec.getaddressinfo(blind_info["unconfidential"])["confidential"], blind_addr)
self.nodes[0].unloadwallet("recover")
def run_test(self):
print("Testing wallet secret recovery")
self.test_wallet_recovery()
print("General Confidential tests")
# Running balances
node0 = self.nodes[0].getbalance()["bitcoin"]