mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-19 13:27:35 +02:00
[BROKEN] Adapt wallet to CA
This commit is contained in:
parent
49e808373e
commit
d3ab44de44
13 changed files with 1962 additions and 360 deletions
|
|
@ -31,7 +31,7 @@ namespace {
|
||||||
class PendingWalletTxImpl : public PendingWalletTx
|
class PendingWalletTxImpl : public PendingWalletTx
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {}
|
explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet) { m_keys.reserve(1); m_keys.emplace_back(new CReserveKey(&wallet)); }
|
||||||
|
|
||||||
const CTransaction& get() override { return *m_tx; }
|
const CTransaction& get() override { return *m_tx; }
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ public:
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, m_wallet.cs_wallet);
|
LOCK2(cs_main, m_wallet.cs_wallet);
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) {
|
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_keys, g_connman.get(), state)) {
|
||||||
reject_reason = state.GetRejectReason();
|
reject_reason = state.GetRejectReason();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,7 @@ public:
|
||||||
|
|
||||||
CTransactionRef m_tx;
|
CTransactionRef m_tx;
|
||||||
CWallet& m_wallet;
|
CWallet& m_wallet;
|
||||||
CReserveKey m_key;
|
std::vector<std::unique_ptr<CReserveKey>> m_keys;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Construct wallet tx struct.
|
//! Construct wallet tx struct.
|
||||||
|
|
@ -224,7 +224,7 @@ public:
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, m_wallet.cs_wallet);
|
LOCK2(cs_main, m_wallet.cs_wallet);
|
||||||
auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
|
auto pending = MakeUnique<PendingWalletTxImpl>(m_wallet);
|
||||||
if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_key, fee, change_pos,
|
if (!m_wallet.CreateTransaction(recipients, pending->m_tx, pending->m_keys, fee, change_pos,
|
||||||
fail_reason, coin_control, sign)) {
|
fail_reason, coin_control, sign)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "rawblindrawtransaction", 3, "inputasset" },
|
{ "rawblindrawtransaction", 3, "inputasset" },
|
||||||
{ "rawblindrawtransaction", 4, "inputassetblinder" },
|
{ "rawblindrawtransaction", 4, "inputassetblinder" },
|
||||||
{ "rawblindrawtransaction", 6, "ignoreblindfail" },
|
{ "rawblindrawtransaction", 6, "ignoreblindfail" },
|
||||||
|
{ "sendmany", 7 , "output_assets" },
|
||||||
|
{ "sendmany", 8 , "ignoreblindfail" },
|
||||||
|
{ "sendtoaddress", 9 , "ignoreblindfail" },
|
||||||
{ "createrawtransaction", 4, "output_assets" },
|
{ "createrawtransaction", 4, "output_assets" },
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
void CCoinControl::SetNull()
|
void CCoinControl::SetNull()
|
||||||
{
|
{
|
||||||
destChange = CNoDestination();
|
destChange.clear();
|
||||||
m_change_type.reset();
|
m_change_type.reset();
|
||||||
fAllowOtherInputs = false;
|
fAllowOtherInputs = false;
|
||||||
fAllowWatchOnly = false;
|
fAllowWatchOnly = false;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#ifndef BITCOIN_WALLET_COINCONTROL_H
|
#ifndef BITCOIN_WALLET_COINCONTROL_H
|
||||||
#define BITCOIN_WALLET_COINCONTROL_H
|
#define BITCOIN_WALLET_COINCONTROL_H
|
||||||
|
|
||||||
|
#include <asset.h>
|
||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <policy/fees.h>
|
#include <policy/fees.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
|
|
@ -17,7 +18,7 @@ class CCoinControl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//! Custom change destination, if not set an address is generated
|
//! Custom change destination, if not set an address is generated
|
||||||
CTxDestination destChange;
|
std::map<CAsset, CTxDestination> destChange;
|
||||||
//! Override the default change type if set, ignored if destChange is set
|
//! Override the default change type if set, ignored if destChange is set
|
||||||
boost::optional<OutputType> m_change_type;
|
boost::optional<OutputType> m_change_type;
|
||||||
//! If false, allows unselected inputs, but requires all selected inputs be used
|
//! If false, allows unselected inputs, but requires all selected inputs be used
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,28 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <wallet/coinselection.h>
|
#include <wallet/coinselection.h>
|
||||||
|
#include <wallet/wallet.h>
|
||||||
|
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
#include <utilmoneystr.h>
|
#include <utilmoneystr.h>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
CInputCoin::CInputCoin(const CWalletTx* wtx, unsigned int i) {
|
||||||
|
if (!wtx || !wtx->tx)
|
||||||
|
throw std::invalid_argument("tx should not be null");
|
||||||
|
if (i >= wtx->tx->vout.size())
|
||||||
|
throw std::out_of_range("The output index is out of range");
|
||||||
|
|
||||||
|
outpoint = COutPoint(wtx->tx->GetHash(), i);
|
||||||
|
txout = wtx->tx->vout[i];
|
||||||
|
effective_value = std::max<CAmount>(0, wtx->GetOutputValueOut(i));
|
||||||
|
value = wtx->GetOutputValueOut(i);
|
||||||
|
asset = wtx->GetOutputAsset(i);
|
||||||
|
bf_value = wtx->GetOutputAmountBlindingFactor(i);
|
||||||
|
bf_asset = wtx->GetOutputAssetBlindingFactor(i);
|
||||||
|
}
|
||||||
|
|
||||||
// Descending order comparator
|
// Descending order comparator
|
||||||
struct {
|
struct {
|
||||||
bool operator()(const OutputGroup& a, const OutputGroup& b) const
|
bool operator()(const OutputGroup& a, const OutputGroup& b) const
|
||||||
|
|
@ -213,6 +229,62 @@ static void ApproximateBestSubset(const std::vector<OutputGroup>& groups, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
bool KnapsackSolver(const CAmountMap& mapTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmountMap& mapValueRet) {
|
||||||
|
setCoinsRet.clear();
|
||||||
|
mapValueRet.clear();
|
||||||
|
|
||||||
|
std::vector<OutputGroup> inner_groups;
|
||||||
|
std::set<CInputCoin> inner_coinsret;
|
||||||
|
// Perform the standard Knapsack solver for every asset individually.
|
||||||
|
for(std::map<CAsset, CAmount>::const_iterator it = mapTargetValue.begin(); it != mapTargetValue.end(); ++it) {
|
||||||
|
inner_groups.clear();
|
||||||
|
inner_coinsret.clear();
|
||||||
|
|
||||||
|
if (it->second == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We filter the groups on two conditions:
|
||||||
|
// - only groups that have (exclusively) coins of the asset we're solving for
|
||||||
|
// - no groups that are already used in setCoinsRet
|
||||||
|
for (const OutputGroup& g : groups) {
|
||||||
|
bool add = true;
|
||||||
|
for (const CInputCoin& c : g.m_outputs) {
|
||||||
|
if (setCoinsRet.find(c) != setCoinsRet.end()) {
|
||||||
|
add = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.asset != it->first) {
|
||||||
|
add = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add) {
|
||||||
|
inner_groups.push_back(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inner_groups.size() == 0) {
|
||||||
|
// No output groups for this asset.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAmount outValue;
|
||||||
|
if (!KnapsackSolver(it->second, inner_groups, inner_coinsret, outValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mapValueRet[it->first] = outValue;
|
||||||
|
for (const CInputCoin& ic : inner_coinsret) {
|
||||||
|
setCoinsRet.insert(ic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet)
|
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet)
|
||||||
{
|
{
|
||||||
setCoinsRet.clear();
|
setCoinsRet.clear();
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,14 @@ static const CAmount MIN_CHANGE = CENT;
|
||||||
//! final minimum change amount after paying for fees
|
//! final minimum change amount after paying for fees
|
||||||
static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;
|
static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;
|
||||||
|
|
||||||
|
class CWalletTx;
|
||||||
|
class uint256;
|
||||||
|
|
||||||
class CInputCoin {
|
class CInputCoin {
|
||||||
public:
|
public:
|
||||||
CInputCoin(const CTransactionRef& tx, unsigned int i)
|
CInputCoin(const CWalletTx* wtx, unsigned int i);
|
||||||
{
|
|
||||||
if (!tx)
|
|
||||||
throw std::invalid_argument("tx should not be null");
|
|
||||||
if (i >= tx->vout.size())
|
|
||||||
throw std::out_of_range("The output index is out of range");
|
|
||||||
|
|
||||||
outpoint = COutPoint(tx->GetHash(), i);
|
CInputCoin(const CWalletTx* wtx, unsigned int i, int input_bytes) : CInputCoin(wtx, i)
|
||||||
txout = tx->vout[i];
|
|
||||||
effective_value = txout.nValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CInputCoin(const CTransactionRef& tx, unsigned int i, int input_bytes) : CInputCoin(tx, i)
|
|
||||||
{
|
{
|
||||||
m_input_bytes = input_bytes;
|
m_input_bytes = input_bytes;
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +29,11 @@ public:
|
||||||
COutPoint outpoint;
|
COutPoint outpoint;
|
||||||
CTxOut txout;
|
CTxOut txout;
|
||||||
CAmount effective_value;
|
CAmount effective_value;
|
||||||
|
// ELEMENTS:
|
||||||
|
CAmount value;
|
||||||
|
CAsset asset;
|
||||||
|
uint256 bf_value;
|
||||||
|
uint256 bf_asset;
|
||||||
|
|
||||||
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
|
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
|
||||||
int m_input_bytes{-1};
|
int m_input_bytes{-1};
|
||||||
|
|
@ -98,4 +96,8 @@ bool SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& target_v
|
||||||
// Original coin selection algorithm as a fallback
|
// Original coin selection algorithm as a fallback
|
||||||
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
|
bool KnapsackSolver(const CAmount& nTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet);
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
// Knapsack that delegates for every asset individually.
|
||||||
|
bool KnapsackSolver(const CAmountMap& mapTargetValue, std::vector<OutputGroup>& groups, std::set<CInputCoin>& setCoinsRet, CAmountMap& mapValueRet);
|
||||||
|
|
||||||
#endif // BITCOIN_WALLET_COINSELECTION_H
|
#endif // BITCOIN_WALLET_COINSELECTION_H
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,10 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
|
||||||
// if there was no change output or multiple change outputs, fail
|
// if there was no change output or multiple change outputs, fail
|
||||||
int nOutput = -1;
|
int nOutput = -1;
|
||||||
for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
|
for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
|
||||||
|
if (wtx.GetOutputAsset(i) != ::policyAsset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (wallet->IsChange(wtx.tx->vout[i])) {
|
if (wallet->IsChange(wtx.tx->vout[i])) {
|
||||||
if (nOutput != -1) {
|
if (nOutput != -1) {
|
||||||
errors.push_back("Transaction has multiple change outputs");
|
errors.push_back("Transaction has multiple change outputs");
|
||||||
|
|
@ -107,8 +111,22 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
|
||||||
return Result::WALLET_ERROR;
|
return Result::WALLET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the fee output.
|
||||||
|
int nFeeOutput = -1;
|
||||||
|
for (int i = (int)wtx.tx->vout.size()-1; i >= 0; --i) {
|
||||||
|
if (wtx.GetOutputAsset(i) == ::policyAsset && wtx.tx->vout[i].IsFee()) {
|
||||||
|
nFeeOutput = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate the expected size of the new transaction.
|
// Calculate the expected size of the new transaction.
|
||||||
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
|
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
|
||||||
|
if (g_con_elementsmode && nFeeOutput == -1) {
|
||||||
|
CMutableTransaction with_fee_output = CMutableTransaction{*wtx.tx};
|
||||||
|
with_fee_output.vout.push_back(CTxOut(::policyAsset, 0, CScript()));
|
||||||
|
txSize = GetVirtualTransactionSize(with_fee_output);
|
||||||
|
}
|
||||||
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, wallet);
|
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, wallet);
|
||||||
if (maxNewTxSize < 0) {
|
if (maxNewTxSize < 0) {
|
||||||
errors.push_back("Transaction contains inputs that cannot be signed");
|
errors.push_back("Transaction contains inputs that cannot be signed");
|
||||||
|
|
@ -116,7 +134,10 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the old fee and fee-rate
|
// calculate the old fee and fee-rate
|
||||||
old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
|
old_fee = wtx.GetDebit(ISMINE_SPENDABLE)[::policyAsset] - wtx.tx->GetValueOutMap()[::policyAsset];
|
||||||
|
if (g_con_elementsmode) {
|
||||||
|
old_fee = GetFeeMap(*wtx.tx)[::policyAsset];
|
||||||
|
}
|
||||||
CFeeRate nOldFeeRate(old_fee, txSize);
|
CFeeRate nOldFeeRate(old_fee, txSize);
|
||||||
CFeeRate nNewFeeRate;
|
CFeeRate nNewFeeRate;
|
||||||
// The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
|
// The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
|
||||||
|
|
@ -187,20 +208,33 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
|
||||||
assert(nDelta > 0);
|
assert(nDelta > 0);
|
||||||
mtx = CMutableTransaction{*wtx.tx};
|
mtx = CMutableTransaction{*wtx.tx};
|
||||||
CTxOut* poutput = &(mtx.vout[nOutput]);
|
CTxOut* poutput = &(mtx.vout[nOutput]);
|
||||||
if (poutput->nValue < nDelta) {
|
// TODO CA: Decrypt output amount using wallet
|
||||||
|
if (!poutput->nValue.IsExplicit() || poutput->nValue.GetAmount() < nDelta) {
|
||||||
errors.push_back("Change output is too small to bump the fee");
|
errors.push_back("Change output is too small to bump the fee");
|
||||||
return Result::WALLET_ERROR;
|
return Result::WALLET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the output would become dust, discard it (converting the dust to fee)
|
// If the output would become dust, discard it (converting the dust to fee)
|
||||||
poutput->nValue -= nDelta;
|
poutput->nValue = poutput->nValue.GetAmount() - nDelta;
|
||||||
if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
|
if (poutput->nValue.GetAmount() <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
|
||||||
wallet->WalletLogPrintf("Bumping fee and discarding dust output\n");
|
wallet->WalletLogPrintf("Bumping fee and discarding dust output\n");
|
||||||
new_fee += poutput->nValue;
|
new_fee += poutput->nValue.GetAmount();
|
||||||
mtx.vout.erase(mtx.vout.begin() + nOutput);
|
mtx.vout.erase(mtx.vout.begin() + nOutput);
|
||||||
if (mtx.witness.vtxoutwit.size() > (size_t) nOutput) {
|
if (mtx.witness.vtxoutwit.size() > (size_t) nOutput) {
|
||||||
mtx.witness.vtxoutwit.erase(mtx.witness.vtxoutwit.begin() + nOutput);
|
mtx.witness.vtxoutwit.erase(mtx.witness.vtxoutwit.begin() + nOutput);
|
||||||
}
|
}
|
||||||
|
if (nFeeOutput > nOutput) {
|
||||||
|
--nFeeOutput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fee output or add one.
|
||||||
|
if (g_con_elementsmode) {
|
||||||
|
if (nFeeOutput >= 0) {
|
||||||
|
mtx.vout[nFeeOutput].nValue.SetToAmount(new_fee);
|
||||||
|
} else {
|
||||||
|
mtx.vout.push_back(CTxOut(::policyAsset, new_fee, CScript()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark new tx not replaceable, if requested.
|
// Mark new tx not replaceable, if requested.
|
||||||
|
|
@ -242,10 +276,15 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
|
||||||
CTransactionRef tx = MakeTransactionRef(std::move(mtx));
|
CTransactionRef tx = MakeTransactionRef(std::move(mtx));
|
||||||
mapValue_t mapValue = oldWtx.mapValue;
|
mapValue_t mapValue = oldWtx.mapValue;
|
||||||
mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
|
mapValue["replaces_txid"] = oldWtx.GetHash().ToString();
|
||||||
|
// wipe blinding details to not store old information
|
||||||
|
mapValue["blindingdata"] = "";
|
||||||
|
// TODO CA: store new blinding data to remember otherwise unblindable outputs
|
||||||
|
|
||||||
CReserveKey reservekey(wallet);
|
std::vector<std::unique_ptr<CReserveKey>> reservekeys;
|
||||||
|
reservekeys.push_back(std::unique_ptr<CReserveKey>(new CReserveKey(wallet)));
|
||||||
|
//reservekeys.push_back(std::unique_ptr<CReserveKey>(wallet));
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, g_connman.get(), state)) {
|
if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekeys, g_connman.get(), state)) {
|
||||||
// NOTE: CommitTransaction never returns false, so this should never happen.
|
// NOTE: CommitTransaction never returns false, so this should never happen.
|
||||||
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
|
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
|
||||||
return Result::WALLET_ERROR;
|
return Result::WALLET_ERROR;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <univalue.h>
|
#include <univalue.h>
|
||||||
|
|
||||||
#include <script/descriptor.h> // getwalletpakinfo
|
#include <script/descriptor.h> // getwalletpakinfo
|
||||||
|
#include <rpc/util.h> // IsBlindDestination
|
||||||
|
|
||||||
|
|
||||||
int64_t static DecodeDumpTime(const std::string &str) {
|
int64_t static DecodeDumpTime(const std::string &str) {
|
||||||
|
|
@ -761,6 +762,10 @@ UniValue dumpwallet(const JSONRPCRequest& request)
|
||||||
file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
|
file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ELEMENTS: Dump the master blinding key in hex as well
|
||||||
|
if (!pwallet->blinding_derivation_key.IsNull()) {
|
||||||
|
file << ("# Master private blinding key: " + pwallet->blinding_derivation_key.GetHex() + "\n\n");
|
||||||
|
}
|
||||||
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
|
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
|
||||||
const CKeyID &keyid = it->second;
|
const CKeyID &keyid = it->second;
|
||||||
std::string strTime = FormatISO8601DateTime(it->first);
|
std::string strTime = FormatISO8601DateTime(it->first);
|
||||||
|
|
@ -825,6 +830,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
|
||||||
// Optional fields.
|
// Optional fields.
|
||||||
const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
|
const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
|
||||||
const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
|
const UniValue& pubKeys = data.exists("pubkeys") ? data["pubkeys"].get_array() : UniValue();
|
||||||
|
const std::string& str_blinding_key = data.exists("blinding_privkey") ? data["blinding_privkey"].get_str() : "";
|
||||||
const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
|
const UniValue& keys = data.exists("keys") ? data["keys"].get_array() : UniValue();
|
||||||
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
|
const bool internal = data.exists("internal") ? data["internal"].get_bool() : false;
|
||||||
const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
|
const bool watchOnly = data.exists("watchonly") ? data["watchonly"].get_bool() : false;
|
||||||
|
|
@ -878,6 +884,18 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
|
||||||
|
|
||||||
// Process. //
|
// Process. //
|
||||||
|
|
||||||
|
// Get blinding privkey
|
||||||
|
if (!str_blinding_key.empty() &&
|
||||||
|
(!IsHex(str_blinding_key) || str_blinding_key.size() != 64)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid blinding privkey");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 blinding_privkey;
|
||||||
|
if (!str_blinding_key.empty()) {
|
||||||
|
std::vector<unsigned char> blind_bytes = ParseHex(str_blinding_key);
|
||||||
|
memcpy(blinding_privkey.begin(), blind_bytes.data(), 32);
|
||||||
|
}
|
||||||
|
|
||||||
// P2SH
|
// P2SH
|
||||||
if (isP2SH) {
|
if (isP2SH) {
|
||||||
// Import redeem script.
|
// Import redeem script.
|
||||||
|
|
@ -1067,6 +1085,11 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lastly, if dest import is valid, import blinding key
|
||||||
|
if (!str_blinding_key.empty() && !pwallet->AddSpecificBlindingKey(CScriptID(GetScriptForDestination(dest)), blinding_privkey)) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding blinding key to wallet");
|
||||||
|
}
|
||||||
|
|
||||||
UniValue result = UniValue(UniValue::VOBJ);
|
UniValue result = UniValue(UniValue::VOBJ);
|
||||||
result.pushKV("success", UniValue(success));
|
result.pushKV("success", UniValue(success));
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1123,6 +1146,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
|
||||||
" creation time of all keys being imported by the importmulti call will be scanned.\n"
|
" creation time of all keys being imported by the importmulti call will be scanned.\n"
|
||||||
" \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
|
" \"redeemscript\": \"<script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n"
|
||||||
" \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
|
" \"pubkeys\": [\"<pubKey>\", ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n"
|
||||||
|
" \"blinding_privkey\": \"<privkey>\" , (string, optional) String giving blinding key in hex that is used to unblind outputs going to `scriptPubKey`\n"
|
||||||
" \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
|
" \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n"
|
||||||
" \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
|
" \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n"
|
||||||
" \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
|
" \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
|
#include <asset.h>
|
||||||
|
#include <assetsdir.h>
|
||||||
#include <block_proof.h>
|
#include <block_proof.h>
|
||||||
#include <chain.h>
|
#include <chain.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
|
|
@ -50,6 +52,8 @@
|
||||||
#include <script/generic.hpp> // signblock
|
#include <script/generic.hpp> // signblock
|
||||||
#include <script/descriptor.h> // initpegoutwallet
|
#include <script/descriptor.h> // initpegoutwallet
|
||||||
#include <span.h> // sendtomainchain_pak
|
#include <span.h> // sendtomainchain_pak
|
||||||
|
#include <blind.h>
|
||||||
|
#include <issuance.h>
|
||||||
|
|
||||||
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
|
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
|
||||||
|
|
||||||
|
|
@ -137,8 +141,12 @@ static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCK
|
||||||
}
|
}
|
||||||
entry.pushKV("bip125-replaceable", rbfStatus);
|
entry.pushKV("bip125-replaceable", rbfStatus);
|
||||||
|
|
||||||
for (const std::pair<const std::string, std::string>& item : wtx.mapValue)
|
for (const std::pair<const std::string, std::string>& item : wtx.mapValue) {
|
||||||
entry.pushKV(item.first, item.second);
|
// Skip blinding data which isn't parseable
|
||||||
|
if (item.first != "blindingdata") {
|
||||||
|
entry.pushKV(item.first, item.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string LabelFromValue(const UniValue& value)
|
static std::string LabelFromValue(const UniValue& value)
|
||||||
|
|
@ -164,6 +172,8 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
|
||||||
"\nReturns a new Bitcoin address for receiving payments.\n"
|
"\nReturns a new Bitcoin address for receiving payments.\n"
|
||||||
"If 'label' is specified, it is added to the address book \n"
|
"If 'label' is specified, it is added to the address book \n"
|
||||||
"so payments received with the address will be associated with 'label'.\n"
|
"so payments received with the address will be associated with 'label'.\n"
|
||||||
|
"Blinded addresses are returned by default, and can be set with\n"
|
||||||
|
"startup argument `-blindedaddresses`.\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"label\" (string, optional) The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
|
"1. \"label\" (string, optional) The label name for the address to be linked to. If not provided, the default label \"\" is used. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name.\n"
|
||||||
"2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
|
"2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
|
||||||
|
|
@ -203,7 +213,7 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
pwallet->LearnRelatedScripts(newKey, output_type);
|
pwallet->LearnRelatedScripts(newKey, output_type);
|
||||||
CTxDestination dest = GetDestinationForKey(newKey, output_type);
|
CTxDestination dest = GetDestinationForKey(newKey, output_type);
|
||||||
if (g_con_elementsmode) {
|
if (gArgs.GetBoolArg("-blindedaddresses", g_con_elementsmode)) {
|
||||||
CPubKey blinding_pubkey = pwallet->GetBlindingPubKey(GetScriptForDestination(dest));
|
CPubKey blinding_pubkey = pwallet->GetBlindingPubKey(GetScriptForDestination(dest));
|
||||||
dest = GetDestinationForKey(newKey, output_type, blinding_pubkey);
|
dest = GetDestinationForKey(newKey, output_type, blinding_pubkey);
|
||||||
}
|
}
|
||||||
|
|
@ -227,6 +237,8 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
||||||
"getrawchangeaddress ( \"address_type\" )\n"
|
"getrawchangeaddress ( \"address_type\" )\n"
|
||||||
"\nReturns a new Bitcoin address, for receiving change.\n"
|
"\nReturns a new Bitcoin address, for receiving change.\n"
|
||||||
"This is for use with raw transactions, NOT normal use.\n"
|
"This is for use with raw transactions, NOT normal use.\n"
|
||||||
|
"Blinded addresses are returned by default, and can be set with\n"
|
||||||
|
"startup argument `-blindedaddresses`.\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
|
"1. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
|
|
@ -262,7 +274,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
||||||
|
|
||||||
pwallet->LearnRelatedScripts(vchPubKey, output_type);
|
pwallet->LearnRelatedScripts(vchPubKey, output_type);
|
||||||
CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
|
CTxDestination dest = GetDestinationForKey(vchPubKey, output_type);
|
||||||
if (g_con_elementsmode) {
|
if (gArgs.GetBoolArg("-blindedaddresses", g_con_elementsmode)) {
|
||||||
CPubKey blinding_pubkey = pwallet->GetBlindingPubKey(GetScriptForDestination(dest));
|
CPubKey blinding_pubkey = pwallet->GetBlindingPubKey(GetScriptForDestination(dest));
|
||||||
dest = GetDestinationForKey(vchPubKey, output_type, blinding_pubkey);
|
dest = GetDestinationForKey(vchPubKey, output_type, blinding_pubkey);
|
||||||
}
|
}
|
||||||
|
|
@ -311,15 +323,15 @@ static UniValue setlabel(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue)
|
static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, const CAsset& asset, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue, bool ignore_blind_fail)
|
||||||
{
|
{
|
||||||
CAmount curBalance = pwallet->GetBalance();
|
CAmountMap curBalance = pwallet->GetBalance();
|
||||||
|
|
||||||
// Check amount
|
// Check amount
|
||||||
if (nValue <= 0)
|
if (nValue <= 0)
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
|
||||||
|
|
||||||
if (nValue > curBalance)
|
if (nValue > curBalance[asset])
|
||||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
|
||||||
|
|
||||||
if (pwallet->GetBroadcastTransactions() && !g_connman) {
|
if (pwallet->GetBroadcastTransactions() && !g_connman) {
|
||||||
|
|
@ -329,22 +341,32 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &
|
||||||
// Parse Bitcoin address
|
// Parse Bitcoin address
|
||||||
CScript scriptPubKey = GetScriptForDestination(address);
|
CScript scriptPubKey = GetScriptForDestination(address);
|
||||||
|
|
||||||
|
// Create the lower bound number of reserve keys.
|
||||||
|
std::vector<COutPoint> vPresetInputs;
|
||||||
|
coin_control.ListSelected(vPresetInputs);
|
||||||
|
int numReservedKeysNeeded = 2 + vPresetInputs.size(); // 1 fee + 1 destination
|
||||||
|
std::vector<std::unique_ptr<CReserveKey>> reserveKeys;
|
||||||
|
for (int i = 0; i < numReservedKeysNeeded; ++i) {
|
||||||
|
reserveKeys.push_back(std::unique_ptr<CReserveKey>(new CReserveKey(pwallet)));
|
||||||
|
}
|
||||||
|
|
||||||
// Create and send the transaction
|
// Create and send the transaction
|
||||||
CReserveKey reservekey(pwallet);
|
|
||||||
CAmount nFeeRequired;
|
CAmount nFeeRequired;
|
||||||
std::string strError;
|
std::string strError;
|
||||||
std::vector<CRecipient> vecSend;
|
std::vector<CRecipient> vecSend;
|
||||||
int nChangePosRet = -1;
|
int nChangePosRet = -1;
|
||||||
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
|
CRecipient recipient = {scriptPubKey, nValue, asset, GetDestinationBlindingKey(address), fSubtractFeeFromAmount};
|
||||||
vecSend.push_back(recipient);
|
vecSend.push_back(recipient);
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
|
BlindDetails* blind_details = g_con_elementsmode ? new BlindDetails() : NULL;
|
||||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
|
if (blind_details) blind_details->ignore_blind_failure = ignore_blind_fail;
|
||||||
|
if (!pwallet->CreateTransaction(vecSend, tx, reserveKeys, nFeeRequired, nChangePosRet, strError, coin_control, true, blind_details)) {
|
||||||
|
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance[policyAsset])
|
||||||
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
|
strError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired));
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
}
|
}
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, g_connman.get(), state)) {
|
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reserveKeys, g_connman.get(), state, blind_details)) {
|
||||||
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
|
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
}
|
}
|
||||||
|
|
@ -360,9 +382,9 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 10)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\")\n"
|
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount replaceable conf_target \"estimate_mode\" \"assetlabel\" ignoreblindfail )\n"
|
||||||
"\nSend an amount to a given address.\n"
|
"\nSend an amount to a given address.\n"
|
||||||
+ HelpRequiringPassphrase(pwallet) +
|
+ HelpRequiringPassphrase(pwallet) +
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
|
|
@ -381,6 +403,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
|
||||||
" \"UNSET\"\n"
|
" \"UNSET\"\n"
|
||||||
" \"ECONOMICAL\"\n"
|
" \"ECONOMICAL\"\n"
|
||||||
" \"CONSERVATIVE\"\n"
|
" \"CONSERVATIVE\"\n"
|
||||||
|
"9. \"assetlabel\" (string, optional) Hex asset id or asset label for balance.\n"
|
||||||
|
"10. \"ignoreblindfail\" (bool, default=true) Return a transaction even when a blinding attempt fails due to number of blinded inputs/outputs.\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"\"txid\" (string) The transaction id.\n"
|
"\"txid\" (string) The transaction id.\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
|
|
@ -433,10 +457,25 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string strasset = Params().GetConsensus().pegged_asset.GetHex();
|
||||||
|
LogPrintf("xxasset: %s\n", strasset);
|
||||||
|
if (request.params.size() > 8 && request.params[8].isStr() && !request.params[8].get_str().empty()) {
|
||||||
|
strasset = request.params[8].get_str();
|
||||||
|
}
|
||||||
|
LogPrintf("xxasset: %s\n", strasset);
|
||||||
|
CAsset asset = GetAssetFromString(strasset);
|
||||||
|
if (asset.IsNull() && g_con_elementsmode) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Unknown label and invalid asset hex: %s", asset.GetHex()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ignore_blind_fail = true;
|
||||||
|
if (request.params.size() > 9) {
|
||||||
|
ignore_blind_fail = request.params[9].get_bool();
|
||||||
|
}
|
||||||
|
|
||||||
EnsureWalletIsUnlocked(pwallet);
|
EnsureWalletIsUnlocked(pwallet);
|
||||||
|
|
||||||
CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
|
CTransactionRef tx = SendMoney(pwallet, dest, nAmount, asset, fSubtractFeeFromAmount, coin_control, std::move(mapValue), ignore_blind_fail);
|
||||||
return tx->GetHash().GetHex();
|
return tx->GetHash().GetHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -579,6 +618,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"address\" (string, required) The bitcoin address for transactions.\n"
|
"1. \"address\" (string, required) The bitcoin address for transactions.\n"
|
||||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||||
|
"3. \"assetlabel\" (string, optional) Hex asset id or asset label for balance.\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
|
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
|
|
@ -614,19 +654,34 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
|
||||||
nMinDepth = request.params[1].get_int();
|
nMinDepth = request.params[1].get_int();
|
||||||
|
|
||||||
// Tally
|
// Tally
|
||||||
CAmount nAmount = 0;
|
CAmountMap amounts;
|
||||||
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
|
for (auto& pairWtx : pwallet->mapWallet) {
|
||||||
const CWalletTx& wtx = pairWtx.second;
|
CWalletTx& wtx = pairWtx.second;
|
||||||
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const CTxOut& txout : wtx.tx->vout)
|
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
|
||||||
if (txout.scriptPubKey == scriptPubKey)
|
const CTxOut& txout = wtx.tx->vout[i];
|
||||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
if (txout.scriptPubKey == scriptPubKey) {
|
||||||
nAmount += txout.nValue;
|
if (wtx.GetDepthInMainChain() >= nMinDepth) {
|
||||||
|
CAmountMap wtxValue;
|
||||||
|
CAmount amt = wtx.GetOutputValueOut(i);
|
||||||
|
if (amt < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
wtxValue[wtx.GetOutputAsset(i)] = amt;
|
||||||
|
amounts += wtxValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValueFromAmount(nAmount);
|
std::string asset = "";
|
||||||
|
if (request.params.size() > 2 && request.params[2].isStr()) {
|
||||||
|
asset = request.params[2].get_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return AmountMapToUniv(amounts, asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -646,6 +701,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"label\" (string, required) The selected label, may be the default label using \"\".\n"
|
"1. \"label\" (string, required) The selected label, may be the default label using \"\".\n"
|
||||||
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
|
||||||
|
"3. \"assetlabel\" (string, optional) Hex asset id or asset label for balance.\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
|
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
|
|
@ -675,23 +731,35 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
|
||||||
std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
|
std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
|
||||||
|
|
||||||
// Tally
|
// Tally
|
||||||
CAmount nAmount = 0;
|
CAmountMap amounts;
|
||||||
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
|
for (auto& pairWtx : pwallet->mapWallet) {
|
||||||
const CWalletTx& wtx = pairWtx.second;
|
CWalletTx& wtx = pairWtx.second;
|
||||||
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const CTxOut& txout : wtx.tx->vout)
|
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
|
||||||
{
|
const CTxOut& txout = wtx.tx->vout[i];
|
||||||
CTxDestination address;
|
CTxDestination address;
|
||||||
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) {
|
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) {
|
||||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
if (wtx.GetDepthInMainChain() >= nMinDepth) {
|
||||||
nAmount += txout.nValue;
|
CAmountMap wtxValue;
|
||||||
|
CAmount amt = wtx.GetOutputValueOut(i);
|
||||||
|
if (amt < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
wtxValue[wtx.GetOutputAsset(i)] = amt;
|
||||||
|
amounts += wtxValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValueFromAmount(nAmount);
|
std::string asset = "";
|
||||||
|
if (request.params.size() > 2 && request.params[2].isStr()) {
|
||||||
|
asset = request.params[2].get_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return AmountMapToUniv(amounts, asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -704,7 +772,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.fHelp || (request.params.size() > 3 ))
|
if (request.fHelp || (request.params.size() > 4 ))
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"getbalance ( \"(dummy)\" minconf include_watchonly )\n"
|
"getbalance ( \"(dummy)\" minconf include_watchonly )\n"
|
||||||
"\nReturns the total available balance.\n"
|
"\nReturns the total available balance.\n"
|
||||||
|
|
@ -714,6 +782,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
|
||||||
"1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
|
"1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n"
|
||||||
"2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n"
|
"2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n"
|
||||||
"3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
|
"3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
|
||||||
|
"4. \"assetlabel\" (string, optional) Hex asset id or asset label for balance.\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
|
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
|
||||||
"\nExamples:\n"
|
"\nExamples:\n"
|
||||||
|
|
@ -746,7 +815,12 @@ static UniValue getbalance(const JSONRPCRequest& request)
|
||||||
filter = filter | ISMINE_WATCH_ONLY;
|
filter = filter | ISMINE_WATCH_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValueFromAmount(pwallet->GetBalance(filter, min_depth));
|
std::string asset = "";
|
||||||
|
if (!request.params[3].isNull() && request.params[3].isStr()) {
|
||||||
|
asset = request.params[3].get_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return AmountMapToUniv(pwallet->GetBalance(filter, min_depth), asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
|
static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
|
||||||
|
|
@ -769,7 +843,7 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
|
||||||
|
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
|
|
||||||
return ValueFromAmount(pwallet->GetUnconfirmedBalance());
|
return AmountMapToUniv(pwallet->GetUnconfirmedBalance(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -782,10 +856,10 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
|
if (request.fHelp || request.params.size() < 2 || request.params.size() > 10)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
|
"sendmany \"\" \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
|
||||||
"\nSend multiple times. Amounts are double-precision floating point numbers.\n"
|
"\nSend multiple times. Amounts are double-precision floating point numbers."
|
||||||
+ HelpRequiringPassphrase(pwallet) + "\n"
|
+ HelpRequiringPassphrase(pwallet) + "\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n"
|
"1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n"
|
||||||
|
|
@ -810,6 +884,12 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
" \"UNSET\"\n"
|
" \"UNSET\"\n"
|
||||||
" \"ECONOMICAL\"\n"
|
" \"ECONOMICAL\"\n"
|
||||||
" \"CONSERVATIVE\"\n"
|
" \"CONSERVATIVE\"\n"
|
||||||
|
"9. \"output_assets\" (string, optional, default=bitcoin) a json object of addresses to assets\n"
|
||||||
|
" {\n"
|
||||||
|
" \"address\": \"hex\" \n"
|
||||||
|
" ...\n"
|
||||||
|
" }\n"
|
||||||
|
"10. \"ignoreblindfail\"\" (bool, default=true) Return a transaction even when a blinding attempt fails due to number of blinded inputs/outputs.\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
|
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
|
||||||
" the number of addresses.\n"
|
" the number of addresses.\n"
|
||||||
|
|
@ -822,7 +902,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
+ HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
|
+ HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
|
||||||
"\nAs a json rpc call\n"
|
"\nAs a json rpc call\n"
|
||||||
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
|
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure the results are valid at least up to the most recent block
|
// Make sure the results are valid at least up to the most recent block
|
||||||
// the user could have gotten from another RPC command prior to now
|
// the user could have gotten from another RPC command prior to now
|
||||||
|
|
@ -865,12 +945,34 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue assets;
|
||||||
|
if (!request.params[8].isNull()) {
|
||||||
|
if (!g_con_elementsmode) {
|
||||||
|
throw JSONRPCError(RPC_TYPE_ERROR, "Asset argument cannot be given for Bitcoin serialization.");
|
||||||
|
}
|
||||||
|
assets = request.params[8].get_obj();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ignore_blind_fail = true;
|
||||||
|
if (request.params.size() > 9) {
|
||||||
|
ignore_blind_fail = request.params[9].get_bool();
|
||||||
|
}
|
||||||
|
|
||||||
std::set<CTxDestination> destinations;
|
std::set<CTxDestination> destinations;
|
||||||
std::vector<CRecipient> vecSend;
|
std::vector<CRecipient> vecSend;
|
||||||
|
|
||||||
CAmount totalAmount = 0;
|
CAmountMap totalAmount;
|
||||||
std::vector<std::string> keys = sendTo.getKeys();
|
std::vector<std::string> keys = sendTo.getKeys();
|
||||||
for (const std::string& name_ : keys) {
|
for (const std::string& name_ : keys) {
|
||||||
|
std::string strasset = Params().GetConsensus().pegged_asset.GetHex();
|
||||||
|
if (!assets.isNull() && assets[name_].isStr()) {
|
||||||
|
strasset = assets[name_].get_str();
|
||||||
|
}
|
||||||
|
CAsset asset = GetAssetFromString(strasset);
|
||||||
|
if (asset.IsNull() && g_con_elementsmode) {
|
||||||
|
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Unknown label and invalid asset hex: %s", asset.GetHex()));
|
||||||
|
}
|
||||||
|
|
||||||
CTxDestination dest = DecodeDestination(name_);
|
CTxDestination dest = DecodeDestination(name_);
|
||||||
if (!IsValidDestination(dest)) {
|
if (!IsValidDestination(dest)) {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_);
|
||||||
|
|
@ -885,7 +987,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
CAmount nAmount = AmountFromValue(sendTo[name_]);
|
CAmount nAmount = AmountFromValue(sendTo[name_]);
|
||||||
if (nAmount <= 0)
|
if (nAmount <= 0)
|
||||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
|
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
|
||||||
totalAmount += nAmount;
|
totalAmount[asset] += nAmount;
|
||||||
|
|
||||||
bool fSubtractFeeFromAmount = false;
|
bool fSubtractFeeFromAmount = false;
|
||||||
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
|
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
|
||||||
|
|
@ -894,7 +996,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
fSubtractFeeFromAmount = true;
|
fSubtractFeeFromAmount = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
|
CRecipient recipient = {scriptPubKey, nAmount, asset, GetDestinationBlindingKey(dest), fSubtractFeeFromAmount};
|
||||||
vecSend.push_back(recipient);
|
vecSend.push_back(recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -909,16 +1011,39 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
||||||
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
|
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
|
||||||
|
|
||||||
// Send
|
// Send
|
||||||
CReserveKey keyChange(pwallet);
|
std::vector<std::unique_ptr<CReserveKey>> change_keys;
|
||||||
|
|
||||||
|
std::set<CAsset> setAssets;
|
||||||
|
setAssets.insert(policyAsset);
|
||||||
|
for (auto recipient : vecSend) {
|
||||||
|
setAssets.insert(recipient.asset);
|
||||||
|
}
|
||||||
|
std::vector<COutPoint> vPresetInputs;
|
||||||
|
coin_control.ListSelected(vPresetInputs);
|
||||||
|
for (const COutPoint& presetInput : vPresetInputs) {
|
||||||
|
std::map<uint256, CWalletTx>::const_iterator it = pwallet->mapWallet.find(presetInput.hash);
|
||||||
|
if (it != pwallet->mapWallet.end()) {
|
||||||
|
setAssets.insert(it->second.GetOutputAsset(presetInput.n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i < setAssets.size(); i++) {
|
||||||
|
change_keys.push_back(std::unique_ptr<CReserveKey>(new CReserveKey(pwallet)));
|
||||||
|
}
|
||||||
|
|
||||||
CAmount nFeeRequired = 0;
|
CAmount nFeeRequired = 0;
|
||||||
int nChangePosRet = -1;
|
int nChangePosRet = -1;
|
||||||
std::string strFailReason;
|
std::string strFailReason;
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
bool fCreated = pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
|
BlindDetails* blind_details = g_con_elementsmode ? new BlindDetails() : NULL;
|
||||||
if (!fCreated)
|
if (g_con_elementsmode) {
|
||||||
|
blind_details->ignore_blind_failure = ignore_blind_fail;
|
||||||
|
}
|
||||||
|
bool fCreated = pwallet->CreateTransaction(vecSend, tx, change_keys, nFeeRequired, nChangePosRet, strFailReason, coin_control, true, blind_details);
|
||||||
|
if (!fCreated) {
|
||||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
|
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
|
||||||
|
}
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, g_connman.get(), state)) {
|
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, change_keys, g_connman.get(), state, blind_details)) {
|
||||||
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
|
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
|
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
|
||||||
}
|
}
|
||||||
|
|
@ -1131,13 +1256,13 @@ static UniValue addwitnessaddress(const JSONRPCRequest& request)
|
||||||
|
|
||||||
struct tallyitem
|
struct tallyitem
|
||||||
{
|
{
|
||||||
CAmount nAmount;
|
CAmountMap mapAmount;
|
||||||
int nConf;
|
int nConf;
|
||||||
std::vector<uint256> txids;
|
std::vector<uint256> txids;
|
||||||
bool fIsWatchonly;
|
bool fIsWatchonly;
|
||||||
tallyitem()
|
tallyitem()
|
||||||
{
|
{
|
||||||
nAmount = 0;
|
mapAmount = CAmountMap();
|
||||||
nConf = std::numeric_limits<int>::max();
|
nConf = std::numeric_limits<int>::max();
|
||||||
fIsWatchonly = false;
|
fIsWatchonly = false;
|
||||||
}
|
}
|
||||||
|
|
@ -1162,7 +1287,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
|
|
||||||
bool has_filtered_address = false;
|
bool has_filtered_address = false;
|
||||||
CTxDestination filtered_address = CNoDestination();
|
CTxDestination filtered_address = CNoDestination();
|
||||||
if (!by_label && params.size() > 3) {
|
if (!by_label && params.size() > 3 && params[3].get_str() != "") {
|
||||||
if (!IsValidDestinationString(params[3].get_str())) {
|
if (!IsValidDestinationString(params[3].get_str())) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
|
throw JSONRPCError(RPC_WALLET_ERROR, "address_filter parameter was invalid");
|
||||||
}
|
}
|
||||||
|
|
@ -1170,6 +1295,15 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
has_filtered_address = true;
|
has_filtered_address = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string strasset = "";
|
||||||
|
if (params.size() > 4 && params[4].isStr()) {
|
||||||
|
strasset = params[4].get_str();
|
||||||
|
}
|
||||||
|
CAsset asset;
|
||||||
|
if (!strasset.empty()) {
|
||||||
|
asset = GetAssetFromString(strasset);
|
||||||
|
}
|
||||||
|
|
||||||
// Tally
|
// Tally
|
||||||
std::map<CTxDestination, tallyitem> mapTally;
|
std::map<CTxDestination, tallyitem> mapTally;
|
||||||
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
|
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
|
||||||
|
|
@ -1182,8 +1316,10 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
if (nDepth < nMinDepth)
|
if (nDepth < nMinDepth)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const CTxOut& txout : wtx.tx->vout)
|
for (size_t index = 0; index < wtx.tx->vout.size(); ++index)
|
||||||
{
|
{
|
||||||
|
const CTxOut& txout = wtx.tx->vout[index];
|
||||||
|
|
||||||
CTxDestination address;
|
CTxDestination address;
|
||||||
if (!ExtractDestination(txout.scriptPubKey, address))
|
if (!ExtractDestination(txout.scriptPubKey, address))
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1196,8 +1332,17 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
if(!(mine & filter))
|
if(!(mine & filter))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
CAmount amt = wtx.GetOutputValueOut(index);
|
||||||
|
if (amt < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strasset != "" && wtx.GetOutputAsset(index) != asset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
tallyitem& item = mapTally[address];
|
tallyitem& item = mapTally[address];
|
||||||
item.nAmount += txout.nValue;
|
item.mapAmount[wtx.GetOutputAsset(index)] += amt;
|
||||||
item.nConf = std::min(item.nConf, nDepth);
|
item.nConf = std::min(item.nConf, nDepth);
|
||||||
item.txids.push_back(wtx.GetHash());
|
item.txids.push_back(wtx.GetHash());
|
||||||
if (mine & ISMINE_WATCH_ONLY)
|
if (mine & ISMINE_WATCH_ONLY)
|
||||||
|
|
@ -1229,12 +1374,12 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
if (it == mapTally.end() && !fIncludeEmpty)
|
if (it == mapTally.end() && !fIncludeEmpty)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CAmount nAmount = 0;
|
CAmountMap mapAmount;
|
||||||
int nConf = std::numeric_limits<int>::max();
|
int nConf = std::numeric_limits<int>::max();
|
||||||
bool fIsWatchonly = false;
|
bool fIsWatchonly = false;
|
||||||
if (it != mapTally.end())
|
if (it != mapTally.end())
|
||||||
{
|
{
|
||||||
nAmount = (*it).second.nAmount;
|
mapAmount = (*it).second.mapAmount;
|
||||||
nConf = (*it).second.nConf;
|
nConf = (*it).second.nConf;
|
||||||
fIsWatchonly = (*it).second.fIsWatchonly;
|
fIsWatchonly = (*it).second.fIsWatchonly;
|
||||||
}
|
}
|
||||||
|
|
@ -1242,7 +1387,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
if (by_label)
|
if (by_label)
|
||||||
{
|
{
|
||||||
tallyitem& _item = label_tally[label];
|
tallyitem& _item = label_tally[label];
|
||||||
_item.nAmount += nAmount;
|
_item.mapAmount += mapAmount;
|
||||||
_item.nConf = std::min(_item.nConf, nConf);
|
_item.nConf = std::min(_item.nConf, nConf);
|
||||||
_item.fIsWatchonly = fIsWatchonly;
|
_item.fIsWatchonly = fIsWatchonly;
|
||||||
}
|
}
|
||||||
|
|
@ -1252,7 +1397,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
if(fIsWatchonly)
|
if(fIsWatchonly)
|
||||||
obj.pushKV("involvesWatchonly", true);
|
obj.pushKV("involvesWatchonly", true);
|
||||||
obj.pushKV("address", EncodeDestination(address));
|
obj.pushKV("address", EncodeDestination(address));
|
||||||
obj.pushKV("amount", ValueFromAmount(nAmount));
|
obj.pushKV("amount", AmountMapToUniv(mapAmount, ""));
|
||||||
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
||||||
obj.pushKV("label", label);
|
obj.pushKV("label", label);
|
||||||
UniValue transactions(UniValue::VARR);
|
UniValue transactions(UniValue::VARR);
|
||||||
|
|
@ -1272,12 +1417,12 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
||||||
{
|
{
|
||||||
for (const auto& entry : label_tally)
|
for (const auto& entry : label_tally)
|
||||||
{
|
{
|
||||||
CAmount nAmount = entry.second.nAmount;
|
CAmountMap mapAmount = entry.second.mapAmount;
|
||||||
int nConf = entry.second.nConf;
|
int nConf = entry.second.nConf;
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
if (entry.second.fIsWatchonly)
|
if (entry.second.fIsWatchonly)
|
||||||
obj.pushKV("involvesWatchonly", true);
|
obj.pushKV("involvesWatchonly", true);
|
||||||
obj.pushKV("amount", ValueFromAmount(nAmount));
|
obj.pushKV("amount", AmountMapToUniv(mapAmount, ""));
|
||||||
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
||||||
obj.pushKV("label", entry.first);
|
obj.pushKV("label", entry.first);
|
||||||
ret.push_back(obj);
|
ret.push_back(obj);
|
||||||
|
|
@ -1296,7 +1441,7 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.fHelp || request.params.size() > 4)
|
if (request.fHelp || request.params.size() > 5)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"listreceivedbyaddress ( minconf include_empty include_watchonly address_filter )\n"
|
"listreceivedbyaddress ( minconf include_empty include_watchonly address_filter )\n"
|
||||||
"\nList balances by receiving address.\n"
|
"\nList balances by receiving address.\n"
|
||||||
|
|
@ -1305,6 +1450,7 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
|
||||||
"2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n"
|
"2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n"
|
||||||
"3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
|
"3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
|
||||||
"4. address_filter (string, optional) If present, only return information on this address.\n"
|
"4. address_filter (string, optional) If present, only return information on this address.\n"
|
||||||
|
"5. assetlabel (string, optional) The hex asset id or asset label to filter for.\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"[\n"
|
"[\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
|
|
@ -1420,6 +1566,11 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
|
||||||
MaybePushAddress(entry, s.destination);
|
MaybePushAddress(entry, s.destination);
|
||||||
entry.pushKV("category", "send");
|
entry.pushKV("category", "send");
|
||||||
entry.pushKV("amount", ValueFromAmount(-s.amount));
|
entry.pushKV("amount", ValueFromAmount(-s.amount));
|
||||||
|
if (g_con_elementsmode) {
|
||||||
|
entry.pushKV("amountblinder", s.amount_blinding_factor.GetHex());
|
||||||
|
entry.pushKV("asset", s.asset.GetHex());
|
||||||
|
entry.pushKV("assetblinder", s.asset_blinding_factor.GetHex());
|
||||||
|
}
|
||||||
if (pwallet->mapAddressBook.count(s.destination)) {
|
if (pwallet->mapAddressBook.count(s.destination)) {
|
||||||
entry.pushKV("label", pwallet->mapAddressBook[s.destination].name);
|
entry.pushKV("label", pwallet->mapAddressBook[s.destination].name);
|
||||||
}
|
}
|
||||||
|
|
@ -1460,6 +1611,11 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
|
||||||
entry.pushKV("category", "receive");
|
entry.pushKV("category", "receive");
|
||||||
}
|
}
|
||||||
entry.pushKV("amount", ValueFromAmount(r.amount));
|
entry.pushKV("amount", ValueFromAmount(r.amount));
|
||||||
|
if (g_con_elementsmode) {
|
||||||
|
entry.pushKV("amountblinder", r.amount_blinding_factor.GetHex());
|
||||||
|
entry.pushKV("asset", r.asset.GetHex());
|
||||||
|
entry.pushKV("assetblinder", r.asset_blinding_factor.GetHex());
|
||||||
|
}
|
||||||
if (pwallet->mapAddressBook.count(r.destination)) {
|
if (pwallet->mapAddressBook.count(r.destination)) {
|
||||||
entry.pushKV("label", label);
|
entry.pushKV("label", label);
|
||||||
}
|
}
|
||||||
|
|
@ -1805,14 +1961,23 @@ static UniValue gettransaction(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
const CWalletTx& wtx = it->second;
|
const CWalletTx& wtx = it->second;
|
||||||
|
|
||||||
CAmount nCredit = wtx.GetCredit(filter);
|
CAmountMap nCredit = wtx.GetCredit(filter);
|
||||||
CAmount nDebit = wtx.GetDebit(filter);
|
CAmountMap nDebit = wtx.GetDebit(filter);
|
||||||
CAmount nNet = nCredit - nDebit;
|
CAmountMap nNet = nCredit - nDebit;
|
||||||
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
|
assert(HasValidFee(*wtx.tx));
|
||||||
|
CAmountMap nFee = wtx.IsFromMe(filter) ? CAmountMap() - GetFeeMap(*wtx.tx) : CAmountMap();
|
||||||
|
if (!g_con_elementsmode) {
|
||||||
|
CAmount total_out = 0;
|
||||||
|
for (const auto& output : wtx.tx->vout) {
|
||||||
|
total_out += output.nValue.GetAmount();
|
||||||
|
}
|
||||||
|
nFee = CAmountMap();
|
||||||
|
nFee[::policyAsset] = wtx.IsFromMe(filter) ? total_out - nDebit[::policyAsset] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
entry.pushKV("amount", ValueFromAmount(nNet - nFee));
|
entry.pushKV("amount", AmountMapToUniv(nNet - nFee, ""));
|
||||||
if (wtx.IsFromMe(filter))
|
if (wtx.IsFromMe(filter))
|
||||||
entry.pushKV("fee", ValueFromAmount(nFee));
|
entry.pushKV("fee", AmountMapToUniv(nFee, ""));
|
||||||
|
|
||||||
WalletTxToJSON(wtx, entry);
|
WalletTxToJSON(wtx, entry);
|
||||||
|
|
||||||
|
|
@ -2450,9 +2615,9 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
||||||
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
|
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
|
||||||
obj.pushKV("walletname", pwallet->GetName());
|
obj.pushKV("walletname", pwallet->GetName());
|
||||||
obj.pushKV("walletversion", pwallet->GetVersion());
|
obj.pushKV("walletversion", pwallet->GetVersion());
|
||||||
obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance()));
|
obj.pushKV("balance", AmountMapToUniv(pwallet->GetBalance(), ""));
|
||||||
obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()));
|
obj.pushKV("unconfirmed_balance", AmountMapToUniv(pwallet->GetUnconfirmedBalance(), ""));
|
||||||
obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()));
|
obj.pushKV("immature_balance", AmountMapToUniv(pwallet->GetImmatureBalance(), ""));
|
||||||
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
|
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
|
||||||
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
|
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
|
||||||
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
|
obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
|
||||||
|
|
@ -2727,6 +2892,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||||
" \"maximumAmount\" (numeric or string, default=unlimited) Maximum value of each UTXO in " + CURRENCY_UNIT + "\n"
|
" \"maximumAmount\" (numeric or string, default=unlimited) Maximum value of each UTXO in " + CURRENCY_UNIT + "\n"
|
||||||
" \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n"
|
" \"maximumCount\" (numeric or string, default=unlimited) Maximum number of UTXOs\n"
|
||||||
" \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n"
|
" \"minimumSumAmount\" (numeric or string, default=unlimited) Minimum sum value of all UTXOs in " + CURRENCY_UNIT + "\n"
|
||||||
|
" \"asset\" (string, default="") Asset to filter outputs for.\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"\nResult\n"
|
"\nResult\n"
|
||||||
"[ (array of json object)\n"
|
"[ (array of json object)\n"
|
||||||
|
|
@ -2794,6 +2960,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||||
CAmount nMaximumAmount = MAX_MONEY;
|
CAmount nMaximumAmount = MAX_MONEY;
|
||||||
CAmount nMinimumSumAmount = MAX_MONEY;
|
CAmount nMinimumSumAmount = MAX_MONEY;
|
||||||
uint64_t nMaximumCount = 0;
|
uint64_t nMaximumCount = 0;
|
||||||
|
std::string asset_str;
|
||||||
|
|
||||||
if (!request.params[4].isNull()) {
|
if (!request.params[4].isNull()) {
|
||||||
const UniValue& options = request.params[4].get_obj();
|
const UniValue& options = request.params[4].get_obj();
|
||||||
|
|
@ -2809,6 +2976,14 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||||
|
|
||||||
if (options.exists("maximumCount"))
|
if (options.exists("maximumCount"))
|
||||||
nMaximumCount = options["maximumCount"].get_int64();
|
nMaximumCount = options["maximumCount"].get_int64();
|
||||||
|
|
||||||
|
if (options.exists("asset"))
|
||||||
|
asset_str = options["asset"].get_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
CAsset asset_filter;
|
||||||
|
if (!asset_str.empty()) {
|
||||||
|
asset_filter = GetAssetFromString(asset_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the results are valid at least up to the most recent block
|
// Make sure the results are valid at least up to the most recent block
|
||||||
|
|
@ -2819,19 +2994,33 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||||
std::vector<COutput> vecOutputs;
|
std::vector<COutput> vecOutputs;
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, pwallet->cs_wallet);
|
LOCK2(cs_main, pwallet->cs_wallet);
|
||||||
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
|
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth, asset_filter.IsNull() ? nullptr : &asset_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCK(pwallet->cs_wallet);
|
LOCK(pwallet->cs_wallet);
|
||||||
|
|
||||||
for (const COutput& out : vecOutputs) {
|
for (const COutput& out : vecOutputs) {
|
||||||
CTxDestination address;
|
CTxDestination address;
|
||||||
|
const CTxOut& tx_out = out.tx->tx->vout[out.i];
|
||||||
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
|
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
|
||||||
bool fValidAddress = ExtractDestination(scriptPubKey, address);
|
bool fValidAddress = ExtractDestination(scriptPubKey, address);
|
||||||
|
|
||||||
if (destinations.size() && (!fValidAddress || !destinations.count(address)))
|
if (destinations.size() && (!fValidAddress || !destinations.count(address)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Elements
|
||||||
|
CAmount amount = out.tx->GetOutputValueOut(out.i);
|
||||||
|
CAsset assetid = out.tx->GetOutputAsset(out.i);
|
||||||
|
// Only list known outputs that match optional filter
|
||||||
|
if (g_con_elementsmode && (amount < 0 || assetid.IsNull())) {
|
||||||
|
LogPrintf("wallet", "Unable to unblind output: %s:%d\n", out.tx->tx->GetHash().GetHex(), out.i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!asset_str.empty() && asset_filter != assetid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//////////
|
||||||
|
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
entry.pushKV("txid", out.tx->GetHash().GetHex());
|
entry.pushKV("txid", out.tx->GetHash().GetHex());
|
||||||
entry.pushKV("vout", out.i);
|
entry.pushKV("vout", out.i);
|
||||||
|
|
@ -2854,7 +3043,18 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
||||||
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
|
entry.pushKV("amount", ValueFromAmount(amount));
|
||||||
|
if (g_con_elementsmode) {
|
||||||
|
if (tx_out.nAsset.IsCommitment()) {
|
||||||
|
entry.pushKV("assetcommitment", HexStr(tx_out.nAsset.vchCommitment));
|
||||||
|
}
|
||||||
|
entry.pushKV("asset", assetid.GetHex());
|
||||||
|
if (tx_out.nValue.IsCommitment()) {
|
||||||
|
entry.pushKV("amountcommitment", HexStr(tx_out.nValue.vchCommitment));
|
||||||
|
}
|
||||||
|
entry.pushKV("amountblinder", out.tx->GetOutputAmountBlindingFactor(out.i).ToString());
|
||||||
|
entry.pushKV("assetblinder", out.tx->GetOutputAssetBlindingFactor(out.i).ToString());
|
||||||
|
}
|
||||||
entry.pushKV("confirmations", out.nDepth);
|
entry.pushKV("confirmations", out.nDepth);
|
||||||
entry.pushKV("spendable", out.fSpendable);
|
entry.pushKV("spendable", out.fSpendable);
|
||||||
entry.pushKV("solvable", out.fSolvable);
|
entry.pushKV("solvable", out.fSolvable);
|
||||||
|
|
@ -2884,9 +3084,10 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
RPCTypeCheckArgument(options, UniValue::VOBJ);
|
RPCTypeCheckArgument(options, UniValue::VOBJ);
|
||||||
|
|
||||||
RPCTypeCheckObj(options,
|
RPCTypeCheckObj(options,
|
||||||
{
|
{
|
||||||
{"changeAddress", UniValueType(UniValue::VSTR)},
|
{"changeAddress", UniValueType()}, // will be checked below
|
||||||
{"changePosition", UniValueType(UniValue::VNUM)},
|
{"changePosition", UniValueType(UniValue::VNUM)},
|
||||||
{"change_type", UniValueType(UniValue::VSTR)},
|
{"change_type", UniValueType(UniValue::VSTR)},
|
||||||
{"includeWatching", UniValueType(UniValue::VBOOL)},
|
{"includeWatching", UniValueType(UniValue::VBOOL)},
|
||||||
|
|
@ -2900,13 +3101,38 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
|
||||||
true, true);
|
true, true);
|
||||||
|
|
||||||
if (options.exists("changeAddress")) {
|
if (options.exists("changeAddress")) {
|
||||||
CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
|
std::map<CAsset, CTxDestination> destinations;
|
||||||
|
|
||||||
if (!IsValidDestination(dest)) {
|
if (options["changeAddress"].isStr()) {
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
|
// Single destination for default asset (policyAsset).
|
||||||
|
CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
|
||||||
|
if (!IsValidDestination(dest)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
|
||||||
|
}
|
||||||
|
destinations[::policyAsset] = dest;
|
||||||
|
} else if (options["changeAddress"].isObject()) {
|
||||||
|
// Map of assets to destinations.
|
||||||
|
std::map<std::string, UniValue> kvMap;
|
||||||
|
options["changeAddress"].getObjMap(kvMap);
|
||||||
|
|
||||||
|
for (const std::pair<std::string, UniValue>& kv : kvMap) {
|
||||||
|
CAsset asset = GetAssetFromString(kv.first);
|
||||||
|
if (asset.IsNull()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "changeAddress key must be a valid asset label or hex");
|
||||||
|
}
|
||||||
|
|
||||||
|
CTxDestination dest = DecodeDestination(kv.second.get_str());
|
||||||
|
if (!IsValidDestination(dest)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
|
||||||
|
}
|
||||||
|
|
||||||
|
destinations[asset] = dest;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "changeAddress must be either a map or a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
coinControl.destChange = dest;
|
coinControl.destChange = destinations;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.exists("changePosition"))
|
if (options.exists("changePosition"))
|
||||||
|
|
@ -3007,7 +3233,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
|
||||||
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
|
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
|
||||||
"2. options (object, optional)\n"
|
"2. options (object, optional)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
|
" \"changeAddress\" (string/object, optional, default pool address) The bitcoin address to receive the change or a map from asset to address\n"
|
||||||
" \"changePosition\" (numeric, optional, default random) The index of the change output\n"
|
" \"changePosition\" (numeric, optional, default random) The index of the change output\n"
|
||||||
" \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
|
" \"change_type\" (string, optional) The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
|
||||||
" \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
|
" \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
|
||||||
|
|
@ -3096,7 +3322,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
|
||||||
" \"vout\":n, (numeric, required) The output number\n"
|
" \"vout\":n, (numeric, required) The output number\n"
|
||||||
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
|
" \"scriptPubKey\": \"hex\", (string, required) script key\n"
|
||||||
" \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
|
" \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n"
|
||||||
" \"amount\": value (numeric, required) The amount spent\n"
|
" \"amount\": value (numeric, required if non-confidential segwit output) The amount spent\n"
|
||||||
|
" \"amountcommitment\": \"hex\", (string, required if confidential segiwt output) The amount commitment spent\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" ,...\n"
|
" ,...\n"
|
||||||
" ]\n"
|
" ]\n"
|
||||||
|
|
@ -3610,6 +3837,83 @@ static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& de
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DescribeWalletBlindAddressVisitor : public boost::static_visitor<UniValue>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CWallet * const pwallet;
|
||||||
|
isminetype mine;
|
||||||
|
|
||||||
|
explicit DescribeWalletBlindAddressVisitor(CWallet* _pwallet, isminetype mine_in) : pwallet(_pwallet), mine(mine_in) {}
|
||||||
|
|
||||||
|
UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); }
|
||||||
|
|
||||||
|
UniValue operator()(const PKHash& pkhash) const
|
||||||
|
{
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
if (!IsBlindDestination(pkhash) && mine != ISMINE_NO) {
|
||||||
|
CPubKey blind_pub = pwallet->GetBlindingPubKey(GetScriptForDestination(pkhash));
|
||||||
|
PKHash dest(pkhash);
|
||||||
|
dest.blinding_pubkey = blind_pub;
|
||||||
|
obj.pushKV("confidential", EncodeDestination(dest));
|
||||||
|
} else {
|
||||||
|
obj.pushKV("confidential", EncodeDestination(pkhash));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue operator()(const ScriptHash& scripthash) const
|
||||||
|
{
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
if (!IsBlindDestination(scripthash) && mine != ISMINE_NO) {
|
||||||
|
CPubKey blind_pub = pwallet->GetBlindingPubKey(GetScriptForDestination(scripthash));
|
||||||
|
ScriptHash dest(scripthash);
|
||||||
|
dest.blinding_pubkey = blind_pub;
|
||||||
|
obj.pushKV("confidential", EncodeDestination(dest));
|
||||||
|
} else {
|
||||||
|
obj.pushKV("confidential", EncodeDestination(scripthash));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue operator()(const WitnessV0KeyHash& id) const
|
||||||
|
{
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
if (!IsBlindDestination(id) && mine != ISMINE_NO) {
|
||||||
|
CPubKey blind_pub = pwallet->GetBlindingPubKey(GetScriptForDestination(id));
|
||||||
|
WitnessV0KeyHash dest(id);
|
||||||
|
dest.blinding_pubkey = blind_pub;
|
||||||
|
obj.pushKV("confidential", EncodeDestination(dest));
|
||||||
|
} else {
|
||||||
|
obj.pushKV("confidential", EncodeDestination(id));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue operator()(const WitnessV0ScriptHash& id) const
|
||||||
|
{
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
if (!IsBlindDestination(id) && mine != ISMINE_NO) {
|
||||||
|
CPubKey blind_pub = pwallet->GetBlindingPubKey(GetScriptForDestination(id));
|
||||||
|
WitnessV0ScriptHash dest(id);
|
||||||
|
dest.blinding_pubkey = blind_pub;
|
||||||
|
obj.pushKV("confidential", EncodeDestination(dest));
|
||||||
|
} else {
|
||||||
|
obj.pushKV("confidential", EncodeDestination(id));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
|
||||||
|
UniValue operator()(const NullData& id) const { return NullUniValue; }
|
||||||
|
};
|
||||||
|
|
||||||
|
static UniValue DescribeWalletBlindAddress(CWallet* pwallet, const CTxDestination& dest, isminetype mine)
|
||||||
|
{
|
||||||
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
ret.pushKVs(boost::apply_visitor(DescribeWalletBlindAddressVisitor(pwallet, mine), dest));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/** Convert CAddressBookData to JSON record. */
|
/** Convert CAddressBookData to JSON record. */
|
||||||
static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool verbose)
|
static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool verbose)
|
||||||
{
|
{
|
||||||
|
|
@ -3658,6 +3962,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
|
||||||
" \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
|
" \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
|
||||||
" \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\").\n"
|
" \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\").\n"
|
||||||
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
|
||||||
|
" \"confidential_key\" : \"hex\", (string) The hex value of the raw blinding public key for that address, if any. \"\" if none.\n"
|
||||||
|
" \"unconfidential\" : \"address\", (string) The address without confidentiality key.\n"
|
||||||
|
" \"confidential\" : \"address\", (string) The address with wallet-stored confidentiality key if known. Only displayed for non-confidential address inputs.\n"
|
||||||
" \"label\" : \"label\" (string) The label associated with the address, \"\" is the default label\n"
|
" \"label\" : \"label\" (string) The label associated with the address, \"\" is the default label\n"
|
||||||
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
|
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
|
||||||
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
|
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
|
||||||
|
|
@ -3694,10 +4001,20 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
|
||||||
ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
||||||
|
|
||||||
isminetype mine = IsMine(*pwallet, dest);
|
isminetype mine = IsMine(*pwallet, dest);
|
||||||
|
// Elements: Addresses we can not unblind outputs for aren't spendable
|
||||||
|
if (IsBlindDestination(dest) &&
|
||||||
|
GetDestinationBlindingKey(dest) != pwallet->GetBlindingPubKey(GetScriptForDestination(dest))) {
|
||||||
|
mine = ISMINE_NO;
|
||||||
|
}
|
||||||
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
|
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
|
||||||
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
|
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
|
||||||
UniValue detail = DescribeWalletAddress(pwallet, dest);
|
UniValue detail = DescribeWalletAddress(pwallet, dest);
|
||||||
ret.pushKVs(detail);
|
ret.pushKVs(detail);
|
||||||
|
// Elements blinding info
|
||||||
|
UniValue blind_detail = DescribeWalletBlindAddress(pwallet, dest, mine);
|
||||||
|
ret.pushKVs(blind_detail);
|
||||||
|
blind_detail = DescribeBlindAddress(dest);
|
||||||
|
ret.pushKVs(blind_detail);
|
||||||
if (pwallet->mapAddressBook.count(dest)) {
|
if (pwallet->mapAddressBook.count(dest)) {
|
||||||
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
|
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
|
||||||
}
|
}
|
||||||
|
|
@ -3957,7 +4274,7 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C
|
||||||
SignatureData sigdata;
|
SignatureData sigdata;
|
||||||
psbt_out.FillSignatureData(sigdata);
|
psbt_out.FillSignatureData(sigdata);
|
||||||
|
|
||||||
MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1);
|
MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue.GetAmount(), 1);
|
||||||
ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata);
|
ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata);
|
||||||
psbt_out.FromSignatureData(sigdata);
|
psbt_out.FromSignatureData(sigdata);
|
||||||
}
|
}
|
||||||
|
|
@ -4115,7 +4432,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
|
||||||
|
|
||||||
CAmount fee;
|
CAmount fee;
|
||||||
int change_position;
|
int change_position;
|
||||||
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"]);
|
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3]["replaceable"], NullUniValue /* CA: assets_in */);
|
||||||
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
|
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
|
||||||
|
|
||||||
// Make a blank psbt
|
// Make a blank psbt
|
||||||
|
|
@ -4190,8 +4507,6 @@ UniValue getpeginaddress(const JSONRPCRequest& request)
|
||||||
|
|
||||||
//Creates new address for receiving unlocked utxos
|
//Creates new address for receiving unlocked utxos
|
||||||
JSONRPCRequest req;
|
JSONRPCRequest req;
|
||||||
//TODO(rebase) replace with CT/CA
|
|
||||||
//CTxDestination dest(DecodeDestination(getnewaddress(req).get_str()).GetUnblinded());
|
|
||||||
CTxDestination address = DecodeDestination(getnewaddress(req).get_str());
|
CTxDestination address = DecodeDestination(getnewaddress(req).get_str());
|
||||||
|
|
||||||
Witnessifier w(pwallet);
|
Witnessifier w(pwallet);
|
||||||
|
|
@ -4459,11 +4774,7 @@ UniValue sendtomainchain_base(const JSONRPCRequest& request)
|
||||||
|
|
||||||
mapValue_t mapValue;
|
mapValue_t mapValue;
|
||||||
CCoinControl no_coin_control; // This is a deprecated API
|
CCoinControl no_coin_control; // This is a deprecated API
|
||||||
CTransactionRef tx = SendMoney(pwallet, address, nAmount, subtract_fee, no_coin_control, std::move(mapValue));
|
CTransactionRef tx = SendMoney(pwallet, address, nAmount, Params().GetConsensus().pegged_asset, subtract_fee, no_coin_control, std::move(mapValue), true /* ignore_blind_fail */);
|
||||||
|
|
||||||
//TODO(rebase) CT/CA
|
|
||||||
// v this line was in elements-0.14 instead of the line above here
|
|
||||||
////SendMoney(scriptPubKey, nAmount, Params().GetConsensus().pegged_asset, subtract_fee, CPubKey(), wtxNew, true);
|
|
||||||
|
|
||||||
return (*tx).GetHash().GetHex();
|
return (*tx).GetHash().GetHex();
|
||||||
|
|
||||||
|
|
@ -4612,9 +4923,10 @@ UniValue sendtomainchain_pak(const JSONRPCRequest& request)
|
||||||
int whitelistindex=-1;
|
int whitelistindex=-1;
|
||||||
std::vector<secp256k1_pubkey> pak_online = paklist.OnlineKeys();
|
std::vector<secp256k1_pubkey> pak_online = paklist.OnlineKeys();
|
||||||
for (unsigned int i=0; i<pak_online.size(); i++) {
|
for (unsigned int i=0; i<pak_online.size(); i++) {
|
||||||
if (memcmp((void *)&pak_online[i], (void *)&onlinepubkey_secp, sizeof(secp256k1_pubkey)) == 0)
|
if (memcmp((void *)&pak_online[i], (void *)&onlinepubkey_secp, sizeof(secp256k1_pubkey)) == 0) {
|
||||||
whitelistindex = i;
|
whitelistindex = i;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (whitelistindex == -1)
|
if (whitelistindex == -1)
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Given online key is not in Pegout Authorization Key List");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Given online key is not in Pegout Authorization Key List");
|
||||||
|
|
@ -4704,7 +5016,7 @@ UniValue sendtomainchain_pak(const JSONRPCRequest& request)
|
||||||
|
|
||||||
mapValue_t mapValue;
|
mapValue_t mapValue;
|
||||||
CCoinControl no_coin_control; // This is a deprecated API
|
CCoinControl no_coin_control; // This is a deprecated API
|
||||||
CTransactionRef tx = SendMoney(pwallet, address, nAmount, subtract_fee, no_coin_control, std::move(mapValue));
|
CTransactionRef tx = SendMoney(pwallet, address, nAmount, Params().GetConsensus().pegged_asset, subtract_fee, no_coin_control, std::move(mapValue), true /* ignore_blind_fail */);
|
||||||
|
|
||||||
pwallet->SetOfflineCounter(counter+1);
|
pwallet->SetOfflineCounter(counter+1);
|
||||||
|
|
||||||
|
|
@ -4852,8 +5164,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
||||||
|
|
||||||
CAmount value = 0;
|
CAmount value = 0;
|
||||||
if (!GetAmountFromParentChainPegin(value, txBTC, nOut)) {
|
if (!GetAmountFromParentChainPegin(value, txBTC, nOut)) {
|
||||||
//TODO(rebase) CT/CA
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Amounts to pegin must be explicit and asset must be %s", Params().GetConsensus().parent_pegged_asset.GetHex()));
|
||||||
//throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Amounts to pegin must be explicit and asset must be %s", Params().GetConsensus().parent_pegged_asset.GetHex()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CDataStream stream(0, 0);
|
CDataStream stream(0, 0);
|
||||||
|
|
@ -4896,10 +5207,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
||||||
CScriptWitness pegin_witness;
|
CScriptWitness pegin_witness;
|
||||||
std::vector<std::vector<unsigned char> >& stack = pegin_witness.stack;
|
std::vector<std::vector<unsigned char> >& stack = pegin_witness.stack;
|
||||||
stack.push_back(value_bytes);
|
stack.push_back(value_bytes);
|
||||||
//TODO(rebase) CA
|
stack.push_back(std::vector<unsigned char>(Params().GetConsensus().pegged_asset.begin(), Params().GetConsensus().pegged_asset.end()));
|
||||||
std::vector<unsigned char> empty(32);
|
|
||||||
stack.push_back(empty);
|
|
||||||
//stack.push_back(std::vector<unsigned char>(Params().GetConsensus().pegged_asset.begin(), Params().GetConsensus().pegged_asset.end()));
|
|
||||||
stack.push_back(std::vector<unsigned char>(genesisBlockHash.begin(), genesisBlockHash.end()));
|
stack.push_back(std::vector<unsigned char>(genesisBlockHash.begin(), genesisBlockHash.end()));
|
||||||
stack.push_back(std::vector<unsigned char>(witnessProgScript.begin(), witnessProgScript.end()));
|
stack.push_back(std::vector<unsigned char>(witnessProgScript.begin(), witnessProgScript.end()));
|
||||||
stack.push_back(txData);
|
stack.push_back(txData);
|
||||||
|
|
@ -4923,10 +5231,8 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
||||||
CCoinControl coin_control;
|
CCoinControl coin_control;
|
||||||
CAmount nFeeNeeded = GetMinimumFee(*pwallet, nBytes, coin_control, mempool, ::feeEstimator, nullptr);
|
CAmount nFeeNeeded = GetMinimumFee(*pwallet, nBytes, coin_control, mempool, ::feeEstimator, nullptr);
|
||||||
|
|
||||||
//TODO(rebase) CT
|
mtx.vout[0].nValue = mtx.vout[0].nValue.GetAmount() - nFeeNeeded;
|
||||||
//mtx.vout[0].nValue = mtx.vout[0].nValue.GetAmount() - nFeeNeeded;
|
mtx.vout[1].nValue = mtx.vout[1].nValue.GetAmount() + nFeeNeeded;
|
||||||
//mtx.vout[1].nValue = mtx.vout[1].nValue.GetAmount() + nFeeNeeded;
|
|
||||||
mtx.vout[0].nValue = mtx.vout[0].nValue - nFeeNeeded;
|
|
||||||
|
|
||||||
UniValue ret(UniValue::VOBJ);
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
|
||||||
|
|
@ -5018,8 +5324,9 @@ UniValue claimpegin(const JSONRPCRequest& request)
|
||||||
// Send it
|
// Send it
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
mapValue_t mapValue;
|
mapValue_t mapValue;
|
||||||
CReserveKey reservekey(pwallet);
|
std::vector<std::unique_ptr<CReserveKey>> reservekeys;
|
||||||
if (!pwallet->CommitTransaction(MakeTransactionRef(mtx), mapValue, {} /* orderForm */, reservekey, g_connman.get(), state)) {
|
reservekeys.push_back(std::unique_ptr<CReserveKey>(new CReserveKey(pwallet)));
|
||||||
|
if (!pwallet->CommitTransaction(MakeTransactionRef(mtx), mapValue, {} /* orderForm */, reservekeys, g_connman.get(), state)) {
|
||||||
std::string strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
|
std::string strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,6 +7,7 @@
|
||||||
#define BITCOIN_WALLET_WALLET_H
|
#define BITCOIN_WALLET_WALLET_H
|
||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
|
#include <asset.h>
|
||||||
#include <outputtype.h>
|
#include <outputtype.h>
|
||||||
#include <policy/feerate.h>
|
#include <policy/feerate.h>
|
||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
|
|
@ -173,6 +174,8 @@ struct CRecipient
|
||||||
{
|
{
|
||||||
CScript scriptPubKey;
|
CScript scriptPubKey;
|
||||||
CAmount nAmount;
|
CAmount nAmount;
|
||||||
|
CAsset asset;
|
||||||
|
CPubKey confidentiality_key;
|
||||||
bool fSubtractFeeFromAmount;
|
bool fSubtractFeeFromAmount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -202,6 +205,9 @@ struct COutputEntry
|
||||||
CTxDestination destination;
|
CTxDestination destination;
|
||||||
CAmount amount;
|
CAmount amount;
|
||||||
int vout;
|
int vout;
|
||||||
|
CAsset asset;
|
||||||
|
uint256 amount_blinding_factor;
|
||||||
|
uint256 asset_blinding_factor;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A transaction with a merkle branch linking it to the block chain. */
|
/** A transaction with a merkle branch linking it to the block chain. */
|
||||||
|
|
@ -320,7 +326,7 @@ public:
|
||||||
* "spent" - serialized vfSpent value that existed prior to
|
* "spent" - serialized vfSpent value that existed prior to
|
||||||
* 2014 (removed in commit 93a18a3)
|
* 2014 (removed in commit 93a18a3)
|
||||||
*/
|
*/
|
||||||
mapValue_t mapValue;
|
mutable mapValue_t mapValue;
|
||||||
std::vector<std::pair<std::string, std::string> > vOrderForm;
|
std::vector<std::pair<std::string, std::string> > vOrderForm;
|
||||||
unsigned int fTimeReceivedIsTxTime;
|
unsigned int fTimeReceivedIsTxTime;
|
||||||
unsigned int nTimeReceived; //!< time received by this node
|
unsigned int nTimeReceived; //!< time received by this node
|
||||||
|
|
@ -354,15 +360,15 @@ public:
|
||||||
mutable bool fAvailableWatchCreditCached;
|
mutable bool fAvailableWatchCreditCached;
|
||||||
mutable bool fChangeCached;
|
mutable bool fChangeCached;
|
||||||
mutable bool fInMempool;
|
mutable bool fInMempool;
|
||||||
mutable CAmount nDebitCached;
|
mutable CAmountMap nDebitCached;
|
||||||
mutable CAmount nCreditCached;
|
mutable CAmountMap nCreditCached;
|
||||||
mutable CAmount nImmatureCreditCached;
|
mutable CAmountMap nImmatureCreditCached;
|
||||||
mutable CAmount nAvailableCreditCached;
|
mutable CAmountMap nAvailableCreditCached;
|
||||||
mutable CAmount nWatchDebitCached;
|
mutable CAmountMap nWatchDebitCached;
|
||||||
mutable CAmount nWatchCreditCached;
|
mutable CAmountMap nWatchCreditCached;
|
||||||
mutable CAmount nImmatureWatchCreditCached;
|
mutable CAmountMap nImmatureWatchCreditCached;
|
||||||
mutable CAmount nAvailableWatchCreditCached;
|
mutable CAmountMap nAvailableWatchCreditCached;
|
||||||
mutable CAmount nChangeCached;
|
mutable CAmountMap nChangeCached;
|
||||||
|
|
||||||
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
|
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
|
||||||
{
|
{
|
||||||
|
|
@ -388,15 +394,15 @@ public:
|
||||||
fAvailableWatchCreditCached = false;
|
fAvailableWatchCreditCached = false;
|
||||||
fChangeCached = false;
|
fChangeCached = false;
|
||||||
fInMempool = false;
|
fInMempool = false;
|
||||||
nDebitCached = 0;
|
nDebitCached = CAmountMap();
|
||||||
nCreditCached = 0;
|
nCreditCached = CAmountMap();
|
||||||
nImmatureCreditCached = 0;
|
nImmatureCreditCached = CAmountMap();
|
||||||
nAvailableCreditCached = 0;
|
nAvailableCreditCached = CAmountMap();
|
||||||
nWatchDebitCached = 0;
|
nWatchDebitCached = CAmountMap();
|
||||||
nWatchCreditCached = 0;
|
nWatchCreditCached = CAmountMap();
|
||||||
nAvailableWatchCreditCached = 0;
|
nAvailableWatchCreditCached = CAmountMap();
|
||||||
nImmatureWatchCreditCached = 0;
|
nImmatureWatchCreditCached = CAmountMap();
|
||||||
nChangeCached = 0;
|
nChangeCached = CAmountMap();
|
||||||
nOrderPos = -1;
|
nOrderPos = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,6 +454,7 @@ public:
|
||||||
fImmatureWatchCreditCached = false;
|
fImmatureWatchCreditCached = false;
|
||||||
fDebitCached = false;
|
fDebitCached = false;
|
||||||
fChangeCached = false;
|
fChangeCached = false;
|
||||||
|
WipeUnknownBlindingData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindWallet(CWallet *pwalletIn)
|
void BindWallet(CWallet *pwalletIn)
|
||||||
|
|
@ -457,12 +464,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
//! filter decides which addresses will count towards the debit
|
//! filter decides which addresses will count towards the debit
|
||||||
CAmount GetDebit(const isminefilter& filter) const;
|
CAmountMap GetDebit(const isminefilter& filter) const;
|
||||||
CAmount GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
CAmountMap GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
CAmount GetImmatureCredit(bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
CAmountMap GetImmatureCredit(bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
CAmountMap GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
CAmountMap GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
CAmount GetChange() const;
|
CAmountMap GetChange() const;
|
||||||
|
|
||||||
// Get the marginal bytes if spending the specified output from this transaction
|
// Get the marginal bytes if spending the specified output from this transaction
|
||||||
int GetSpendSize(unsigned int out, bool use_max_sig = false) const
|
int GetSpendSize(unsigned int out, bool use_max_sig = false) const
|
||||||
|
|
@ -475,7 +482,7 @@ public:
|
||||||
|
|
||||||
bool IsFromMe(const isminefilter& filter) const
|
bool IsFromMe(const isminefilter& filter) const
|
||||||
{
|
{
|
||||||
return (GetDebit(filter) > 0);
|
return (GetDebit(filter) > CAmountMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// True if only scriptSigs are different
|
// True if only scriptSigs are different
|
||||||
|
|
@ -493,6 +500,48 @@ public:
|
||||||
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
std::set<uint256> GetConflicts() const;
|
std::set<uint256> GetConflicts() const;
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* Computes, stores and returns the unblinded info, or retrieves if already computed previously.
|
||||||
|
* @param[in] map_index - Where to store the blinding data. Issuance data is stored after the output data, with additional index offset calculated via GetPseudoInputOffset
|
||||||
|
* @param[in] vchRangeproof - The rangeproof to unwind
|
||||||
|
* @param[in] conf_value - The value to unblind
|
||||||
|
* @param[in] conf_asset - The asset to unblind
|
||||||
|
* @param[in] nonce - The nonce used to ECDH with the blinding key. This is null for issuance as blinding key is directly used as nonce
|
||||||
|
* @param[in] scriptPubKey - The script being committed to by the rangeproof
|
||||||
|
* @param[out] blinding_pubkey_out - Pointer to the recovered pubkey of the destination
|
||||||
|
* @param[out] value_out - Pointer to the CAmount where the unblinded amount will be stored
|
||||||
|
* @param[out] value_factor_out - Pointer to the recovered value blinding factor of the output
|
||||||
|
* @param[out] asset_out - Pointer to the recovered underlying asset type
|
||||||
|
* @param[out] asset_factor_out - Pointer to the recovered asset blinding factor of the output
|
||||||
|
*/
|
||||||
|
void GetBlindingData(const unsigned int map_index, const std::vector<unsigned char>& vchRangeproof, const CConfidentialValue& conf_value, const CConfidentialAsset& conf_asset, const CConfidentialNonce nonce, const CScript& scriptPubKey, CPubKey* blinding_pubkey_out, CAmount* value_out, uint256* value_factor_out, CAsset* asset_out, uint256* asset_factor_out) const;
|
||||||
|
void WipeUnknownBlindingData();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// For use in wallet transaction creation to remember 3rd party values
|
||||||
|
// Unneeded for issuance.
|
||||||
|
void SetBlindingData(const unsigned int output_index, const CPubKey& blinding_pubkey, const CAmount value, const uint256& value_factor, const CAsset& asset, const uint256& asset_factor);
|
||||||
|
|
||||||
|
//! Returns either the value out (if it is known) or -1
|
||||||
|
CAmount GetOutputValueOut(unsigned int ouput_index) const;
|
||||||
|
|
||||||
|
//! Returns either the blinding factor (if it is to us) or 0
|
||||||
|
uint256 GetOutputAmountBlindingFactor(unsigned int output_index) const;
|
||||||
|
uint256 GetOutputAssetBlindingFactor(unsigned int output_index) const;
|
||||||
|
//! Returns the underlying asset type, or 0 if unknown
|
||||||
|
CAsset GetOutputAsset(unsigned int output_index) const;
|
||||||
|
// ! Returns receiver's blinding pubkey
|
||||||
|
CPubKey GetOutputBlindingPubKey(unsigned int output_index) const;
|
||||||
|
//! Get the issuance blinder for either the asset itself or the issuing tokens
|
||||||
|
uint256 GetIssuanceBlindingFactor(unsigned int input_index, bool reissuance_token) const;
|
||||||
|
//! Get the issuance amount for either the asset itself or the issuing tokens
|
||||||
|
CAmount GetIssuanceAmount(unsigned int input_index, bool reissuance_token) const;
|
||||||
|
|
||||||
|
//! Get the mapValue offset for a specific vin index and type of issuance pseudo-input
|
||||||
|
unsigned int GetPseudoInputOffset(unsigned int input_index, bool reissuance_token) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class COutput
|
class COutput
|
||||||
|
|
@ -535,7 +584,7 @@ public:
|
||||||
|
|
||||||
inline CInputCoin GetInputCoin() const
|
inline CInputCoin GetInputCoin() const
|
||||||
{
|
{
|
||||||
return CInputCoin(tx->tx, i, nInputBytes);
|
return CInputCoin(tx, i, nInputBytes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -578,6 +627,40 @@ struct CoinSelectionParams
|
||||||
CoinSelectionParams() {}
|
CoinSelectionParams() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IssuanceDetails {
|
||||||
|
bool issuing = false;
|
||||||
|
|
||||||
|
bool blind_issuance = true;
|
||||||
|
CAsset reissuance_asset;
|
||||||
|
CAsset reissuance_token;
|
||||||
|
uint256 entropy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlindDetails {
|
||||||
|
bool ignore_blind_failure = true; // Certain corner-cases are hard to avoid
|
||||||
|
|
||||||
|
// Temporary tx-specific details.
|
||||||
|
std::vector<uint256> i_amount_blinds;
|
||||||
|
std::vector<uint256> i_asset_blinds;
|
||||||
|
std::vector<CAsset> i_assets;
|
||||||
|
std::vector<CAmount> i_amounts;
|
||||||
|
std::vector<CAmount> o_amounts;
|
||||||
|
std::vector<CPubKey> o_pubkeys;
|
||||||
|
std::vector<uint256> o_amount_blinds;
|
||||||
|
std::vector<CAsset> o_assets;
|
||||||
|
std::vector<uint256> o_asset_blinds;
|
||||||
|
// We need to store an unblinded and unsigned version of the transaction
|
||||||
|
// in case of !sign
|
||||||
|
CMutableTransaction tx_unblinded_unsigned;
|
||||||
|
|
||||||
|
int num_to_blind;
|
||||||
|
int change_to_blind;
|
||||||
|
// Only used to strip blinding if its the only blind output in certain situations
|
||||||
|
int only_recipient_blind_index;
|
||||||
|
// Needed in case of one blinded output that is change and no blind inputs
|
||||||
|
int only_change_pos;
|
||||||
|
};
|
||||||
|
|
||||||
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
|
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
|
||||||
/**
|
/**
|
||||||
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
|
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
|
||||||
|
|
@ -708,7 +791,7 @@ public:
|
||||||
* all coins from coinControl are selected; Never select unconfirmed coins
|
* all coins from coinControl are selected; Never select unconfirmed coins
|
||||||
* if they are not ours
|
* if they are not ours
|
||||||
*/
|
*/
|
||||||
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
|
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmountMap& mapTargetValue, std::set<CInputCoin>& setCoinsRet, CAmountMap& mapValueRet,
|
||||||
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
|
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
|
||||||
|
|
||||||
/** Get a name for this wallet for logging/debugging purposes.
|
/** Get a name for this wallet for logging/debugging purposes.
|
||||||
|
|
@ -765,6 +848,11 @@ public:
|
||||||
//The offline xpub aka `bitcoin_xpub` in the wallet set by `initpegoutwallet`
|
//The offline xpub aka `bitcoin_xpub` in the wallet set by `initpegoutwallet`
|
||||||
CExtPubKey offline_xpub;
|
CExtPubKey offline_xpub;
|
||||||
|
|
||||||
|
// Master derivation blinding key
|
||||||
|
uint256 blinding_derivation_key;
|
||||||
|
// Specifically imported blinding keys
|
||||||
|
std::map<CScriptID, uint256> mapSpecificBlindingKeys;
|
||||||
|
|
||||||
// END ELEMENTS
|
// END ELEMENTS
|
||||||
|
|
||||||
const CWalletTx* GetWalletTx(const uint256& hash) const;
|
const CWalletTx* GetWalletTx(const uint256& hash) const;
|
||||||
|
|
@ -775,7 +863,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* populate vCoins with vector of available COutputs.
|
* populate vCoins with vector of available COutputs.
|
||||||
*/
|
*/
|
||||||
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
|
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999, const CAsset* = nullptr) const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of available coins and locked coins grouped by non-change output address.
|
* Return list of available coins and locked coins grouped by non-change output address.
|
||||||
|
|
@ -793,8 +881,8 @@ public:
|
||||||
* completion the coin set and corresponding actual target value is
|
* completion the coin set and corresponding actual target value is
|
||||||
* assembled
|
* assembled
|
||||||
*/
|
*/
|
||||||
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
|
bool SelectCoinsMinConf(const CAmountMap& mapTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
|
||||||
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
|
std::set<CInputCoin>& setCoinsRet, CAmountMap& mapValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
|
||||||
|
|
||||||
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
|
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
|
||||||
|
|
@ -883,13 +971,13 @@ public:
|
||||||
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
|
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
|
||||||
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
|
CAmountMap GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
|
||||||
CAmount GetUnconfirmedBalance() const;
|
CAmountMap GetUnconfirmedBalance() const;
|
||||||
CAmount GetImmatureBalance() const;
|
CAmountMap GetImmatureBalance() const;
|
||||||
CAmount GetUnconfirmedWatchOnlyBalance() const;
|
CAmountMap GetUnconfirmedWatchOnlyBalance() const;
|
||||||
CAmount GetImmatureWatchOnlyBalance() const;
|
CAmountMap GetImmatureWatchOnlyBalance() const;
|
||||||
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const;
|
CAmountMap GetLegacyBalance(const isminefilter& filter, int minDepth) const;
|
||||||
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
|
CAmountMap GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
|
||||||
|
|
||||||
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
|
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);
|
||||||
|
|
||||||
|
|
@ -905,9 +993,9 @@ public:
|
||||||
* selected by SelectCoins(); Also create the change output, when needed
|
* selected by SelectCoins(); Also create the change output, when needed
|
||||||
* @note passing nChangePosInOut as -1 will result in setting a random position
|
* @note passing nChangePosInOut as -1 will result in setting a random position
|
||||||
*/
|
*/
|
||||||
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
|
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, std::vector<std::unique_ptr<CReserveKey>>& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
|
||||||
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
|
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true, BlindDetails* blind_details = nullptr, const IssuanceDetails* issuance_details = nullptr);
|
||||||
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
|
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::vector<std::unique_ptr<CReserveKey>>& reservekey, CConnman* connman, CValidationState& state, const BlindDetails* blind_details = nullptr);
|
||||||
|
|
||||||
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
|
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
|
||||||
{
|
{
|
||||||
|
|
@ -973,19 +1061,24 @@ public:
|
||||||
* Returns amount of debit if the input matches the
|
* Returns amount of debit if the input matches the
|
||||||
* filter, otherwise returns 0
|
* filter, otherwise returns 0
|
||||||
*/
|
*/
|
||||||
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
|
CAmountMap GetDebit(const CTxIn& txin, const isminefilter& filter) const;
|
||||||
isminetype IsMine(const CTxOut& txout) const;
|
isminetype IsMine(const CTxOut& txout) const;
|
||||||
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
|
CAmountMap GetCredit(const CTxOut& txout, const isminefilter& filter) const;
|
||||||
bool IsChange(const CTxOut& txout) const;
|
bool IsChange(const CTxOut& txout) const;
|
||||||
CAmount GetChange(const CTxOut& txout) const;
|
CAmountMap GetChange(const CTxOut& txout) const;
|
||||||
bool IsMine(const CTransaction& tx) const;
|
bool IsMine(const CTransaction& tx) const;
|
||||||
/** should probably be renamed to IsRelevantToMe */
|
/** should probably be renamed to IsRelevantToMe */
|
||||||
bool IsFromMe(const CTransaction& tx) const;
|
bool IsFromMe(const CTransaction& tx) const;
|
||||||
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
|
CAmountMap GetDebit(const CTransaction& tx, const isminefilter& filter) const;
|
||||||
/** Returns whether all of the inputs match the filter */
|
/** Returns whether all of the inputs match the filter */
|
||||||
bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
|
bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
|
||||||
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
|
CAmountMap GetCredit(const CTransaction& tx, const isminefilter& filter) const;
|
||||||
CAmount GetChange(const CTransaction& tx) const;
|
CAmountMap GetChange(const CTransaction& tx) const;
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
CAmountMap GetCredit(const CWalletTx& wtx, const isminefilter& filter) const;
|
||||||
|
CAmountMap GetChange(const CWalletTx& wtx) const;
|
||||||
|
|
||||||
void ChainStateFlushed(const CBlockLocator& loc) override;
|
void ChainStateFlushed(const CBlockLocator& loc) override;
|
||||||
|
|
||||||
DBErrors LoadWallet(bool& fFirstRunRet);
|
DBErrors LoadWallet(bool& fFirstRunRet);
|
||||||
|
|
@ -1149,6 +1242,20 @@ public:
|
||||||
bool SetOfflineCounter(int counter);
|
bool SetOfflineCounter(int counter);
|
||||||
bool SetOfflineDescriptor(const std::string& offline_desc_in);
|
bool SetOfflineDescriptor(const std::string& offline_desc_in);
|
||||||
bool SetOfflineXPubKey(const CExtPubKey& offline_xpub_in);
|
bool SetOfflineXPubKey(const CExtPubKey& offline_xpub_in);
|
||||||
|
|
||||||
|
void ComputeBlindingData(const CConfidentialValue& conf_value, const CConfidentialAsset& conf_asset, const CConfidentialNonce& nonce, const CScript& scriptPubKey, const std::vector<unsigned char>& vchRangeproof, CAmount& value, CPubKey& blinding_pubkey, uint256& value_factor, CAsset& asset, uint256& asset_factor) const;
|
||||||
|
|
||||||
|
// First looks in imported blinding key store, then derives on its own
|
||||||
|
CKey GetBlindingKey(const CScript* script) const;
|
||||||
|
// Pubkey accessor for GetBlindingKey
|
||||||
|
CPubKey GetBlindingPubKey(const CScript& script) const;
|
||||||
|
|
||||||
|
bool LoadSpecificBlindingKey(const CScriptID& scriptid, const uint256& key);
|
||||||
|
bool AddSpecificBlindingKey(const CScriptID& scriptid, const uint256& key);
|
||||||
|
bool SetMasterBlindingKey(const uint256& key);
|
||||||
|
|
||||||
|
/// Returns a map of entropy to the respective pair of reissuance token and issuance asset.
|
||||||
|
std::map<uint256, std::pair<CAsset, CAsset> > GetReissuanceTokenTypes() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A key allocated from the key pool. */
|
/** A key allocated from the key pool. */
|
||||||
|
|
@ -1170,6 +1277,18 @@ public:
|
||||||
CReserveKey() = default;
|
CReserveKey() = default;
|
||||||
CReserveKey(const CReserveKey&) = delete;
|
CReserveKey(const CReserveKey&) = delete;
|
||||||
CReserveKey& operator=(const CReserveKey&) = delete;
|
CReserveKey& operator=(const CReserveKey&) = delete;
|
||||||
|
// ELEMENTS:
|
||||||
|
CReserveKey& operator=(CReserveKey&&) = delete;
|
||||||
|
CReserveKey(CReserveKey&& in) {
|
||||||
|
// Copy fields over
|
||||||
|
pwallet = in.pwallet;
|
||||||
|
nIndex = in.nIndex;
|
||||||
|
vchPubKey = in.vchPubKey;
|
||||||
|
fInternal = in.fInternal;
|
||||||
|
// Invalidate the object being moved from
|
||||||
|
in.nIndex = -1;
|
||||||
|
in.vchPubKey = CPubKey();
|
||||||
|
}
|
||||||
|
|
||||||
~CReserveKey()
|
~CReserveKey()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,14 @@ bool WalletBatch::WriteOfflineCounter(int counter)
|
||||||
return WriteIC(std::string("offlinecounter"), counter);
|
return WriteIC(std::string("offlinecounter"), counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WalletBatch::WriteBlindingDerivationKey(const uint256& key) {
|
||||||
|
return WriteIC(std::string("blindingderivationkey"), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WalletBatch::WriteSpecificBlindingKey(const uint160& scriptid, const uint256& key) {
|
||||||
|
return WriteIC(std::make_pair(std::string("specificblindingkey"), scriptid), key);
|
||||||
|
}
|
||||||
|
|
||||||
class CWalletScanState {
|
class CWalletScanState {
|
||||||
public:
|
public:
|
||||||
unsigned int nKeys;
|
unsigned int nKeys;
|
||||||
|
|
@ -466,6 +474,24 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
||||||
std::string descriptor;
|
std::string descriptor;
|
||||||
ssValue >> descriptor;
|
ssValue >> descriptor;
|
||||||
pwallet->offline_desc = descriptor;
|
pwallet->offline_desc = descriptor;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
} else if (strType != "bestblock" && strType != "bestblock_nomerkle" &&
|
} else if (strType != "bestblock" && strType != "bestblock_nomerkle" &&
|
||||||
strType != "minversion" && strType != "acentry") {
|
strType != "minversion" && strType != "acentry") {
|
||||||
wss.m_unknown_records++;
|
wss.m_unknown_records++;
|
||||||
|
|
@ -555,6 +581,16 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
|
||||||
if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
|
if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
|
||||||
result = DBErrors::NONCRITICAL_ERROR;
|
result = DBErrors::NONCRITICAL_ERROR;
|
||||||
|
|
||||||
|
if (pwallet->blinding_derivation_key.IsNull()) {
|
||||||
|
CKey key;
|
||||||
|
key.MakeNewKey(true);
|
||||||
|
uint256 keybin;
|
||||||
|
memcpy(keybin.begin(), key.begin(), key.size());
|
||||||
|
if (!pwallet->SetMasterBlindingKey(keybin)) {
|
||||||
|
result = DBErrors::LOAD_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Any wallet corruption at all: skip any rewriting or
|
// Any wallet corruption at all: skip any rewriting or
|
||||||
// upgrading, we don't want to make it worse.
|
// upgrading, we don't want to make it worse.
|
||||||
if (result != DBErrors::LOAD_OK)
|
if (result != DBErrors::LOAD_OK)
|
||||||
|
|
|
||||||
|
|
@ -206,8 +206,9 @@ public:
|
||||||
bool WriteOnlineKey(const CPubKey& online_key);
|
bool WriteOnlineKey(const CPubKey& online_key);
|
||||||
bool WriteOfflineCounter(int counter);
|
bool WriteOfflineCounter(int counter);
|
||||||
bool WriteOfflineDescriptor(const std::string& offline_desc);
|
bool WriteOfflineDescriptor(const std::string& offline_desc);
|
||||||
// DEPRECATED
|
|
||||||
bool WriteOfflineXPubKey(const CExtPubKey& offline_xpub);
|
bool WriteOfflineXPubKey(const CExtPubKey& offline_xpub);
|
||||||
|
bool WriteBlindingDerivationKey(const uint256& key);
|
||||||
|
bool WriteSpecificBlindingKey(const uint160& scriptid, const uint256& key);
|
||||||
|
|
||||||
DBErrors LoadWallet(CWallet* pwallet);
|
DBErrors LoadWallet(CWallet* pwallet);
|
||||||
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
|
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue