Skip to content

Commit

Permalink
Make new actually working master blinding calls, test
Browse files Browse the repository at this point in the history
  • Loading branch information
instagibbs committed Mar 29, 2019
1 parent 09e20ab commit 9acbd24
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 18 deletions.
1 change: 0 additions & 1 deletion src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" },

};
Expand Down
90 changes: 74 additions & 16 deletions src/wallet/rpcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>")
);
Expand All @@ -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)) {
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 5 additions & 1 deletion src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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"}},
Expand Down
57 changes: 57 additions & 0 deletions test/functional/feature_confidential_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
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):

Expand All @@ -28,8 +30,63 @@ def setup_network(self, split=False):
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"]
Expand Down

0 comments on commit 9acbd24

Please sign in to comment.