Wallet updates for CT

A few fixes from Jerzy Kozera <jerzy.kozera@gmail.com>
This commit is contained in:
Pieter Wuille 2016-03-01 14:56:41 -08:00 committed by Gregory Sanders
parent 57028e5c5c
commit 683dd3cdbe
8 changed files with 641 additions and 81 deletions

View file

@ -75,6 +75,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 0 },
{ "createrawtransaction", 1 },
{ "createrawtransaction", 2 },
{ "rawblindrawtransaction", 1 },
{ "signrawtransaction", 1 },
{ "signrawtransaction", 2 },
{ "sendrawtransaction", 1 },

View file

@ -166,6 +166,9 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
" \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
" \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n"
" \"confidential_key\" : \"pubkey\" (string) The confidentiality key associated with the address, or \"\" if none\n"
" \"unconfidential\" : \"address\" (string) The address without confidentiality key\n"
" \"confidential\" : \"address\" (string) Confidential version of the address, only if it is yours and unconfidential\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
" \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n"
"}\n"
@ -194,10 +197,26 @@ UniValue validateaddress(const UniValue& params, bool fHelp)
CScript scriptPubKey = GetScriptForDestination(dest);
ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
if (address.IsBlinded()) {
CPubKey key = address.GetBlindingKey();
ret.push_back(Pair("confidential_key", HexStr(key.begin(), key.end())));
ret.push_back(Pair("unconfidential", address.GetUnblinded().ToString()));
} else {
ret.push_back(Pair("confidential_key", ""));
ret.push_back(Pair("unconfidential", currentAddress));
}
#ifdef ENABLE_WALLET
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
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));
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
if (!address.IsBlinded() && mine != ISMINE_NO) {
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(dest))).ToString()));
}
UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
ret.pushKVs(detail);
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))

View file

@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
#include "blind.h"
#include "chain.h"
#include "coins.h"
#include "consensus/validation.h"
@ -30,11 +31,36 @@
#include <stdint.h>
#include <boost/assign/list_of.hpp>
#include <secp256k1_rangeproof.h>
#include <univalue.h>
using namespace std;
static secp256k1_context* secp256k1_blind_context = NULL;
class RPCRawTransaction_ECC_Init {
public:
RPCRawTransaction_ECC_Init() {
assert(secp256k1_blind_context == NULL);
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
assert(ctx != NULL);
secp256k1_blind_context = ctx;
}
~RPCRawTransaction_ECC_Init() {
secp256k1_context *ctx = secp256k1_blind_context;
secp256k1_blind_context = NULL;
if (ctx) {
secp256k1_context_destroy(ctx);
}
}
};
static RPCRawTransaction_ECC_Init ecc_init_on_load;
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
{
txnouttype type;
@ -104,7 +130,28 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
UniValue out(UniValue::VOBJ);
if (txout.nValue.IsAmount())
out.push_back(Pair("value", ValueFromAmount(txout.nValue.GetAmount())));
else {} // TODO: Non-Amount values
else {
int exp;
int mantissa;
uint64_t minv;
uint64_t maxv;
if (secp256k1_rangeproof_info(secp256k1_blind_context, &exp, &mantissa, &minv, &maxv, &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size())) {
if (exp == -1) {
out.push_back(Pair("value", ValueFromAmount((CAmount)minv)));
} else {
out.push_back(Pair("value-minimum", ValueFromAmount((CAmount)minv)));
out.push_back(Pair("value-maximum", ValueFromAmount((CAmount)maxv)));
}
out.push_back(Pair("ct-exponent", exp));
out.push_back(Pair("ct-bits", mantissa));
}
}
{
CDataStream ssValue(SER_NETWORK, PROTOCOL_VERSION);
ssValue << txout.nValue;
out.push_back(Pair("serValue", HexStr(ssValue.begin(), ssValue.end())));
}
out.push_back(Pair("n", (int64_t)i));
UniValue o(UniValue::VOBJ);
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
@ -356,6 +403,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
" {\n"
" \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n (numeric, required) The output number\n"
" \"nValue\":x.xxx, (numeric, required) The amount being spent\n"
" \"sequence\":n (numeric, optional) The sequence number\n"
" }\n"
" ,...\n"
@ -454,6 +502,12 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
CTxOut out(nAmount, scriptPubKey);
if (address.IsBlinded()) {
CPubKey confidentiality_pubkey = address.GetBlindingKey();
if (!confidentiality_pubkey.IsValid())
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given"));
out.nValue.vchNonceCommitment = std::vector<unsigned char>(confidentiality_pubkey.begin(), confidentiality_pubkey.end());
}
rawTx.vout.push_back(out);
}
}
@ -463,6 +517,153 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
return EncodeHexTx(rawTx);
}
void FillOutputBlinds(const CMutableTransaction& tx, bool fUseWallet, 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()) {
uint256 blinding_factor;
CAmount amount;
#ifdef ENABLE_WALLET
if (fUseWallet && UnblindOutput(pwalletMain->blinding_key, tx.vout[nOut], amount, blinding_factor) != 0) {
output_blinds.push_back(blinding_factor);
output_pubkeys.push_back(CPubKey());
} else if (fUseWallet)
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction outputs must be unblinded or to wallet"));
#endif
if (!fUseWallet)
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction outputs must be unblinded"));
} else if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) {
output_pubkeys.push_back(CPubKey());
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(uint256());
}
}
}
UniValue rawblindrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || (params.size() != 2 && params.size() != 3))
throw runtime_error(
"rawblindrawtransaction \"hexstring\" [\"inputblinder\",...] [\"totalblinder\"]\n"
"\nConvert one or more outputs of a raw transaction into confidential ones.\n"
"Returns the hex-encoded raw transaction.\n"
"If at least one of the inputs is confidential, at least one of the outputs must be.\n"
"The input raw transaction cannot have already-blinded outputs.\n"
"The output keys used can be specified by using a confidential address in createrawtransaction.\n"
"\nArguments:\n"
"1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n"
"2. [ (array, required) An array with one entry per transaction input.\n"
" \"inputblinder\" (string, required) A hex-encoded blinding factor, one for each input.\n"
" Blinding factors can be found in the \"blinder\" output of listunspent.\n"
" ],\n"
"3. \"totalblinder\" (string, optional) Ignored for now.\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
);
if (params.size() == 2) {
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR));
} else {
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VSTR));
}
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CMutableTransaction tx;
try {
ssData >> tx;
} catch (const std::exception &) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
UniValue inputBlinds = params[1].get_array();
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<uint256> input_blinds;
std::vector<uint256> output_blinds;
std::vector<CPubKey> output_pubkeys;
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
if (!inputBlinds[nIn].isStr())
throw JSONRPCError(RPC_INVALID_PARAMETER, "input blinds must be an array of hex strings");
std::string blind(inputBlinds[nIn].get_str());
if (!IsHex(blind) || blind.length() != 32*2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "input blinds must be an array of 32-byte hex-encoded strings");
input_blinds.push_back(uint256S(blind));
}
FillOutputBlinds(tx, false, output_blinds, output_pubkeys);
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx);
return EncodeHexTx(tx);
}
#ifdef ENABLE_WALLET
UniValue blindrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || (params.size() != 1 && params.size() != 2))
throw runtime_error(
"blindrawtransaction \"hexstring\" [\"totalblinder\"]\n"
"\nConvert one or more outputs of a raw transaction into confidential ones using only wallet inputs.\n"
"Returns the hex-encoded raw transaction.\n"
"If at least one of the inputs is confidential, at least one of the outputs must be.\n"
"The output keys used can be specified by using a confidential address in createrawtransaction.\n"
"\nArguments:\n"
"1. \"hexstring\", (string, required) A hex-encoded raw transaction.\n"
"2. \"totalblinder\" (string, optional) Ignored for now.\n"
"\nResult:\n"
"\"transaction\" (string) hex string of the transaction\n"
);
if (params.size() == 1) {
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
} else {
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VSTR));
}
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CMutableTransaction tx;
try {
ssData >> tx;
} catch (const std::exception &) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
LOCK(pwalletMain->cs_wallet);
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);
if (it == pwalletMain->mapWallet.end()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction spends from non-wallet output"));
}
if (tx.vin[nIn].prevout.n >= it->second.vout.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction spends non-existing output"));
}
input_blinds.push_back(it->second.GetBlindingFactor(tx.vin[nIn].prevout.n));
}
FillOutputBlinds(tx, true, output_blinds, output_pubkeys);
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx);
return EncodeHexTx(tx);
}
#endif
UniValue decoderawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
@ -921,6 +1122,8 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "decodescript", &decodescript, true },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false },
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */
{ "rawtransactions", "rawblindrawtransaction", &rawblindrawtransaction, false },
{ "rawtransactions", "blindrawtransaction", &blindrawtransaction, true },
{ "blockchain", "gettxoutproof", &gettxoutproof, true },
{ "blockchain", "verifytxoutproof", &verifytxoutproof, true },

View file

@ -141,7 +141,7 @@ UniValue getnewaddress(const UniValue& params, bool fHelp)
pwalletMain->SetAddressBook(keyID, strAccount, "receive");
return CBitcoinAddress(keyID).ToString();
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(CTxDestination(keyID)))).ToString();
}
@ -152,7 +152,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
}
return CBitcoinAddress(pubKey.GetID());
return CBitcoinAddress(pubKey.GetID()).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(pubKey.GetID())));
}
UniValue getaccountaddress(const UniValue& params, bool fHelp)
@ -218,7 +218,7 @@ UniValue getrawchangeaddress(const UniValue& params, bool fHelp)
CKeyID keyID = vchPubKey.GetID();
return CBitcoinAddress(keyID).ToString();
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(CTxDestination(keyID)))).ToString();
}
@ -327,9 +327,10 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp)
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
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());
@ -337,7 +338,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp)
return ret;
}
static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew)
static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CPubKey &confidentiality_key, CWalletTx& wtxNew)
{
CAmount curBalance = pwalletMain->GetBalance();
@ -357,7 +358,7 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr
std::string strError;
vector<CRecipient> vecSend;
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
CRecipient recipient = {scriptPubKey, nValue, confidentiality_key, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance())
@ -408,6 +409,11 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
if (nAmount <= 0)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
CPubKey confidentiality_pubkey;
if (address.IsBlinded()) {
confidentiality_pubkey = address.GetBlindingKey();
}
// Wallet comments
CWalletTx wtx;
if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty())
@ -421,7 +427,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked();
SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx);
SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, confidentiality_pubkey, wtx);
return wtx.GetHash().GetHex();
}
@ -464,11 +470,13 @@ UniValue listaddressgroupings(const UniValue& params, bool fHelp)
BOOST_FOREACH(CTxDestination address, grouping)
{
UniValue addressInfo(UniValue::VARR);
addressInfo.push_back(CBitcoinAddress(address).ToString());
CBitcoinAddress addr(address);
addr.AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(addr.Get())));
addressInfo.push_back(addr.ToString());
addressInfo.push_back(ValueFromAmount(balances[address]));
{
if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end())
addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name);
if (pwalletMain->mapAddressBook.find(addr.Get()) != pwalletMain->mapAddressBook.end())
addressInfo.push_back(pwalletMain->mapAddressBook.find(addr.Get())->second.name);
}
jsonGrouping.push_back(addressInfo);
}
@ -581,10 +589,11 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (txout.scriptPubKey == scriptPubKey)
for (unsigned int i = 0; i < wtx.vout.size(); i++)
if (wtx.vout[i].scriptPubKey == scriptPubKey)
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
nAmount += wtx.GetValueOut(i);
}
return ValueFromAmount(nAmount);
@ -635,12 +644,12 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp)
if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
for (unsigned int i = 0; i < wtx.vout.size(); i++)
{
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
if (ExtractDestination(wtx.vout[i].scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue;
nAmount += wtx.GetValueOut(i);
}
}
@ -830,6 +839,11 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
if (params.size() > 3)
nMinDepth = params[3].get_int();
CPubKey confidentiality_pubkey;
if (address.IsBlinded())
confidentiality_pubkey = address.GetBlindingKey();
CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty())
@ -844,7 +858,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
SendMoney(address.Get(), nAmount, false, wtx);
SendMoney(address.Get(), nAmount, false, confidentiality_pubkey, wtx);
return wtx.GetHash().GetHex();
}
@ -929,6 +943,11 @@ UniValue sendmany(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
totalAmount += nAmount;
CPubKey confidentiality_pubkey;
if (address.IsBlinded())
confidentiality_pubkey = address.GetBlindingKey();
bool fSubtractFeeFromAmount = false;
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
const UniValue& addr = subtractFeeFromAmount[idx];
@ -936,7 +955,7 @@ UniValue sendmany(const UniValue& params, bool fHelp)
fSubtractFeeFromAmount = true;
}
CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
CRecipient recipient = {scriptPubKey, nAmount, confidentiality_pubkey, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
}
@ -1103,6 +1122,7 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp)
struct tallyitem
{
CBitcoinAddress address;
CAmount nAmount;
int nConf;
vector<uint256> txids;
@ -1133,7 +1153,7 @@ UniValue ListReceived(const UniValue& 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;
@ -1145,18 +1165,23 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
if (nDepth < nMinDepth)
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
for (unsigned int i = 0; i < wtx.vout.size(); i++)
{
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address))
if (!ExtractDestination(wtx.vout[i].scriptPubKey, address))
continue;
isminefilter mine = IsMine(*pwalletMain, address);
if(!(mine & filter))
continue;
CBitcoinAddress bitcoinaddress(address);
if (!wtx.vout[i].nValue.IsAmount())
bitcoinaddress.AddBlindingKey(wtx.GetBlindingKey(i));
tallyitem& item = mapTally[address];
item.nAmount += txout.nValue;
item.address = bitcoinaddress;
item.nAmount += wtx.GetValueOut(i);
item.nConf = min(item.nConf, nDepth);
item.txids.push_back(wtx.GetHash());
if (mine & ISMINE_WATCH_ONLY)
@ -1167,19 +1192,21 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
// Reply
UniValue ret(UniValue::VARR);
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;
@ -1197,7 +1224,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
UniValue obj(UniValue::VOBJ);
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)));
@ -1310,11 +1337,14 @@ UniValue listreceivedbyaccount(const UniValue& params, bool fHelp)
return ListReceived(params, true);
}
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest)
static void MaybePushAddress(UniValue & entry, const CTxDestination &dest, const CPubKey& confidentiality_pubkey)
{
CBitcoinAddress addr;
if (addr.Set(dest))
if (addr.Set(dest)) {
if (confidentiality_pubkey.size() == 33)
addr.AddBlindingKey(confidentiality_pubkey);
entry.push_back(Pair("address", addr.ToString()));
}
}
void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter)
@ -1338,7 +1368,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", strSentAccount));
MaybePushAddress(entry, s.destination);
MaybePushAddress(entry, s.destination, s.confidentiality_pubkey);
entry.push_back(Pair("category", "send"));
entry.push_back(Pair("amount", ValueFromAmount(-s.amount)));
if (pwalletMain->mapAddressBook.count(s.destination))
@ -1366,7 +1396,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", account));
MaybePushAddress(entry, r.destination);
MaybePushAddress(entry, r.destination, r.confidentiality_pubkey);
if (wtx.IsCoinBase())
{
if (wtx.GetDepthInMainChain() < 1)
@ -2359,6 +2389,8 @@ UniValue listunspent(const UniValue& params, bool fHelp)
" \"scriptPubKey\" : \"key\", (string) the script key\n"
" \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"serValue\" : \"hex\", (string) the output's value commitment\n"
" \"blinder\" : \"blind\" (string) The blinding factor used for a confidential output (or \"\")\n"
" \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
" \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
@ -2412,13 +2444,16 @@ UniValue listunspent(const UniValue& params, bool fHelp)
if (setAddress.size() && (!fValidAddress || !setAddress.count(address)))
continue;
CAmount nValue = out.tx->GetValueOut(out.i);
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
entry.push_back(Pair("vout", out.i));
if (fValidAddress) {
entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
CBitcoinAddress addr(address);
if (out.tx->GetBlindingFactor(out.i).size() > 0)
addr.AddBlindingKey(out.tx->GetBlindingKey(out.i));
entry.push_back(Pair("address", addr.ToString()));
if (pwalletMain->mapAddressBook.count(address))
entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name));
@ -2431,10 +2466,14 @@ UniValue listunspent(const UniValue& params, bool fHelp)
}
entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
entry.push_back(Pair("amount", ValueFromAmount(out.tx->vout[out.i].nValue)));
entry.push_back(Pair("amount", ValueFromAmount(nValue)));
entry.push_back(Pair("confirmations", out.nDepth));
entry.push_back(Pair("spendable", out.fSpendable));
entry.push_back(Pair("solvable", out.fSolvable));
CDataStream ssValue(SER_NETWORK, PROTOCOL_VERSION);
ssValue << nValue;
entry.push_back(Pair("serValue", HexStr(ssValue.begin(), ssValue.end())));
entry.push_back(Pair("blinder",out.tx->GetBlindingFactor(out.i).ToString()));
results.push_back(entry);
}

View file

@ -11,6 +11,7 @@
#include "coincontrol.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "crypto/hmac_sha256.h"
#include "key.h"
#include "keystore.h"
#include "main.h"
@ -32,6 +33,8 @@
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <secp256k1.h>
using namespace std;
CWallet* pwalletMain = NULL;
@ -74,7 +77,7 @@ struct CompareValueOnly
std::string COutput::ToString() const
{
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue));
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->GetValueOut(i)));
}
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
@ -1079,7 +1082,7 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]) & filter)
return prev.vout[txin.prevout.n].nValue;
return prev.GetValueOut(txin.prevout.n);
}
}
return 0;
@ -1090,13 +1093,6 @@ isminetype CWallet::IsMine(const CTxOut& txout) const
return ::IsMine(*this, txout.scriptPubKey);
}
CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
return ((IsMine(txout) & filter) ? txout.nValue : 0);
}
bool CWallet::IsChange(const CTxOut& txout) const
{
// TODO: fix handling of 'change' outputs. The assumption is that any
@ -1119,13 +1115,6 @@ bool CWallet::IsChange(const CTxOut& txout) const
return false;
}
CAmount CWallet::GetChange(const CTxOut& txout) const
{
if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0);
}
bool CWallet::IsMine(const CTransaction& tx) const
{
BOOST_FOREACH(const CTxOut& txout, tx.vout)
@ -1151,24 +1140,24 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co
return nDebit;
}
CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
CAmount CWallet::GetCredit(const CWalletTx& tx, const isminefilter& filter) const
{
CAmount nCredit = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
for (unsigned int i = 0; i < tx.vout.size(); i++)
{
nCredit += GetCredit(txout, filter);
nCredit += tx.GetCredit(i, filter);
if (!MoneyRange(nCredit))
throw std::runtime_error(std::string(__func__) + ": value out of range");
}
return nCredit;
}
CAmount CWallet::GetChange(const CTransaction& tx) const
CAmount CWallet::GetChange(const CWalletTx& tx) const
{
CAmount nChange = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout)
for (unsigned int i = 0; i < tx.vout.size(); i++)
{
nChange += GetChange(txout);
nChange += tx.GetChange(i);
if (!MoneyRange(nChange))
throw std::runtime_error(std::string(__func__) + ": value out of range");
}
@ -1319,7 +1308,11 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
address = CNoDestination();
}
COutputEntry output = {address, txout.nValue, (int)i};
COutputEntry output = {address, GetValueOut(i), (int)i, CPubKey()};
if (!txout.nValue.IsAmount() && GetValueOut(i) > 0) {
output.confidentiality_pubkey = GetBlindingKey(i);
}
// If we are debited by the transaction, add the output as a "sent" entry
if (nDebit > 0)
@ -1502,6 +1495,19 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const
return debit;
}
CAmount CWalletTx::GetCredit(unsigned int nTxOut, const isminefilter& filter) const
{
CAmount amount = 0;
if (pwallet->IsMine(vout[nTxOut]) & filter)
amount = GetValueOut(nTxOut);
// Can be -1 if someone sent us a transaction using a wrong scanning key:
if (amount == -1)
return 0;
if (!MoneyRange(amount))
throw std::runtime_error("CWallet::GetCredit(): value out of range");
return amount;
}
CAmount CWalletTx::GetCredit(const isminefilter& filter) const
{
// Must wait until coinbase is safely deep enough in the chain before valuing it
@ -1567,8 +1573,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
{
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
nCredit += GetCredit(i, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
}
@ -1610,8 +1615,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
{
if (!pwallet->IsSpent(GetHash(), i))
{
const CTxOut &txout = vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
nCredit += GetCredit(i, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
}
@ -1622,6 +1626,16 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
return nCredit;
}
CAmount CWalletTx::GetChange(unsigned int nTxOut) const
{
CAmount amount = 0;
if (pwallet->IsChange(vout[nTxOut]))
amount = GetValueOut(nTxOut);
if (!MoneyRange(amount))
throw std::runtime_error("CWallet::GetChange(): value out of range");
return amount;
}
CAmount CWalletTx::GetChange() const
{
if (fChangeCached)
@ -1680,6 +1694,68 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& tx) const
return CTransaction(tx1) == CTransaction(tx2);
}
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;
}
CAmount CWalletTx::GetValueOut(unsigned int nOut) const {
CAmount ret;
GetBlindingData(nOut, &ret, NULL, NULL);
return ret;
}
uint256 CWalletTx::GetBlindingFactor(unsigned int nOut) const {
uint256 ret;
GetBlindingData(nOut, NULL, NULL, &ret);
return ret;
}
CPubKey CWalletTx::GetBlindingKey(unsigned int nOut) const {
CPubKey ret;
GetBlindingData(nOut, NULL, &ret, NULL);
return ret;
}
std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime)
{
std::vector<uint256> result;
@ -1860,7 +1936,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
isminetype mine = IsMine(pcoin->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
!IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) &&
!IsLockedCoin((*it).first, i) && (pcoin->GetValueOut(i) > 0 || fIncludeZeroValue) &&
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i))))
vCoins.push_back(COutput(pcoin, i, nDepth,
((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
@ -1943,7 +2019,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
continue;
int i = output.i;
CAmount n = pcoin->vout[i].nValue;
CAmount n = pcoin->GetValueOut(i);
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
@ -2029,8 +2105,8 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
BOOST_FOREACH(const COutput& out, vCoins)
{
if (!out.fSpendable)
continue;
nValueRet += out.tx->vout[out.i].nValue;
continue;
nValueRet += out.tx->GetValueOut(out.i);
setCoinsRet.insert(make_pair(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
@ -2052,7 +2128,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
// Clearly invalid input, fail
if (pcoin->vout.size() <= outpoint.n)
return false;
nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue;
nValueFromPresetInputs += pcoin->GetValueOut(outpoint.n);
setPresetCoins.insert(make_pair(pcoin, outpoint.n));
} else
return false; // TODO: Allow non-wallet inputs
@ -2088,7 +2164,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
// Turn the txout set into a CRecipient vector
BOOST_FOREACH(const CTxOut& txOut, tx.vout)
{
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false};
if (!txOut.nValue.IsAmount()) {
strFailReason = _("Pre-funded amounts must be non-blinded");
return false;
}
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue.GetAmount(), CPubKey(), false};
vecSend.push_back(recipient);
}
@ -2199,6 +2279,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
while (true)
{
nChangePosInOut = nChangePosRequest;
std::vector<CPubKey> output_pubkeys;
bool fBlindedOuts = false;
txNew.vin.clear();
txNew.vout.clear();
txNew.wit.SetNull();
@ -2216,12 +2299,12 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
if (recipient.fSubtractFeeFromAmount)
{
txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient
txout.nValue = recipient.nAmount - (nFeeRet / nSubtractFeeFromAmount); // Subtract fee equally from each selected recipient
if (fFirst) // first receiver pays the remainder not divisible by output count
{
fFirst = false;
txout.nValue -= nFeeRet % nSubtractFeeFromAmount;
txout.nValue = recipient.nAmount - (nFeeRet / nSubtractFeeFromAmount) - (nFeeRet % nSubtractFeeFromAmount);
}
}
@ -2229,7 +2312,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
if (txout.nValue < 0)
if (txout.nValue.GetAmount() < 0)
strFailReason = _("The transaction amount is too small to pay the fee");
else
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
@ -2239,6 +2322,9 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
return false;
}
txNew.vout.push_back(txout);
output_pubkeys.push_back(recipient.confidentiality_key);
if (recipient.confidentiality_key.size() != 0)
fBlindedOuts = true;
}
// Choose coins to use
@ -2249,9 +2335,12 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
strFailReason = _("Insufficient funds");
return false;
}
bool fBlindedIns = false;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
CAmount nCredit = pcoin.first->vout[pcoin.second].nValue;
if (!pcoin.first->vout[pcoin.second].nValue.IsAmount())
fBlindedIns = true;
CAmount nCredit = pcoin.first->GetValueOut(pcoin.second);
//The coin age after the next block (depth+1) is used instead of the current,
//reflecting an assumption the user would accept a bit more delay for
//a chance at a free transaction.
@ -2301,13 +2390,13 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// So instead we raise the change and deduct from the recipient.
if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))
{
CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;
newTxOut.nValue += nDust; // raise change until no more dust
CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue.GetAmount();
newTxOut.nValue = newTxOut.nValue.GetAmount() + nDust; // raise change until no more dust
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
{
if (vecSend[i].fSubtractFeeFromAmount)
{
txNew.vout[i].nValue -= nDust;
txNew.vout[i].nValue = txNew.vout[i].nValue.GetAmount() - nDust;
if (txNew.vout[i].IsDust(::minRelayTxFee))
{
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
@ -2341,11 +2430,21 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;
txNew.vout.insert(position, newTxOut);
output_pubkeys.insert(output_pubkeys.begin() + nChangePosInOut, GetBlindingPubKey(scriptChange));
fBlindedOuts = true;
}
}
else
reservekey.ReturnKey();
if (fBlindedIns && !fBlindedOuts) {
CTxOut newTxOut(0, CScript() << OP_RETURN);
txNew.vout.push_back(newTxOut);
output_pubkeys.push_back(GetBlindingPubKey(newTxOut.scriptPubKey));
fBlindedOuts = true;
}
// Fill vin
//
// Note how the sequence number is set to max()-1 so that the
@ -2355,6 +2454,23 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
std::numeric_limits<unsigned int>::max()-1));
txNew.nTxFee = nFeeRet;
LogPrintf("Created transaction (before blinding): %s", CTransaction(txNew).ToString());
// Create blinded outputs
std::vector<uint256> input_blinds;
std::vector<uint256> output_blinds;
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) {
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(uint256());
}
if (fBlindedIns && !fBlindedOuts) {
strFailReason = _("Confidential inputs without confidential outputs");
return false;
}
BlindOutputs(input_blinds, output_blinds, output_pubkeys, txNew);
// Sign
int nIn = 0;
@ -2836,7 +2952,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr))
continue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue;
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->GetValueOut(i);
if (!balances.count(addr))
balances[addr] = 0;
@ -3596,3 +3712,91 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, CAmount nAbsurdFee)
CValidationState state;
return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, false, nAbsurdFee);
}
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.IsNull()) {
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.SetNull();
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.SetNull();
}

View file

@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include "amount.h"
#include "blind.h"
#include "streams.h"
#include "tinyformat.h"
#include "ui_interface.h"
@ -124,6 +125,7 @@ struct CRecipient
{
CScript scriptPubKey;
CAmount nAmount;
CPubKey confidentiality_key;
bool fSubtractFeeFromAmount;
};
@ -153,6 +155,7 @@ struct COutputEntry
CTxDestination destination;
CAmount amount;
int vout;
CPubKey confidentiality_pubkey;
};
/** A transaction with a merkle branch linking it to the block chain. */
@ -229,7 +232,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
@ -238,6 +241,11 @@ public:
std::string strFromAccount;
int64_t nOrderPos; //!< position in ordered transaction list
// For each output
mutable std::vector<uint256> vBlindingFactors;
mutable std::vector<CAmount> vAmountsOut;
mutable std::vector<CPubKey> vBlindingKeys;
// memory only
mutable bool fDebitCached;
mutable bool fCreditCached;
@ -374,11 +382,13 @@ public:
//! filter decides which addresses will count towards the debit
CAmount GetDebit(const isminefilter& filter) const;
CAmount GetCredit(unsigned int nTxOut, const isminefilter& filter) const;
CAmount GetCredit(const isminefilter& filter) const;
CAmount GetImmatureCredit(bool fUseCache=true) const;
CAmount GetAvailableCredit(bool fUseCache=true) const;
CAmount GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const;
CAmount GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const;
CAmount GetChange(unsigned int nTxOut) const;
CAmount GetChange() const;
void GetAmounts(std::list<COutputEntry>& listReceived,
@ -404,6 +414,17 @@ public:
bool RelayWalletTransaction();
std::set<uint256> GetConflicts() const;
private:
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;
//! Returns either the blinding factor (if it is to us) or 0
uint256 GetBlindingFactor(unsigned int nOut) const;
CPubKey GetBlindingKey(unsigned int nOut) const;
};
@ -596,6 +617,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;
@ -632,6 +654,8 @@ public:
nLastResend = 0;
nTimeFirstKey = 0;
fBroadcastTransactions = false;
blinding_key = CKey();
blinding_derivation_key = uint256();
}
std::map<uint256, CWalletTx> mapWallet;
@ -652,6 +676,13 @@ 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;
const CWalletTx* GetWalletTx(const uint256& hash) const;
//! check whether we are allowed to upgrade (or already support) to the named feature
@ -689,6 +720,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; }
@ -792,15 +827,13 @@ public:
isminetype IsMine(const CTxIn& txin) const;
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const;
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
bool IsChange(const CTxOut& txout) const;
CAmount GetChange(const CTxOut& txout) const;
bool IsMine(const CTransaction& tx) const;
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
CAmount GetCredit(const CWalletTx& tx, const isminefilter& filter) const;
CAmount GetChange(const CWalletTx& tx) const;
void SetBestChain(const CBlockLocator& loc);
DBErrors LoadWallet(bool& fFirstRunRet);
@ -886,6 +919,12 @@ public:
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
bool AbandonTransaction(const uint256& hashTx);
//! 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;
/* Returns the wallets help message */
static std::string GetWalletHelpString(bool showDebug);

View file

@ -197,6 +197,16 @@ bool CWalletDB::WriteAccountingEntry_Backend(const CAccountingEntry& acentry)
return WriteAccountingEntry(++nAccountingEntryNumber, acentry);
}
bool CWalletDB::WriteSpecificBlindingKey(const CScriptID& scriptid, const uint256& key)
{
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)
{
list<CAccountingEntry> entries;
@ -609,6 +619,36 @@ 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()) {
strErr = "Error reading wallet blinding key";
return false;
}
}
else if (strType == "blindingderivationkey")
{
assert(pwallet->blinding_derivation_key.IsNull());
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 (...)
{
return false;
@ -726,6 +766,17 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair((CWalletTx*)0, &entry)));
}
if (result == DB_LOAD_OK && pwallet->blinding_derivation_key.IsNull()) {
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

@ -25,6 +25,7 @@ struct CBlockLocator;
class CKeyPool;
class CMasterKey;
class CScript;
class CScriptID;
class CWallet;
class CWalletTx;
class uint160;
@ -167,6 +168,9 @@ public:
CAmount GetAccountCreditDebit(const std::string& strAccount);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries);
bool WriteSpecificBlindingKey(const CScriptID& scriptid, const uint256& key);
bool WriteBlindingDerivationKey(const uint256& key);
DBErrors ReorderTransactions(CWallet* pwallet);
DBErrors LoadWallet(CWallet* pwallet);
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);