Use HMAC derivation for blinding keys

This commit is contained in:
Pieter Wuille 2016-02-02 14:00:21 +01:00
parent 5daed69e36
commit c5904a9da8
16 changed files with 373 additions and 103 deletions

View file

@ -237,6 +237,9 @@ bool CBitcoinAddress::Set(const CTxDestination& dest)
CBitcoinAddress& CBitcoinAddress::AddBlindingKey(const CPubKey& pubkey)
{
if (!pubkey.IsValid()) {
return *this;
}
assert(pubkey.size() == 33);
assert(!IsBlinded());
std::vector<unsigned char> data = vchVersion;

View file

@ -298,11 +298,12 @@ static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)
bool fBlindedIns = false;
bool fBlindedOuts = false;
std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256> input_blinds;
std::vector<uint256> output_blinds;
std::vector<CPubKey> output_pubkeys;
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
std::vector<unsigned char> blind = ParseHex(input_blinding_factors[nIn]);
uint256 blind;
blind.SetHex(input_blinding_factors[nIn]);
if (blind.size() == 0) {
input_blinds.push_back(blind);
} else if (blind.size() == 32) {
@ -323,7 +324,7 @@ static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)
output_pubkeys.push_back(pubkey);
fBlindedOuts = true;
}
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
}
if (fBlindedIns && !fBlindedOuts) {

View file

@ -34,33 +34,32 @@ const secp256k1_context* ECC_Blinding_Context() {
return secp256k1_blind_context;
}
int UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out)
bool UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, uint256& blinding_factor_out)
{
if (txout.nValue.IsAmount()) {
amount_out = txout.nValue.GetAmount();
blinding_factor_out.resize(0);
return -1;
if (!key.IsValid()) {
return false;
}
CPubKey ephemeral_key(txout.nValue.vchNonceCommitment);
if (!ephemeral_key.IsValid()) {
return 0;
return false;
}
uint256 nonce = key.ECDH(ephemeral_key);
CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
unsigned char msg[4096];
int msg_size;
uint64_t min_value, max_value, amount;
blinding_factor_out.resize(32);
int res = secp256k1_rangeproof_rewind(secp256k1_blind_context, &blinding_factor_out[0], &amount, msg, &msg_size, nonce.begin(), &min_value, &max_value, &txout.nValue.vchCommitment[0], &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size());
int res = secp256k1_rangeproof_rewind(secp256k1_blind_context, blinding_factor_out.begin(), &amount, msg, &msg_size, nonce.begin(), &min_value, &max_value, &txout.nValue.vchCommitment[0], &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size());
if (!res || amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)) {
amount_out = 0;
blinding_factor_out.resize(0);
} else
blinding_factor_out = uint256();
return false;
} else {
amount_out = (CAmount)amount;
return res ? 1 : 0;
return true;
}
}
void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding_factors, const std::vector<std::vector<unsigned char> >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx)
void BlindOutputs(const std::vector<uint256 >& input_blinding_factors, const std::vector<uint256 >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx)
{
assert(tx.vout.size() == output_blinding_factors.size());
assert(tx.vout.size() == output_pubkeys.size());
@ -71,9 +70,9 @@ void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding
int nBlindsIn = 0;
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
if (input_blinding_factors[nIn].size() != 0) {
if (input_blinding_factors[nIn] != uint256()) {
assert(input_blinding_factors[nIn].size() == 32);
blindptrs.push_back(&input_blinding_factors[nIn][0]);
blindptrs.push_back(input_blinding_factors[nIn].begin());
nBlindsIn++;
}
}
@ -81,10 +80,9 @@ void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding
int nBlindsOut = 0;
int nToBlind = 0;
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
assert((output_blinding_factors[nOut].size() != 0) == !tx.vout[nOut].nValue.IsAmount());
if (output_blinding_factors[nOut].size() != 0) {
assert(output_blinding_factors[nOut].size() == 32);
blindptrs.push_back(&output_blinding_factors[nOut][0]);
assert((output_blinding_factors[nOut] != uint256()) == !tx.vout[nOut].nValue.IsAmount());
if (output_blinding_factors[nOut] != uint256()) {
blindptrs.push_back(output_blinding_factors[nOut].begin());
nBlindsOut++;
} else {
if (output_pubkeys[nOut].IsValid()) {
@ -98,7 +96,7 @@ void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding
}
int nBlinded = 0;
unsigned char blind[nToBlind][32];
unsigned char blind[tx.vout.size()][32];
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
if (tx.vout[nOut].nValue.IsAmount() && output_pubkeys[nOut].IsValid()) {

View file

@ -8,7 +8,7 @@
void ECC_Blinding_Start();
void ECC_Blinding_Stop();
int UnblindOutput(const CKey& blinding_key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out);
void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding_factors, const std::vector<std::vector<unsigned char> >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx);
bool UnblindOutput(const CKey& blinding_key, const CTxOut& txout, CAmount& amount_out, uint256& blinding_factor_out);
void BlindOutputs(const std::vector<uint256>& input_blinding_factors, const std::vector<uint256>& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx);
#endif

View file

@ -383,7 +383,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
strAddress = CBitcoinAddress(newKey.GetID()).AddBlindingKey(wallet->blinding_pubkey).ToString();
strAddress = CBitcoinAddress(newKey.GetID()).AddBlindingKey(wallet->GetBlindingPubKey(GetScriptForDestination(CTxDestination(newKey.GetID())))).ToString();
}
else
{

View file

@ -397,3 +397,85 @@ Value dumpwallet(const Array& params, bool fHelp)
file.close();
return Value::null;
}
Value dumpblindingkey(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 1)
throw runtime_error(
"dumpblindingkey \"address\"\n"
"\nDumps the private blinding key for a CT address in hex."
"\nArguments:\n"
"1. \"address\" (string, required) The CT address\n"
);
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
if (!address.IsBlinded()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a CT address");
}
CTxDestination dest = address.Get();
CScript script = GetScriptForDestination(dest);
CKey key;
key = pwalletMain->GetBlindingKey(&script);
if (key.IsValid()) {
CPubKey pubkey = key.GetPubKey();
if (pubkey == address.GetBlindingKey()) {
return HexStr(key.begin(), key.end());
}
}
// Just for backward compatibility
key = pwalletMain->GetBlindingKey(NULL);
if (key.IsValid()) {
CPubKey pubkey = key.GetPubKey();
if (pubkey == address.GetBlindingKey()) {
return HexStr(key.begin(), key.end());
}
}
throw JSONRPCError(RPC_WALLET_ERROR, "Blinding key for address is unknown");
}
Value importblindingkey(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 2)
throw 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"
);
CBitcoinAddress address(params[0].get_str());
if (!address.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
}
if (!address.IsBlinded()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a CT address");
}
if (!IsHex(params[1].get_str())) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key");
}
std::vector<unsigned char> keydata = ParseHex(params[1].get_str());
if (keydata.size() != 32) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length");
}
CKey key;
key.Set(keydata.begin(), keydata.end(), true);
if (!key.IsValid() || key.GetPubKey() != address.GetBlindingKey()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Address and key do not match");
}
uint256 keyval;
memcpy(keyval.begin(), &keydata[0], 32);
if (!pwalletMain->AddSpecificBlindingKey(CScriptID(GetScriptForDestination(address.Get())), keyval)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import blinding key");
}
pwalletMain->MarkDirty();
return Value::null;
}

View file

@ -196,12 +196,13 @@ Value validateaddress(const Array& params, bool fHelp)
}
#ifdef ENABLE_WALLET
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
if (address.IsBlinded() && address.GetBlindingKey() != pwalletMain->blinding_pubkey) {
if (mine != ISMINE_NO && address.IsBlinded() && address.GetBlindingKey() != pwalletMain->GetBlindingPubKey(GetScriptForDestination(dest))) {
// Note: this will fail to return ismine for deprecated static blinded addresses.
mine = ISMINE_NO;
}
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
if (!address.IsBlinded() && mine != ISMINE_NO) {
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->blinding_pubkey).ToString()));
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(dest))).ToString()));
}
if (mine != ISMINE_NO) {
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));

View file

@ -421,7 +421,7 @@ Value listunspent(const Array& params, bool fHelp)
if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
CBitcoinAddress addr(address);
if (out.tx->GetBlindingFactor(out.i).size() > 0) {
addr.AddBlindingKey(pwalletMain->blinding_pubkey);
addr.AddBlindingKey(out.tx->GetBlindingKey(out.i));
}
entry.push_back(Pair("address", addr.ToString()));
if (pwalletMain->mapAddressBook.count(address))
@ -588,8 +588,8 @@ Value rawblindrawtransaction(const Array& params, bool fHelp)
if (inputBlinds.size() != tx.vin.size()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: one (potentially empty) input blind for each input must be provided"));
std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256 > input_blinds;
std::vector<uint256 > output_blinds;
std::vector<CPubKey> output_pubkeys;
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
if (!tx.vout[nOut].nValue.IsAmount())
@ -603,7 +603,7 @@ Value rawblindrawtransaction(const Array& params, bool fHelp)
}
output_pubkeys.push_back(pubkey);
}
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
}
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx);
@ -649,8 +649,8 @@ Value blindrawtransaction(const Array& params, bool fHelp)
LOCK(pwalletMain->cs_wallet);
std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256> input_blinds;
std::vector<uint256> output_blinds;
std::vector<CPubKey> output_pubkeys;
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
std::map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.find(tx.vin[nIn].prevout.hash);
@ -665,7 +665,7 @@ Value blindrawtransaction(const Array& params, bool fHelp)
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
if (!tx.vout[nOut].nValue.IsAmount()) {
std::vector<unsigned char> blinding_factor;
uint256 blinding_factor;
CAmount amount;
if (UnblindOutput(pwalletMain->blinding_key, tx.vout[nOut], amount, blinding_factor) != 0) {
output_blinds.push_back(blinding_factor);
@ -675,14 +675,14 @@ Value blindrawtransaction(const Array& params, bool fHelp)
}
} else if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) {
output_pubkeys.push_back(CPubKey());
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
} else {
CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment);
if (!pubkey.IsValid()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given"));
}
output_pubkeys.push_back(pubkey);
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
}
}

View file

@ -321,6 +321,7 @@ static const CRPCCommand vRPCCommands[] =
{ "wallet", "backupwallet", &backupwallet, true, false, true },
{ "wallet", "dumpprivkey", &dumpprivkey, true, false, true },
{ "wallet", "dumpwallet", &dumpwallet, true, false, true },
{ "wallet", "dumpblindingkey", &dumpblindingkey, true, false, true },
{ "wallet", "encryptwallet", &encryptwallet, true, false, true },
{ "wallet", "getaccountaddress", &getaccountaddress, true, false, true },
{ "wallet", "getaccount", &getaccount, true, false, true },
@ -336,6 +337,7 @@ static const CRPCCommand vRPCCommands[] =
{ "wallet", "importprivkey", &importprivkey, true, false, true },
{ "wallet", "importwallet", &importwallet, true, false, true },
{ "wallet", "importaddress", &importaddress, true, false, true },
{ "wallet", "importblindingkey", &importblindingkey, true, false, true },
{ "wallet", "keypoolrefill", &keypoolrefill, true, false, true },
{ "wallet", "listaccounts", &listaccounts, false, false, true },
{ "wallet", "listaddressgroupings", &listaddressgroupings, false, false, true },

View file

@ -150,6 +150,8 @@ extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool f
extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpblindingkey(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importblindingkey(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp
extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp);

View file

@ -110,7 +110,7 @@ Value getnewaddress(const Array& params, bool fHelp)
pwalletMain->SetAddressBook(keyID, strAccount, "receive");
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->blinding_pubkey).ToString();
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(CTxDestination(keyID)))).ToString();
}
@ -148,7 +148,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
walletdb.WriteAccount(strAccount, account);
}
return CBitcoinAddress(account.vchPubKey.GetID()).AddBlindingKey(pwalletMain->blinding_pubkey);
return CBitcoinAddress(account.vchPubKey.GetID()).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(account.vchPubKey.GetID())));
}
Value getaccountaddress(const Array& params, bool fHelp)
@ -205,7 +205,7 @@ Value getrawchangeaddress(const Array& params, bool fHelp)
CKeyID keyID = vchPubKey.GetID();
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->blinding_pubkey).ToString();
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(CTxDestination(keyID)))).ToString();
}
@ -300,9 +300,10 @@ Value getaddressesbyaccount(const Array& params, bool fHelp)
// Find all addresses that have the given account
Array ret;
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook)
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, pwalletMain->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
CBitcoinAddress address = item.first;
address.AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(item.first)));
const string& strName = item.second.name;
if (strName == strAccount)
ret.push_back(address.ToString());
@ -426,7 +427,7 @@ Value listaddressgroupings(const Array& params, bool fHelp)
{
Array addressInfo;
CBitcoinAddress addr(address);
addr.AddBlindingKey(pwalletMain->blinding_pubkey);
addr.AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(addr.Get())));
addressInfo.push_back(addr.ToString());
addressInfo.push_back(ValueFromAmount(balances[address]));
{
@ -521,8 +522,6 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
CScript scriptPubKey = GetScriptForDestination(address.Get());
if (!IsMine(*pwalletMain,scriptPubKey))
return (double)0.0;
if (address.IsBlinded() && address.GetBlindingKey() != pwalletMain->blinding_pubkey)
return (double)0.0;
// Minimum confirmations
int nMinDepth = 1;
@ -976,6 +975,7 @@ Value addmultisigaddress(const Array& params, bool fHelp)
struct tallyitem
{
CBitcoinAddress address;
CAmount nAmount;
int nConf;
vector<uint256> txids;
@ -1006,7 +1006,7 @@ Value ListReceived(const Array& params, bool fByAccounts)
filter = filter | ISMINE_WATCH_ONLY;
// Tally
map<CBitcoinAddress, tallyitem> mapTally;
map<CTxDestination, tallyitem> mapTally;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
@ -1028,7 +1028,12 @@ Value ListReceived(const Array& params, bool fByAccounts)
if(!(mine & filter))
continue;
CBitcoinAddress bitcoinaddress(address);
if (!wtx.vout[i].nValue.IsAmount()) {
bitcoinaddress.AddBlindingKey(wtx.GetBlindingKey(i));
}
tallyitem& item = mapTally[address];
item.address = bitcoinaddress;
item.nAmount += wtx.GetValueOut(i);
item.nConf = min(item.nConf, nDepth);
item.txids.push_back(wtx.GetHash());
@ -1040,19 +1045,21 @@ Value ListReceived(const Array& params, bool fByAccounts)
// Reply
Array ret;
map<string, tallyitem> mapAccountTally;
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook)
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, pwalletMain->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
const CTxDestination& address = item.first;
const string& strAccount = item.second.name;
map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address);
map<CTxDestination, tallyitem>::iterator it = mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty)
continue;
CBitcoinAddress fulladdress = address;
CAmount nAmount = 0;
int nConf = std::numeric_limits<int>::max();
bool fIsWatchonly = false;
if (it != mapTally.end())
{
fulladdress = (*it).second.address;
nAmount = (*it).second.nAmount;
nConf = (*it).second.nConf;
fIsWatchonly = (*it).second.fIsWatchonly;
@ -1070,7 +1077,7 @@ Value ListReceived(const Array& params, bool fByAccounts)
Object obj;
if(fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true));
obj.push_back(Pair("address", address.ToString()));
obj.push_back(Pair("address", fulladdress.ToString()));
obj.push_back(Pair("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));

View file

@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(naive_blinding_test)
CPubKey pubkey1 = key1.GetPubKey();
CPubKey pubkey2 = key2.GetPubKey();
std::vector<unsigned char> blind3, blind4;
uint256 blind3, blind4;
{
CCoinsModifier tx1 = cache.ModifyCoins(uint256(1));
@ -52,12 +52,12 @@ BOOST_AUTO_TEST_CASE(naive_blinding_test)
tx3.nTxFee = 22;
BOOST_CHECK(cache.VerifyAmounts(tx3));
std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256> input_blinds;
std::vector<uint256> output_blinds;
std::vector<CPubKey> output_pubkeys;
input_blinds.push_back(std::vector<unsigned char>(0, 0));
input_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(std::vector<unsigned char>(0, 0));
input_blinds.push_back(uint256());
input_blinds.push_back(uint256());
output_blinds.push_back(uint256());
output_pubkeys.push_back(pubkey1);
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx3);
BOOST_CHECK(!tx3.vout[0].nValue.IsAmount());
@ -91,14 +91,14 @@ BOOST_AUTO_TEST_CASE(naive_blinding_test)
tx4.nTxFee = 100 + 111 - 30 - 40 - 50;
BOOST_CHECK(cache.VerifyAmounts(tx4));
std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256> input_blinds;
std::vector<uint256> output_blinds;
std::vector<CPubKey> output_pubkeys;
input_blinds.push_back(std::vector<unsigned char>(0, 0));
input_blinds.push_back(uint256());
input_blinds.push_back(blind3);
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
output_blinds.push_back(uint256());
output_blinds.push_back(uint256());
output_pubkeys.push_back(pubkey2);
output_pubkeys.push_back(CPubKey());
output_pubkeys.push_back(pubkey2);

View file

@ -8,6 +8,7 @@
#include "base58.h"
#include "checkpoints.h"
#include "coincontrol.h"
#include "crypto/hmac_sha256.h"
#include "net.h"
#include "script/script.h"
#include "script/sign.h"
@ -895,7 +896,7 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
COutputEntry output = {address, GetValueOut(i), (int)i, CPubKey()};
if (!txout.nValue.IsAmount() && GetValueOut(i) > 0) {
output.confidentiality_pubkey = pwallet->blinding_pubkey;
output.confidentiality_pubkey = GetBlindingKey(i);
}
// If we are debited by the transaction, add the output as a "sent" entry
@ -1614,7 +1615,7 @@ bool CWallet::CreateTransaction(const vector<CSend>& vecSend, const vector<CTxIn
int pos = GetRandInt(txNew.vout.size()+1);
vector<CTxOut>::iterator position = txNew.vout.begin()+pos;
txNew.vout.insert(position, newTxOut);
output_pubkeys.insert(output_pubkeys.begin() + pos, blinding_pubkey);
output_pubkeys.insert(output_pubkeys.begin() + pos, GetBlindingPubKey(scriptChange));
nValueOut += nChange;
fBlindedOuts = true;
}
@ -1625,7 +1626,7 @@ bool CWallet::CreateTransaction(const vector<CSend>& vecSend, const vector<CTxIn
if (fBlindedIns && !fBlindedOuts) {
CTxOut newTxOut(0, CScript() << OP_RETURN);
txNew.vout.push_back(newTxOut);
output_pubkeys.push_back(blinding_pubkey);
output_pubkeys.push_back(GetBlindingPubKey(newTxOut.scriptPubKey));
fBlindedOuts = true;
}
// Fill vin
@ -1636,14 +1637,14 @@ bool CWallet::CreateTransaction(const vector<CSend>& vecSend, const vector<CTxIn
LogPrintf("Created transaction (before blinding): %s", CTransaction(txNew).ToString());
// Create blinded outputs
std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256> input_blinds;
std::vector<uint256> output_blinds;
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) {
std::vector<unsigned char> blind =coin.first->GetBlindingFactor(coin.second);
uint256 blind = coin.first->GetBlindingFactor(coin.second);
input_blinds.push_back(blind);
}
for (size_t nOut = 0; nOut < txNew.vout.size(); nOut++) {
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
}
if (fBlindedIns && !fBlindedOuts) {
strFailReason = _("Confidential inputs without confidential outputs");
@ -2528,3 +2529,133 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectInsaneFee)
return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectInsaneFee);
}
CKey CWallet::GetBlindingKey(const CScript* script) const
{
CKey key;
if (script != NULL) {
std::map<CScriptID, uint256>::const_iterator it = mapSpecificBlindingKeys.find(CScriptID(*script));
if (it != mapSpecificBlindingKeys.end()) {
key.Set(it->second.begin(), it->second.end(), true);
if (key.IsValid()) {
return key;
}
}
}
if (script != NULL && blinding_derivation_key != 0) {
unsigned char vch[32];
CHMAC_SHA256(blinding_derivation_key.begin(), blinding_derivation_key.size()).Write(&((*script)[0]), script->size()).Finalize(vch);
key.Set(&vch[0], &vch[32], true);
if (key.IsValid()) {
return key;
}
}
if (script == NULL && blinding_key.IsValid()) {
return blinding_key;
}
return CKey();
}
CPubKey CWallet::GetBlindingPubKey(const CScript& script) const
{
CKey key = GetBlindingKey(&script);
if (key.IsValid()) {
return key.GetPubKey();
}
return CPubKey();
}
bool CWallet::LoadSpecificBlindingKey(const CScriptID& scriptid, const uint256& key)
{
AssertLockHeld(cs_wallet); // mapSpecificBlindingKeys
mapSpecificBlindingKeys[scriptid] = key;
return true;
}
bool CWallet::AddSpecificBlindingKey(const CScriptID& scriptid, const uint256& key)
{
AssertLockHeld(cs_wallet); // mapSpecificBlindingKeys
if (!LoadSpecificBlindingKey(scriptid, key))
return false;
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).WriteSpecificBlindingKey(scriptid, key);
}
void CWallet::ComputeBlindingData(const CTxOut& output, CAmount& amount, CPubKey& pubkey, uint256& blindingfactor) const
{
if (output.nValue.IsAmount()) {
amount = output.nValue.GetAmount();
pubkey = CPubKey();
blindingfactor = 0;
return;
}
CKey blinding_key;
if ((blinding_key = GetBlindingKey(&output.scriptPubKey)).IsValid()) {
// For outputs using derived blinding.
if (UnblindOutput(blinding_key, output, amount, blindingfactor)) {
pubkey = blinding_key.GetPubKey();
return;
}
}
if ((blinding_key = GetBlindingKey(NULL)).IsValid()) {
// For outputs using deprecated static blinding.
if (UnblindOutput(blinding_key, output, amount, blindingfactor)) {
pubkey = blinding_key.GetPubKey();
return;
}
}
amount = -1;
pubkey = CPubKey();
blindingfactor = 0;
}
void CWalletTx::GetBlindingData(unsigned int nOut, CAmount* pamountOut, CPubKey* ppubkeyOut, uint256* pblindingfactorOut) const
{
// Blinding data is cached in a serialized record mapWallet["blindingdata"].
// It contains a concatenation byte vectors, 74 bytes per txout.
// Each consists of:
// * 1 byte boolean marker (has the output been computed)?
// * 8 bytes amount (-1 if unknown)
// * 32 bytes blinding factor
// * 33 bytes blinding pubkey (ECDH pubkey of the destination)
// This is really ugly, and should use CDataStream serialization instead.
assert(nOut < vout.size());
if (mapValue["blindingdata"].size() < (nOut + 1) * 74) {
mapValue["blindingdata"].resize(vout.size() * 74);
}
unsigned char* it = (unsigned char*)(&mapValue["blindingdata"][0]) + 74 * nOut;
CAmount amount = -1;
CPubKey pubkey;
uint256 blindingfactor;
if (*it == 1) {
memcpy(&amount, &*(it + 1), 8);
memcpy(blindingfactor.begin(), &*(it + 9), 32);
pubkey.Set(it + 41, it + 74);
} else {
pwallet->ComputeBlindingData(vout[nOut], amount, pubkey, blindingfactor);
*it = 1;
memcpy(&*(it + 1), &amount, 8);
memcpy(&*(it + 9), blindingfactor.begin(), 32);
if (pubkey.IsValid() && pubkey.size() == 33) {
memcpy(&*(it + 41), pubkey.begin(), 33);
} else {
memset(&*(it + 41), 0, 33);
}
}
if (pamountOut) *pamountOut = amount;
if (ppubkeyOut) *ppubkeyOut = pubkey;
if (pblindingfactorOut) *pblindingfactorOut = blindingfactor;
}

View file

@ -161,6 +161,7 @@ public:
std::set<int64_t> setKeyPool;
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
std::map<CScriptID, uint256> mapSpecificBlindingKeys;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
@ -196,9 +197,8 @@ public:
nNextResend = 0;
nLastResend = 0;
nTimeFirstKey = 0;
unsigned char static_blinding_key[32] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32};
blinding_key.Set(&static_blinding_key[0], &static_blinding_key[32], true);
blinding_pubkey = blinding_key.GetPubKey();
blinding_key = CKey();
blinding_derivation_key = uint256();
}
std::map<uint256, CWalletTx> mapWallet;
@ -214,8 +214,12 @@ public:
int64_t nTimeFirstKey;
//! The actual blinding key is computed as HMAC-SHA256(key=blinding_derivation_key, msg=scriptPubKey).
//! There can be exceptions in mapSpecificBlindingKeys.
uint256 blinding_derivation_key;
//! Only for backward compatibility with older wallets (superseded by blinding_derivation_key).
CKey blinding_key;
CPubKey blinding_pubkey;
const CWalletTx* GetWalletTx(const uint256& hash) const;
@ -244,6 +248,10 @@ public:
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
//! Load metadata (used by LoadWallet)
bool LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &metadata);
//! Adds a script-specific blinding key to the wallet, and saves it to disk.
bool AddSpecificBlindingKey(const CScriptID& scriptid, const uint256& key);
//! Adds a script-specific blinding key to the wallet without saving it to disk (used by LoadWallet)
bool LoadSpecificBlindingKey(const CScriptID& scriptid, const uint256& key);
bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
@ -427,6 +435,12 @@ public:
/** Watch-only address added */
boost::signals2::signal<void (bool fHaveWatchOnly)> NotifyWatchonlyChanged;
//! script == NULL gives the backward compatible blinding key
CKey GetBlindingKey(const CScript* script) const;
CPubKey GetBlindingPubKey(const CScript& script) const;
void ComputeBlindingData(const CTxOut& output, CAmount& amount, CPubKey& pubkey, uint256& blindingfactor) const;
};
/** A key allocated from the key pool. */
@ -552,7 +566,7 @@ private:
const CWallet* pwallet;
public:
mapValue_t mapValue;
mutable mapValue_t mapValue;
std::vector<std::pair<std::string, std::string> > vOrderForm;
unsigned int fTimeReceivedIsTxTime;
unsigned int nTimeReceived; //! time received by this node
@ -561,8 +575,10 @@ public:
std::string strFromAccount;
int64_t nOrderPos; //! position in ordered transaction list
mutable std::vector<std::vector<unsigned char> > vBlindingFactors;
// For each output
mutable std::vector<uint256> vBlindingFactors;
mutable std::vector<CAmount> vAmountsOut;
mutable std::vector<CPubKey> vBlindingKeys;
// memory only
mutable bool fDebitCached;
@ -662,8 +678,6 @@ public:
READWRITE(nTimeReceived);
READWRITE(fFromMe);
READWRITE(fSpent);
READWRITE(vBlindingFactors);
READWRITE(vAmountsOut);
if (ser_action.ForRead())
{
@ -930,31 +944,29 @@ public:
std::set<uint256> GetConflicts() const;
private:
void FillValuesAndBlindingFactors() const {
if (!vAmountsOut.size()) {
vAmountsOut.resize(vout.size());
vBlindingFactors.resize(vout.size());
for (unsigned int i = 0; i < vout.size(); i++) {
std::vector<unsigned char> nonce(32, 0);
int res = UnblindOutput(pwallet->blinding_key, vout[i], vAmountsOut[i], vBlindingFactors[i]);
if (!res)
vAmountsOut[i] = -1;
}
}
}
void GetBlindingData(unsigned int nOut, CAmount* pamountOut, CPubKey* ppubkeyOut, uint256* pblindingfactorOut) const;
public:
//! Returns either the value out (if it is to us) or 0
CAmount GetValueOut(unsigned int nOut) const {
FillValuesAndBlindingFactors();
return vAmountsOut[nOut];
CAmount ret;
GetBlindingData(nOut, &ret, NULL, NULL);
return ret;
}
//! Returns either the blinding factor (if it is to us) or 0
std::vector<unsigned char> GetBlindingFactor(unsigned int nOut) const {
FillValuesAndBlindingFactors();
return vBlindingFactors[nOut];
uint256 GetBlindingFactor(unsigned int nOut) const {
uint256 ret;
GetBlindingData(nOut, NULL, NULL, &ret);
return ret;
}
CPubKey GetBlindingKey(unsigned int nOut) const {
CPubKey ret;
GetBlindingData(nOut, NULL, &ret, NULL);
return ret;
}
};

View file

@ -193,9 +193,14 @@ bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
}
bool CWalletDB::WriteBlindingKey(const CKey& privKey)
bool CWalletDB::WriteSpecificBlindingKey(const CScriptID& scriptid, const uint256& key)
{
return Write(std::string("blindingkey"), std::vector<unsigned char>(privKey.begin(), privKey.end()));
return Write(make_pair(std::string("specificblindingkey"), scriptid), key);
}
bool CWalletDB::WriteBlindingDerivationKey(const uint256& key)
{
return Write(std::string("blindingderivationkey"), key);
}
CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount)
@ -595,14 +600,34 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return false;
}
}
/* Only for backward compatibility with older wallets. */
else if (strType == "blindingkey")
{
assert(!pwallet->blinding_key.IsValid());
std::vector<unsigned char> vchBlindingKey;
ssValue >> vchBlindingKey;
pwallet->blinding_key.Set(vchBlindingKey.begin(), vchBlindingKey.end(), true);
if (pwallet->blinding_key.IsValid()) {
pwallet->blinding_pubkey = pwallet->blinding_key.GetPubKey();
if (!pwallet->blinding_key.IsValid()) {
strErr = "Error reading wallet blinding key";
return false;
}
}
else if (strType == "blindingderivationkey")
{
assert(pwallet->blinding_derivation_key == 0);
uint256 key;
ssValue >> key;
pwallet->blinding_derivation_key = key;
}
else if (strType == "specificblindingkey")
{
CScriptID scriptid;
ssKey >> scriptid;
uint256 key;
ssValue >> key;
if (!pwallet->LoadSpecificBlindingKey(scriptid, key)) {
strErr = "Error reading wallet database: LoadSpecificBlindingKey failed";
return false;
}
}
} catch (...)
@ -716,10 +741,15 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
if (wss.fAnyUnordered)
result = ReorderTransactions(pwallet);
if (!pwallet->blinding_key.IsValid()) {
pwallet->blinding_key.MakeNewKey(true);
pwallet->blinding_pubkey = pwallet->blinding_key.GetPubKey();
WriteBlindingKey(pwallet->blinding_key);
if (result == DB_LOAD_OK && pwallet->blinding_derivation_key == 0) {
CKey key;
key.MakeNewKey(true);
uint256 keybin;
memcpy(keybin.begin(), key.begin(), key.size());
pwallet->blinding_derivation_key = keybin;
if (!WriteBlindingDerivationKey(pwallet->blinding_derivation_key)) {
result = DB_LOAD_FAIL;
}
}
return result;

View file

@ -123,7 +123,8 @@ public:
CAmount GetAccountCreditDebit(const std::string& strAccount);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);
bool WriteBlindingKey(const CKey& privKey);
bool WriteSpecificBlindingKey(const CScriptID& scriptid, const uint256& key);
bool WriteBlindingDerivationKey(const uint256& key);
DBErrors ReorderTransactions(CWallet* pwallet);
DBErrors LoadWallet(CWallet* pwallet);