mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Wallet updates for CT
This commit is contained in:
parent
8cbec40bec
commit
8eb5eaab32
8 changed files with 640 additions and 81 deletions
|
|
@ -83,6 +83,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "getblockheader", 1, "verbose" },
|
||||
{ "gettransaction", 1, "include_watchonly" },
|
||||
{ "getrawtransaction", 1, "verbose" },
|
||||
{ "rawblindrawtransaction", 1, "inputblinder" },
|
||||
{ "createrawtransaction", 0, "inputs" },
|
||||
{ "createrawtransaction", 1, "outputs" },
|
||||
{ "createrawtransaction", 2, "locktime" },
|
||||
|
|
|
|||
|
|
@ -168,6 +168,8 @@ UniValue validateaddress(const JSONRPCRequest& request)
|
|||
" \"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"
|
||||
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\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"
|
||||
|
|
@ -196,10 +198,26 @@ UniValue validateaddress(const JSONRPCRequest& request)
|
|||
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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -101,7 +127,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);
|
||||
|
|
@ -370,6 +417,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
|
|||
" {\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"
|
||||
|
|
@ -468,6 +516,12 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
|
|||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -477,6 +531,153 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
|
|||
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 JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || (request.params.size() != 2 && request.params.size() != 3))
|
||||
throw std::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 (request.params.size() == 2) {
|
||||
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR));
|
||||
} else {
|
||||
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VSTR));
|
||||
}
|
||||
|
||||
vector<unsigned char> txData(ParseHexV(request.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 = request.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 JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || (request.params.size() != 1 && request.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 (request.params.size() == 1) {
|
||||
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR));
|
||||
} else {
|
||||
RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VSTR));
|
||||
}
|
||||
|
||||
vector<unsigned char> txData(ParseHexV(request.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.tx->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 JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
|
|
@ -953,6 +1154,8 @@ static const CRPCCommand commands[] =
|
|||
{ "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} },
|
||||
{ "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */
|
||||
{ "rawtransactions", "rawblindrawtransaction", &rawblindrawtransaction, false, {}},
|
||||
{ "rawtransactions", "blindrawtransaction", &blindrawtransaction, true, {}},
|
||||
|
||||
{ "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} },
|
||||
{ "blockchain", "verifytxoutproof", &verifytxoutproof, true, {"proof"} },
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ UniValue getnewaddress(const JSONRPCRequest& request)
|
|||
|
||||
pwalletMain->SetAddressBook(keyID, strAccount, "receive");
|
||||
|
||||
return CBitcoinAddress(keyID).ToString();
|
||||
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(CTxDestination(keyID)))).ToString();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -154,7 +154,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 JSONRPCRequest& request)
|
||||
|
|
@ -220,7 +220,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
|||
|
||||
CKeyID keyID = vchPubKey.GetID();
|
||||
|
||||
return CBitcoinAddress(keyID).ToString();
|
||||
return CBitcoinAddress(keyID).AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(CTxDestination(keyID)))).ToString();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -329,9 +329,10 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
|
|||
|
||||
// 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());
|
||||
|
|
@ -339,7 +340,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
|
|||
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();
|
||||
|
||||
|
|
@ -362,7 +363,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 > curBalance)
|
||||
|
|
@ -416,6 +417,11 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
|
|||
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 (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty())
|
||||
|
|
@ -429,7 +435,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
|
|||
|
||||
EnsureWalletIsUnlocked();
|
||||
|
||||
SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx);
|
||||
SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, confidentiality_pubkey, wtx);
|
||||
|
||||
return wtx.GetHash().GetHex();
|
||||
}
|
||||
|
|
@ -472,11 +478,13 @@ UniValue listaddressgroupings(const JSONRPCRequest& request)
|
|||
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);
|
||||
}
|
||||
|
|
@ -589,10 +597,11 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
|
|||
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
||||
continue;
|
||||
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
|
||||
if (txout.scriptPubKey == scriptPubKey)
|
||||
|
||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
||||
if (wtx.tx->vout[i].scriptPubKey == scriptPubKey)
|
||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
nAmount += txout.nValue.GetAmount();
|
||||
nAmount += wtx.GetValueOut(i);
|
||||
}
|
||||
|
||||
return ValueFromAmount(nAmount);
|
||||
|
|
@ -643,12 +652,12 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request)
|
|||
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
||||
continue;
|
||||
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
|
||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
||||
{
|
||||
CTxDestination address;
|
||||
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
|
||||
if (ExtractDestination(wtx.tx->vout[i].scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address))
|
||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
nAmount += txout.nValue.GetAmount();
|
||||
nAmount += wtx.GetValueOut(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -855,6 +864,11 @@ UniValue sendfrom(const JSONRPCRequest& request)
|
|||
if (request.params.size() > 3)
|
||||
nMinDepth = request.params[3].get_int();
|
||||
|
||||
|
||||
CPubKey confidentiality_pubkey;
|
||||
if (address.IsBlinded())
|
||||
confidentiality_pubkey = address.GetBlindingKey();
|
||||
|
||||
CWalletTx wtx;
|
||||
wtx.strFromAccount = strAccount;
|
||||
if (request.params.size() > 4 && !request.params[4].isNull() && !request.params[4].get_str().empty())
|
||||
|
|
@ -869,7 +883,7 @@ UniValue sendfrom(const JSONRPCRequest& request)
|
|||
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();
|
||||
}
|
||||
|
|
@ -957,6 +971,11 @@ UniValue sendmany(const JSONRPCRequest& request)
|
|||
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];
|
||||
|
|
@ -964,7 +983,7 @@ UniValue sendmany(const JSONRPCRequest& request)
|
|||
fSubtractFeeFromAmount = true;
|
||||
}
|
||||
|
||||
CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
|
||||
CRecipient recipient = {scriptPubKey, nAmount, confidentiality_pubkey, fSubtractFeeFromAmount};
|
||||
vecSend.push_back(recipient);
|
||||
}
|
||||
|
||||
|
|
@ -1134,6 +1153,7 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
|
|||
|
||||
struct tallyitem
|
||||
{
|
||||
CBitcoinAddress address;
|
||||
CAmount nAmount;
|
||||
int nConf;
|
||||
vector<uint256> txids;
|
||||
|
|
@ -1164,7 +1184,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;
|
||||
|
|
@ -1176,18 +1196,23 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
|
|||
if (nDepth < nMinDepth)
|
||||
continue;
|
||||
|
||||
BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout)
|
||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
|
||||
{
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(txout.scriptPubKey, address))
|
||||
if (!ExtractDestination(wtx.tx->vout[i].scriptPubKey, address))
|
||||
continue;
|
||||
|
||||
isminefilter mine = IsMine(*pwalletMain, address);
|
||||
if(!(mine & filter))
|
||||
continue;
|
||||
|
||||
CBitcoinAddress bitcoinaddress(address);
|
||||
if (!wtx.tx->vout[i].nValue.IsAmount())
|
||||
bitcoinaddress.AddBlindingKey(wtx.GetBlindingKey(i));
|
||||
|
||||
tallyitem& item = mapTally[address];
|
||||
item.nAmount += txout.nValue.GetAmount();
|
||||
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)
|
||||
|
|
@ -1198,19 +1223,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;
|
||||
|
|
@ -1228,7 +1255,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)));
|
||||
|
|
@ -1345,11 +1372,14 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request)
|
|||
return ListReceived(request.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)
|
||||
|
|
@ -1373,7 +1403,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))
|
||||
|
|
@ -1401,7 +1431,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)
|
||||
|
|
@ -2422,6 +2452,8 @@ UniValue listunspent(const JSONRPCRequest& request)
|
|||
" \"scriptPubKey\" : \"key\", (string) the script key\n"
|
||||
" \"amount\" : x.xxx, (numeric) the transaction output 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"
|
||||
|
|
@ -2484,13 +2516,16 @@ UniValue listunspent(const JSONRPCRequest& request)
|
|||
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));
|
||||
|
||||
|
|
@ -2503,10 +2538,14 @@ UniValue listunspent(const JSONRPCRequest& request)
|
|||
}
|
||||
|
||||
entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
||||
entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue.GetAmount())));
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "wallet/coincontrol.h"
|
||||
#include "consensus/consensus.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "crypto/hmac_sha256.h"
|
||||
#include "key.h"
|
||||
#include "keystore.h"
|
||||
#include "validation.h"
|
||||
|
|
@ -32,6 +33,8 @@
|
|||
#include <boost/filesystem.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <secp256k1.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
CWallet* pwalletMain = NULL;
|
||||
|
|
@ -75,7 +78,7 @@ struct CompareValueOnly
|
|||
|
||||
std::string COutput::ToString() const
|
||||
{
|
||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue.GetAmount()));
|
||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->GetValueOut(i)));
|
||||
}
|
||||
|
||||
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
|
||||
|
|
@ -1226,7 +1229,7 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
|
|||
const CWalletTx& prev = (*mi).second;
|
||||
if (txin.prevout.n < prev.tx->vout.size())
|
||||
if (IsMine(prev.tx->vout[txin.prevout.n]) & filter)
|
||||
return prev.tx->vout[txin.prevout.n].nValue.GetAmount();
|
||||
return prev.GetValueOut(txin.prevout.n);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -1237,13 +1240,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.GetAmount()))
|
||||
throw std::runtime_error(std::string(__func__) + ": value out of range");
|
||||
return ((IsMine(txout) & filter) ? txout.nValue.GetAmount() : 0);
|
||||
}
|
||||
|
||||
bool CWallet::IsChange(const CTxOut& txout) const
|
||||
{
|
||||
// TODO: fix handling of 'change' outputs. The assumption is that any
|
||||
|
|
@ -1266,13 +1262,6 @@ bool CWallet::IsChange(const CTxOut& txout) const
|
|||
return false;
|
||||
}
|
||||
|
||||
CAmount CWallet::GetChange(const CTxOut& txout) const
|
||||
{
|
||||
if (!MoneyRange(txout.nValue.GetAmount()))
|
||||
throw std::runtime_error(std::string(__func__) + ": value out of range");
|
||||
return (IsChange(txout) ? txout.nValue.GetAmount() : 0);
|
||||
}
|
||||
|
||||
bool CWallet::IsMine(const CTransaction& tx) const
|
||||
{
|
||||
BOOST_FOREACH(const CTxOut& txout, tx.vout)
|
||||
|
|
@ -1319,24 +1308,24 @@ bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) co
|
|||
return true;
|
||||
}
|
||||
|
||||
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.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.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");
|
||||
}
|
||||
|
|
@ -1492,7 +1481,11 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
|
|||
address = CNoDestination();
|
||||
}
|
||||
|
||||
COutputEntry output = {address, txout.nValue.GetAmount(), (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)
|
||||
|
|
@ -1692,6 +1685,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(tx->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
|
||||
|
|
@ -1757,8 +1763,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const
|
|||
{
|
||||
if (!pwallet->IsSpent(hashTx, i))
|
||||
{
|
||||
const CTxOut &txout = tx->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");
|
||||
}
|
||||
|
|
@ -1800,8 +1805,7 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
|
|||
{
|
||||
if (!pwallet->IsSpent(GetHash(), i))
|
||||
{
|
||||
const CTxOut &txout = tx->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");
|
||||
}
|
||||
|
|
@ -1812,6 +1816,16 @@ CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const
|
|||
return nCredit;
|
||||
}
|
||||
|
||||
CAmount CWalletTx::GetChange(unsigned int nTxOut) const
|
||||
{
|
||||
CAmount amount = 0;
|
||||
if (pwallet->IsChange(tx->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)
|
||||
|
|
@ -1870,6 +1884,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 < tx->vout.size());
|
||||
if (mapValue["blindingdata"].size() < (nOut + 1) * 74) {
|
||||
mapValue["blindingdata"].resize(tx->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(tx->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, CConnman* connman)
|
||||
{
|
||||
std::vector<uint256> result;
|
||||
|
|
@ -2081,7 +2157,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
|
|||
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
|
||||
isminetype mine = IsMine(pcoin->tx->vout[i]);
|
||||
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
|
||||
!IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue.GetAmount() > 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) ||
|
||||
|
|
@ -2167,7 +2243,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin
|
|||
continue;
|
||||
|
||||
int i = output.i;
|
||||
CAmount n = pcoin->tx->vout[i].nValue.GetAmount();
|
||||
CAmount n = pcoin->GetValueOut(i);
|
||||
|
||||
pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
|
||||
|
||||
|
|
@ -2253,8 +2329,8 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
|
|||
BOOST_FOREACH(const COutput& out, vCoins)
|
||||
{
|
||||
if (!out.fSpendable)
|
||||
continue;
|
||||
nValueRet += out.tx->tx->vout[out.i].nValue.GetAmount();
|
||||
continue;
|
||||
nValueRet += out.tx->GetValueOut(out.i);
|
||||
setCoinsRet.insert(make_pair(out.tx, out.i));
|
||||
}
|
||||
return (nValueRet >= nTargetValue);
|
||||
|
|
@ -2276,7 +2352,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
|
|||
// Clearly invalid input, fail
|
||||
if (pcoin->tx->vout.size() <= outpoint.n)
|
||||
return false;
|
||||
nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue.GetAmount();
|
||||
nValueFromPresetInputs += pcoin->GetValueOut(outpoint.n);
|
||||
setPresetCoins.insert(make_pair(pcoin, outpoint.n));
|
||||
} else
|
||||
return false; // TODO: Allow non-wallet inputs
|
||||
|
|
@ -2320,7 +2396,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
|
|||
for (size_t idx = 0; idx < tx.vout.size(); idx++)
|
||||
{
|
||||
const CTxOut& txOut = tx.vout[idx];
|
||||
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue.GetAmount(), setSubtractFeeFromOutputs.count(idx) == 1};
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -2440,6 +2520,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();
|
||||
wtxNew.fFromMe = true;
|
||||
|
|
@ -2456,12 +2539,12 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||
|
||||
if (recipient.fSubtractFeeFromAmount)
|
||||
{
|
||||
txout.nValue = CTxOutValue(txout.nValue.GetAmount() - (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 = CTxOutValue(txout.nValue.GetAmount() - (nFeeRet % nSubtractFeeFromAmount));
|
||||
txout.nValue = recipient.nAmount - (nFeeRet / nSubtractFeeFromAmount) - (nFeeRet % nSubtractFeeFromAmount);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2479,6 +2562,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
|
||||
|
|
@ -2489,9 +2575,12 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||
strFailReason = _("Insufficient funds");
|
||||
return false;
|
||||
}
|
||||
for (const auto& pcoin : setCoins)
|
||||
bool fBlindedIns = false;
|
||||
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
|
||||
{
|
||||
CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue.GetAmount();
|
||||
if (!pcoin.first->tx->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.
|
||||
|
|
@ -2545,13 +2634,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(dustRelayFee))
|
||||
{
|
||||
CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue.GetAmount();
|
||||
newTxOut.nValue = CTxOutValue(newTxOut.nValue.GetAmount() + 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 = CTxOutValue(txNew.vout[i].nValue.GetAmount() - nDust);
|
||||
txNew.vout[i].nValue = txNew.vout[i].nValue.GetAmount() - nDust;
|
||||
if (txNew.vout[i].IsDust(dustRelayFee))
|
||||
{
|
||||
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
|
||||
|
|
@ -2585,11 +2674,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 non-maxint so that
|
||||
|
|
@ -2605,6 +2704,23 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||
std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 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);
|
||||
|
||||
// Fill in dummy signatures for fee calculation.
|
||||
if (!DummySignTx(txNew, setCoins)) {
|
||||
|
|
@ -3146,7 +3262,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances()
|
|||
if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
|
||||
continue;
|
||||
|
||||
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue.GetAmount();
|
||||
CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->GetValueOut(i);
|
||||
|
||||
if (!balances.count(addr))
|
||||
balances[addr] = 0;
|
||||
|
|
@ -3976,3 +4092,91 @@ bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
|
|||
{
|
||||
return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, 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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -137,6 +138,7 @@ struct CRecipient
|
|||
{
|
||||
CScript scriptPubKey;
|
||||
CAmount nAmount;
|
||||
CPubKey confidentiality_key;
|
||||
bool fSubtractFeeFromAmount;
|
||||
};
|
||||
|
||||
|
|
@ -166,6 +168,7 @@ struct COutputEntry
|
|||
CTxDestination destination;
|
||||
CAmount amount;
|
||||
int vout;
|
||||
CPubKey confidentiality_pubkey;
|
||||
};
|
||||
|
||||
/** A transaction with a merkle branch linking it to the block chain. */
|
||||
|
|
@ -256,7 +259,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
|
||||
|
|
@ -270,6 +273,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;
|
||||
|
|
@ -397,11 +405,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,
|
||||
|
|
@ -427,6 +437,17 @@ public:
|
|||
bool RelayWalletTransaction(CConnman* connman);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -656,6 +677,7 @@ public:
|
|||
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
|
||||
MasterKeyMap mapMasterKeys;
|
||||
unsigned int nMasterKeyMaxID;
|
||||
std::map<CScriptID, uint256> mapSpecificBlindingKeys;
|
||||
|
||||
CWallet()
|
||||
{
|
||||
|
|
@ -686,6 +708,8 @@ public:
|
|||
nLastResend = 0;
|
||||
nTimeFirstKey = 0;
|
||||
fBroadcastTransactions = false;
|
||||
blinding_key = CKey();
|
||||
blinding_derivation_key = uint256();
|
||||
}
|
||||
|
||||
std::map<uint256, CWalletTx> mapWallet;
|
||||
|
|
@ -704,6 +728,13 @@ public:
|
|||
|
||||
std::set<COutPoint> setLockedCoins;
|
||||
|
||||
//! 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
|
||||
|
|
@ -742,6 +773,10 @@ public:
|
|||
bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); }
|
||||
//! Load metadata (used by LoadWallet)
|
||||
bool LoadKeyMetadata(const CTxDestination& 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; }
|
||||
void UpdateTimeFirstKey(int64_t nCreateTime);
|
||||
|
|
@ -861,17 +896,15 @@ public:
|
|||
*/
|
||||
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 CWalletTx& tx, const isminefilter& filter) const;
|
||||
CAmount GetChange(const CWalletTx& tx) const;
|
||||
/** Returns whether all of the inputs match the filter */
|
||||
bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
|
||||
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
|
||||
CAmount GetChange(const CTransaction& tx) const;
|
||||
void SetBestChain(const CBlockLocator& loc) override;
|
||||
|
||||
DBErrors LoadWallet(bool& fFirstRunRet);
|
||||
|
|
@ -960,6 +993,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;
|
||||
|
||||
/** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
|
||||
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
|
||||
|
||||
|
|
|
|||
|
|
@ -204,6 +204,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;
|
||||
|
|
@ -539,6 +549,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;
|
||||
|
|
@ -656,6 +696,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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ struct CBlockLocator;
|
|||
class CKeyPool;
|
||||
class CMasterKey;
|
||||
class CScript;
|
||||
class CScriptID;
|
||||
class CWallet;
|
||||
class CWalletTx;
|
||||
class uint160;
|
||||
|
|
@ -166,6 +167,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 LoadWallet(CWallet* pwallet);
|
||||
DBErrors FindWalletTx(CWallet* pwallet, std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
|
||||
DBErrors ZapWalletTx(CWallet* pwallet, std::vector<CWalletTx>& vWtx);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue