mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +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
|
||||
{
|
||||
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; }
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ public:
|
|||
{
|
||||
LOCK2(cs_main, m_wallet.cs_wallet);
|
||||
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();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ public:
|
|||
|
||||
CTransactionRef m_tx;
|
||||
CWallet& m_wallet;
|
||||
CReserveKey m_key;
|
||||
std::vector<std::unique_ptr<CReserveKey>> m_keys;
|
||||
};
|
||||
|
||||
//! Construct wallet tx struct.
|
||||
|
|
@ -224,7 +224,7 @@ public:
|
|||
{
|
||||
LOCK2(cs_main, m_wallet.cs_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)) {
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,6 +174,9 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "rawblindrawtransaction", 3, "inputasset" },
|
||||
{ "rawblindrawtransaction", 4, "inputassetblinder" },
|
||||
{ "rawblindrawtransaction", 6, "ignoreblindfail" },
|
||||
{ "sendmany", 7 , "output_assets" },
|
||||
{ "sendmany", 8 , "ignoreblindfail" },
|
||||
{ "sendtoaddress", 9 , "ignoreblindfail" },
|
||||
{ "createrawtransaction", 4, "output_assets" },
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
void CCoinControl::SetNull()
|
||||
{
|
||||
destChange = CNoDestination();
|
||||
destChange.clear();
|
||||
m_change_type.reset();
|
||||
fAllowOtherInputs = false;
|
||||
fAllowWatchOnly = false;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef BITCOIN_WALLET_COINCONTROL_H
|
||||
#define BITCOIN_WALLET_COINCONTROL_H
|
||||
|
||||
#include <asset.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/fees.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
|
@ -17,7 +18,7 @@ class CCoinControl
|
|||
{
|
||||
public:
|
||||
//! 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
|
||||
boost::optional<OutputType> m_change_type;
|
||||
//! 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.
|
||||
|
||||
#include <wallet/coinselection.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
#include <util.h>
|
||||
#include <utilmoneystr.h>
|
||||
|
||||
#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
|
||||
struct {
|
||||
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)
|
||||
{
|
||||
setCoinsRet.clear();
|
||||
|
|
|
|||
|
|
@ -14,21 +14,14 @@ static const CAmount MIN_CHANGE = CENT;
|
|||
//! final minimum change amount after paying for fees
|
||||
static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;
|
||||
|
||||
class CWalletTx;
|
||||
class uint256;
|
||||
|
||||
class CInputCoin {
|
||||
public:
|
||||
CInputCoin(const CTransactionRef& tx, 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");
|
||||
CInputCoin(const CWalletTx* wtx, unsigned int i);
|
||||
|
||||
outpoint = COutPoint(tx->GetHash(), i);
|
||||
txout = tx->vout[i];
|
||||
effective_value = txout.nValue;
|
||||
}
|
||||
|
||||
CInputCoin(const CTransactionRef& tx, unsigned int i, int input_bytes) : CInputCoin(tx, i)
|
||||
CInputCoin(const CWalletTx* wtx, unsigned int i, int input_bytes) : CInputCoin(wtx, i)
|
||||
{
|
||||
m_input_bytes = input_bytes;
|
||||
}
|
||||
|
|
@ -36,6 +29,11 @@ public:
|
|||
COutPoint outpoint;
|
||||
CTxOut txout;
|
||||
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 */
|
||||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
int nOutput = -1;
|
||||
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 (nOutput != -1) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
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);
|
||||
if (maxNewTxSize < 0) {
|
||||
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
|
||||
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 nNewFeeRate;
|
||||
// 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);
|
||||
mtx = CMutableTransaction{*wtx.tx};
|
||||
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");
|
||||
return Result::WALLET_ERROR;
|
||||
}
|
||||
|
||||
// If the output would become dust, discard it (converting the dust to fee)
|
||||
poutput->nValue -= nDelta;
|
||||
if (poutput->nValue <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
|
||||
poutput->nValue = poutput->nValue.GetAmount() - nDelta;
|
||||
if (poutput->nValue.GetAmount() <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
|
||||
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);
|
||||
if (mtx.witness.vtxoutwit.size() > (size_t) 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.
|
||||
|
|
@ -242,10 +276,15 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
|
|||
CTransactionRef tx = MakeTransactionRef(std::move(mtx));
|
||||
mapValue_t mapValue = oldWtx.mapValue;
|
||||
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;
|
||||
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.
|
||||
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
|
||||
return Result::WALLET_ERROR;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <univalue.h>
|
||||
|
||||
#include <script/descriptor.h> // getwalletpakinfo
|
||||
#include <rpc/util.h> // IsBlindDestination
|
||||
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
// 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++) {
|
||||
const CKeyID &keyid = it->second;
|
||||
std::string strTime = FormatISO8601DateTime(it->first);
|
||||
|
|
@ -825,6 +830,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
|
|||
// Optional fields.
|
||||
const std::string& strRedeemScript = data.exists("redeemscript") ? data["redeemscript"].get_str() : "";
|
||||
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 bool internal = data.exists("internal") ? data["internal"].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. //
|
||||
|
||||
// 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
|
||||
if (isP2SH) {
|
||||
// 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);
|
||||
result.pushKV("success", UniValue(success));
|
||||
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"
|
||||
" \"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"
|
||||
" \"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"
|
||||
" \"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"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <amount.h>
|
||||
#include <asset.h>
|
||||
#include <assetsdir.h>
|
||||
#include <block_proof.h>
|
||||
#include <chain.h>
|
||||
#include <consensus/validation.h>
|
||||
|
|
@ -50,6 +52,8 @@
|
|||
#include <script/generic.hpp> // signblock
|
||||
#include <script/descriptor.h> // initpegoutwallet
|
||||
#include <span.h> // sendtomainchain_pak
|
||||
#include <blind.h>
|
||||
#include <issuance.h>
|
||||
|
||||
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);
|
||||
|
||||
for (const std::pair<const std::string, std::string>& item : wtx.mapValue)
|
||||
entry.pushKV(item.first, item.second);
|
||||
for (const std::pair<const std::string, std::string>& item : wtx.mapValue) {
|
||||
// Skip blinding data which isn't parseable
|
||||
if (item.first != "blindingdata") {
|
||||
entry.pushKV(item.first, item.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
"If 'label' is specified, it is added to the address book \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"
|
||||
"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"
|
||||
|
|
@ -203,7 +213,7 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
|
|||
}
|
||||
pwallet->LearnRelatedScripts(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));
|
||||
dest = GetDestinationForKey(newKey, output_type, blinding_pubkey);
|
||||
}
|
||||
|
|
@ -227,6 +237,8 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
|||
"getrawchangeaddress ( \"address_type\" )\n"
|
||||
"\nReturns a new Bitcoin address, for receiving change.\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"
|
||||
"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"
|
||||
|
|
@ -262,7 +274,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
|
|||
|
||||
pwallet->LearnRelatedScripts(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));
|
||||
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
|
||||
if (nValue <= 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
|
||||
|
||||
if (nValue > curBalance)
|
||||
if (nValue > curBalance[asset])
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
|
||||
|
||||
if (pwallet->GetBroadcastTransactions() && !g_connman) {
|
||||
|
|
@ -329,22 +341,32 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &
|
|||
// Parse Bitcoin 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
|
||||
CReserveKey reservekey(pwallet);
|
||||
CAmount nFeeRequired;
|
||||
std::string strError;
|
||||
std::vector<CRecipient> vecSend;
|
||||
int nChangePosRet = -1;
|
||||
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
|
||||
CRecipient recipient = {scriptPubKey, nValue, asset, GetDestinationBlindingKey(address), fSubtractFeeFromAmount};
|
||||
vecSend.push_back(recipient);
|
||||
CTransactionRef tx;
|
||||
if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coin_control)) {
|
||||
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance)
|
||||
BlindDetails* blind_details = g_con_elementsmode ? new BlindDetails() : NULL;
|
||||
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));
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||
}
|
||||
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));
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||
}
|
||||
|
|
@ -360,9 +382,9 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
|
|||
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(
|
||||
"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"
|
||||
+ HelpRequiringPassphrase(pwallet) +
|
||||
"\nArguments:\n"
|
||||
|
|
@ -381,6 +403,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
|
|||
" \"UNSET\"\n"
|
||||
" \"ECONOMICAL\"\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"
|
||||
"\"txid\" (string) The transaction id.\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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
@ -579,6 +618,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
|
|||
"\nArguments:\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"
|
||||
"3. \"assetlabel\" (string, optional) Hex asset id or asset label for balance.\n"
|
||||
"\nResult:\n"
|
||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
|
||||
"\nExamples:\n"
|
||||
|
|
@ -614,19 +654,34 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
|
|||
nMinDepth = request.params[1].get_int();
|
||||
|
||||
// Tally
|
||||
CAmount nAmount = 0;
|
||||
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
|
||||
const CWalletTx& wtx = pairWtx.second;
|
||||
CAmountMap amounts;
|
||||
for (auto& pairWtx : pwallet->mapWallet) {
|
||||
CWalletTx& wtx = pairWtx.second;
|
||||
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
||||
continue;
|
||||
|
||||
for (const CTxOut& txout : wtx.tx->vout)
|
||||
if (txout.scriptPubKey == scriptPubKey)
|
||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
nAmount += txout.nValue;
|
||||
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
|
||||
const CTxOut& txout = wtx.tx->vout[i];
|
||||
if (txout.scriptPubKey == scriptPubKey) {
|
||||
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"
|
||||
"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"
|
||||
"3. \"assetlabel\" (string, optional) Hex asset id or asset label for balance.\n"
|
||||
"\nResult:\n"
|
||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
|
||||
"\nExamples:\n"
|
||||
|
|
@ -675,23 +731,35 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
|
|||
std::set<CTxDestination> setAddress = pwallet->GetLabelAddresses(label);
|
||||
|
||||
// Tally
|
||||
CAmount nAmount = 0;
|
||||
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
|
||||
const CWalletTx& wtx = pairWtx.second;
|
||||
CAmountMap amounts;
|
||||
for (auto& pairWtx : pwallet->mapWallet) {
|
||||
CWalletTx& wtx = pairWtx.second;
|
||||
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
|
||||
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;
|
||||
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwallet, address) && setAddress.count(address)) {
|
||||
if (wtx.GetDepthInMainChain() >= nMinDepth)
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -704,7 +772,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
|
|||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || (request.params.size() > 3 ))
|
||||
if (request.fHelp || (request.params.size() > 4 ))
|
||||
throw std::runtime_error(
|
||||
"getbalance ( \"(dummy)\" minconf include_watchonly )\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"
|
||||
"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"
|
||||
"4. \"assetlabel\" (string, optional) Hex asset id or asset label for balance.\n"
|
||||
"\nResult:\n"
|
||||
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
|
||||
"\nExamples:\n"
|
||||
|
|
@ -746,7 +815,12 @@ static UniValue getbalance(const JSONRPCRequest& request)
|
|||
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)
|
||||
|
|
@ -769,7 +843,7 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
"sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
|
||||
"\nSend multiple times. Amounts are double-precision floating point numbers.\n"
|
||||
"sendmany \"\" \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n"
|
||||
"\nSend multiple times. Amounts are double-precision floating point numbers."
|
||||
+ HelpRequiringPassphrase(pwallet) + "\n"
|
||||
"\nArguments:\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"
|
||||
" \"ECONOMICAL\"\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"
|
||||
"\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \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\\\"]\"") +
|
||||
"\nAs a json rpc call\n"
|
||||
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
|
||||
);
|
||||
);
|
||||
|
||||
// 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
|
||||
|
|
@ -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::vector<CRecipient> vecSend;
|
||||
|
||||
CAmount totalAmount = 0;
|
||||
CAmountMap totalAmount;
|
||||
std::vector<std::string> keys = sendTo.getKeys();
|
||||
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_);
|
||||
if (!IsValidDestination(dest)) {
|
||||
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_]);
|
||||
if (nAmount <= 0)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
|
||||
totalAmount += nAmount;
|
||||
totalAmount[asset] += nAmount;
|
||||
|
||||
bool fSubtractFeeFromAmount = false;
|
||||
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
|
||||
|
|
@ -894,7 +996,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
|||
fSubtractFeeFromAmount = true;
|
||||
}
|
||||
|
||||
CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
|
||||
CRecipient recipient = {scriptPubKey, nAmount, asset, GetDestinationBlindingKey(dest), fSubtractFeeFromAmount};
|
||||
vecSend.push_back(recipient);
|
||||
}
|
||||
|
||||
|
|
@ -909,16 +1011,39 @@ static UniValue sendmany(const JSONRPCRequest& request)
|
|||
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
|
||||
|
||||
// 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;
|
||||
int nChangePosRet = -1;
|
||||
std::string strFailReason;
|
||||
CTransactionRef tx;
|
||||
bool fCreated = pwallet->CreateTransaction(vecSend, tx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
|
||||
if (!fCreated)
|
||||
BlindDetails* blind_details = g_con_elementsmode ? new BlindDetails() : NULL;
|
||||
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);
|
||||
}
|
||||
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));
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
|
||||
}
|
||||
|
|
@ -1131,13 +1256,13 @@ static UniValue addwitnessaddress(const JSONRPCRequest& request)
|
|||
|
||||
struct tallyitem
|
||||
{
|
||||
CAmount nAmount;
|
||||
CAmountMap mapAmount;
|
||||
int nConf;
|
||||
std::vector<uint256> txids;
|
||||
bool fIsWatchonly;
|
||||
tallyitem()
|
||||
{
|
||||
nAmount = 0;
|
||||
mapAmount = CAmountMap();
|
||||
nConf = std::numeric_limits<int>::max();
|
||||
fIsWatchonly = false;
|
||||
}
|
||||
|
|
@ -1162,7 +1287,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
|||
|
||||
bool has_filtered_address = false;
|
||||
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())) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::string strasset = "";
|
||||
if (params.size() > 4 && params[4].isStr()) {
|
||||
strasset = params[4].get_str();
|
||||
}
|
||||
CAsset asset;
|
||||
if (!strasset.empty()) {
|
||||
asset = GetAssetFromString(strasset);
|
||||
}
|
||||
|
||||
// Tally
|
||||
std::map<CTxDestination, tallyitem> mapTally;
|
||||
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)
|
||||
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;
|
||||
if (!ExtractDestination(txout.scriptPubKey, address))
|
||||
continue;
|
||||
|
|
@ -1196,8 +1332,17 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
|||
if(!(mine & filter))
|
||||
continue;
|
||||
|
||||
CAmount amt = wtx.GetOutputValueOut(index);
|
||||
if (amt < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strasset != "" && wtx.GetOutputAsset(index) != asset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tallyitem& item = mapTally[address];
|
||||
item.nAmount += txout.nValue;
|
||||
item.mapAmount[wtx.GetOutputAsset(index)] += amt;
|
||||
item.nConf = std::min(item.nConf, nDepth);
|
||||
item.txids.push_back(wtx.GetHash());
|
||||
if (mine & ISMINE_WATCH_ONLY)
|
||||
|
|
@ -1229,12 +1374,12 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
|||
if (it == mapTally.end() && !fIncludeEmpty)
|
||||
continue;
|
||||
|
||||
CAmount nAmount = 0;
|
||||
CAmountMap mapAmount;
|
||||
int nConf = std::numeric_limits<int>::max();
|
||||
bool fIsWatchonly = false;
|
||||
if (it != mapTally.end())
|
||||
{
|
||||
nAmount = (*it).second.nAmount;
|
||||
mapAmount = (*it).second.mapAmount;
|
||||
nConf = (*it).second.nConf;
|
||||
fIsWatchonly = (*it).second.fIsWatchonly;
|
||||
}
|
||||
|
|
@ -1242,7 +1387,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
|||
if (by_label)
|
||||
{
|
||||
tallyitem& _item = label_tally[label];
|
||||
_item.nAmount += nAmount;
|
||||
_item.mapAmount += mapAmount;
|
||||
_item.nConf = std::min(_item.nConf, nConf);
|
||||
_item.fIsWatchonly = fIsWatchonly;
|
||||
}
|
||||
|
|
@ -1252,7 +1397,7 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
|||
if(fIsWatchonly)
|
||||
obj.pushKV("involvesWatchonly", true);
|
||||
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("label", label);
|
||||
UniValue transactions(UniValue::VARR);
|
||||
|
|
@ -1272,12 +1417,12 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo
|
|||
{
|
||||
for (const auto& entry : label_tally)
|
||||
{
|
||||
CAmount nAmount = entry.second.nAmount;
|
||||
CAmountMap mapAmount = entry.second.mapAmount;
|
||||
int nConf = entry.second.nConf;
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
if (entry.second.fIsWatchonly)
|
||||
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("label", entry.first);
|
||||
ret.push_back(obj);
|
||||
|
|
@ -1296,7 +1441,7 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
|
|||
return NullUniValue;
|
||||
}
|
||||
|
||||
if (request.fHelp || request.params.size() > 4)
|
||||
if (request.fHelp || request.params.size() > 5)
|
||||
throw std::runtime_error(
|
||||
"listreceivedbyaddress ( minconf include_empty include_watchonly address_filter )\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"
|
||||
"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"
|
||||
"5. assetlabel (string, optional) The hex asset id or asset label to filter for.\n"
|
||||
"\nResult:\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
|
|
@ -1420,6 +1566,11 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int n
|
|||
MaybePushAddress(entry, s.destination);
|
||||
entry.pushKV("category", "send");
|
||||
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)) {
|
||||
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("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)) {
|
||||
entry.pushKV("label", label);
|
||||
}
|
||||
|
|
@ -1805,14 +1961,23 @@ static UniValue gettransaction(const JSONRPCRequest& request)
|
|||
}
|
||||
const CWalletTx& wtx = it->second;
|
||||
|
||||
CAmount nCredit = wtx.GetCredit(filter);
|
||||
CAmount nDebit = wtx.GetDebit(filter);
|
||||
CAmount nNet = nCredit - nDebit;
|
||||
CAmount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit : 0);
|
||||
CAmountMap nCredit = wtx.GetCredit(filter);
|
||||
CAmountMap nDebit = wtx.GetDebit(filter);
|
||||
CAmountMap nNet = nCredit - nDebit;
|
||||
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))
|
||||
entry.pushKV("fee", ValueFromAmount(nFee));
|
||||
entry.pushKV("fee", AmountMapToUniv(nFee, ""));
|
||||
|
||||
WalletTxToJSON(wtx, entry);
|
||||
|
||||
|
|
@ -2450,9 +2615,9 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
|
|||
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
|
||||
obj.pushKV("walletname", pwallet->GetName());
|
||||
obj.pushKV("walletversion", pwallet->GetVersion());
|
||||
obj.pushKV("balance", ValueFromAmount(pwallet->GetBalance()));
|
||||
obj.pushKV("unconfirmed_balance", ValueFromAmount(pwallet->GetUnconfirmedBalance()));
|
||||
obj.pushKV("immature_balance", ValueFromAmount(pwallet->GetImmatureBalance()));
|
||||
obj.pushKV("balance", AmountMapToUniv(pwallet->GetBalance(), ""));
|
||||
obj.pushKV("unconfirmed_balance", AmountMapToUniv(pwallet->GetUnconfirmedBalance(), ""));
|
||||
obj.pushKV("immature_balance", AmountMapToUniv(pwallet->GetImmatureBalance(), ""));
|
||||
obj.pushKV("txcount", (int)pwallet->mapWallet.size());
|
||||
obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime());
|
||||
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"
|
||||
" \"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"
|
||||
" \"asset\" (string, default="") Asset to filter outputs for.\n"
|
||||
" }\n"
|
||||
"\nResult\n"
|
||||
"[ (array of json object)\n"
|
||||
|
|
@ -2794,6 +2960,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
|||
CAmount nMaximumAmount = MAX_MONEY;
|
||||
CAmount nMinimumSumAmount = MAX_MONEY;
|
||||
uint64_t nMaximumCount = 0;
|
||||
std::string asset_str;
|
||||
|
||||
if (!request.params[4].isNull()) {
|
||||
const UniValue& options = request.params[4].get_obj();
|
||||
|
|
@ -2809,6 +2976,14 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
|||
|
||||
if (options.exists("maximumCount"))
|
||||
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
|
||||
|
|
@ -2819,19 +2994,33 @@ static UniValue listunspent(const JSONRPCRequest& request)
|
|||
std::vector<COutput> vecOutputs;
|
||||
{
|
||||
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);
|
||||
|
||||
for (const COutput& out : vecOutputs) {
|
||||
CTxDestination address;
|
||||
const CTxOut& tx_out = out.tx->tx->vout[out.i];
|
||||
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
|
||||
bool fValidAddress = ExtractDestination(scriptPubKey, address);
|
||||
|
||||
if (destinations.size() && (!fValidAddress || !destinations.count(address)))
|
||||
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);
|
||||
entry.pushKV("txid", out.tx->GetHash().GetHex());
|
||||
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("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("spendable", out.fSpendable);
|
||||
entry.pushKV("solvable", out.fSolvable);
|
||||
|
|
@ -2884,9 +3084,10 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
|
|||
}
|
||||
else {
|
||||
RPCTypeCheckArgument(options, UniValue::VOBJ);
|
||||
|
||||
RPCTypeCheckObj(options,
|
||||
{
|
||||
{"changeAddress", UniValueType(UniValue::VSTR)},
|
||||
{"changeAddress", UniValueType()}, // will be checked below
|
||||
{"changePosition", UniValueType(UniValue::VNUM)},
|
||||
{"change_type", UniValueType(UniValue::VSTR)},
|
||||
{"includeWatching", UniValueType(UniValue::VBOOL)},
|
||||
|
|
@ -2900,13 +3101,38 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
|
|||
true, true);
|
||||
|
||||
if (options.exists("changeAddress")) {
|
||||
CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
|
||||
std::map<CAsset, CTxDestination> destinations;
|
||||
|
||||
if (!IsValidDestination(dest)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address");
|
||||
if (options["changeAddress"].isStr()) {
|
||||
// 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"))
|
||||
|
|
@ -3007,7 +3233,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
|
|||
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
|
||||
"2. options (object, optional)\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"
|
||||
" \"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"
|
||||
|
|
@ -3096,7 +3322,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
|
|||
" \"vout\":n, (numeric, required) The output number\n"
|
||||
" \"scriptPubKey\": \"hex\", (string, required) script key\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"
|
||||
|
|
@ -3610,6 +3837,83 @@ static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& de
|
|||
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. */
|
||||
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"
|
||||
" \"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"
|
||||
" \"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"
|
||||
" \"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"
|
||||
|
|
@ -3694,10 +4001,20 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
|
|||
ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
||||
|
||||
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("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
|
||||
UniValue detail = DescribeWalletAddress(pwallet, dest);
|
||||
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)) {
|
||||
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
|
||||
}
|
||||
|
|
@ -3957,7 +4274,7 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C
|
|||
SignatureData 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);
|
||||
psbt_out.FromSignatureData(sigdata);
|
||||
}
|
||||
|
|
@ -4115,7 +4432,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
|
|||
|
||||
CAmount fee;
|
||||
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]);
|
||||
|
||||
// Make a blank psbt
|
||||
|
|
@ -4190,8 +4507,6 @@ UniValue getpeginaddress(const JSONRPCRequest& request)
|
|||
|
||||
//Creates new address for receiving unlocked utxos
|
||||
JSONRPCRequest req;
|
||||
//TODO(rebase) replace with CT/CA
|
||||
//CTxDestination dest(DecodeDestination(getnewaddress(req).get_str()).GetUnblinded());
|
||||
CTxDestination address = DecodeDestination(getnewaddress(req).get_str());
|
||||
|
||||
Witnessifier w(pwallet);
|
||||
|
|
@ -4459,11 +4774,7 @@ UniValue sendtomainchain_base(const JSONRPCRequest& request)
|
|||
|
||||
mapValue_t mapValue;
|
||||
CCoinControl no_coin_control; // This is a deprecated API
|
||||
CTransactionRef tx = SendMoney(pwallet, address, nAmount, subtract_fee, no_coin_control, std::move(mapValue));
|
||||
|
||||
//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);
|
||||
CTransactionRef tx = SendMoney(pwallet, address, nAmount, Params().GetConsensus().pegged_asset, subtract_fee, no_coin_control, std::move(mapValue), true /* ignore_blind_fail */);
|
||||
|
||||
return (*tx).GetHash().GetHex();
|
||||
|
||||
|
|
@ -4612,9 +4923,10 @@ UniValue sendtomainchain_pak(const JSONRPCRequest& request)
|
|||
int whitelistindex=-1;
|
||||
std::vector<secp256k1_pubkey> pak_online = paklist.OnlineKeys();
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (whitelistindex == -1)
|
||||
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;
|
||||
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);
|
||||
|
||||
|
|
@ -4852,8 +5164,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
|||
|
||||
CAmount value = 0;
|
||||
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);
|
||||
|
|
@ -4896,10 +5207,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
|||
CScriptWitness pegin_witness;
|
||||
std::vector<std::vector<unsigned char> >& stack = pegin_witness.stack;
|
||||
stack.push_back(value_bytes);
|
||||
//TODO(rebase) CA
|
||||
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>(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>(witnessProgScript.begin(), witnessProgScript.end()));
|
||||
stack.push_back(txData);
|
||||
|
|
@ -4923,10 +5231,8 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
|
|||
CCoinControl coin_control;
|
||||
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[1].nValue = mtx.vout[1].nValue.GetAmount() + nFeeNeeded;
|
||||
mtx.vout[0].nValue = mtx.vout[0].nValue - nFeeNeeded;
|
||||
mtx.vout[0].nValue = mtx.vout[0].nValue.GetAmount() - nFeeNeeded;
|
||||
mtx.vout[1].nValue = mtx.vout[1].nValue.GetAmount() + nFeeNeeded;
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
|
|
@ -5018,8 +5324,9 @@ UniValue claimpegin(const JSONRPCRequest& request)
|
|||
// Send it
|
||||
CValidationState state;
|
||||
mapValue_t mapValue;
|
||||
CReserveKey reservekey(pwallet);
|
||||
if (!pwallet->CommitTransaction(MakeTransactionRef(mtx), mapValue, {} /* orderForm */, reservekey, g_connman.get(), state)) {
|
||||
std::vector<std::unique_ptr<CReserveKey>> reservekeys;
|
||||
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));
|
||||
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
|
||||
|
||||
#include <amount.h>
|
||||
#include <asset.h>
|
||||
#include <outputtype.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <streams.h>
|
||||
|
|
@ -173,6 +174,8 @@ struct CRecipient
|
|||
{
|
||||
CScript scriptPubKey;
|
||||
CAmount nAmount;
|
||||
CAsset asset;
|
||||
CPubKey confidentiality_key;
|
||||
bool fSubtractFeeFromAmount;
|
||||
};
|
||||
|
||||
|
|
@ -202,6 +205,9 @@ struct COutputEntry
|
|||
CTxDestination destination;
|
||||
CAmount amount;
|
||||
int vout;
|
||||
CAsset asset;
|
||||
uint256 amount_blinding_factor;
|
||||
uint256 asset_blinding_factor;
|
||||
};
|
||||
|
||||
/** 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
|
||||
* 2014 (removed in commit 93a18a3)
|
||||
*/
|
||||
mapValue_t mapValue;
|
||||
mutable mapValue_t mapValue;
|
||||
std::vector<std::pair<std::string, std::string> > vOrderForm;
|
||||
unsigned int fTimeReceivedIsTxTime;
|
||||
unsigned int nTimeReceived; //!< time received by this node
|
||||
|
|
@ -354,15 +360,15 @@ public:
|
|||
mutable bool fAvailableWatchCreditCached;
|
||||
mutable bool fChangeCached;
|
||||
mutable bool fInMempool;
|
||||
mutable CAmount nDebitCached;
|
||||
mutable CAmount nCreditCached;
|
||||
mutable CAmount nImmatureCreditCached;
|
||||
mutable CAmount nAvailableCreditCached;
|
||||
mutable CAmount nWatchDebitCached;
|
||||
mutable CAmount nWatchCreditCached;
|
||||
mutable CAmount nImmatureWatchCreditCached;
|
||||
mutable CAmount nAvailableWatchCreditCached;
|
||||
mutable CAmount nChangeCached;
|
||||
mutable CAmountMap nDebitCached;
|
||||
mutable CAmountMap nCreditCached;
|
||||
mutable CAmountMap nImmatureCreditCached;
|
||||
mutable CAmountMap nAvailableCreditCached;
|
||||
mutable CAmountMap nWatchDebitCached;
|
||||
mutable CAmountMap nWatchCreditCached;
|
||||
mutable CAmountMap nImmatureWatchCreditCached;
|
||||
mutable CAmountMap nAvailableWatchCreditCached;
|
||||
mutable CAmountMap nChangeCached;
|
||||
|
||||
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
|
||||
{
|
||||
|
|
@ -388,15 +394,15 @@ public:
|
|||
fAvailableWatchCreditCached = false;
|
||||
fChangeCached = false;
|
||||
fInMempool = false;
|
||||
nDebitCached = 0;
|
||||
nCreditCached = 0;
|
||||
nImmatureCreditCached = 0;
|
||||
nAvailableCreditCached = 0;
|
||||
nWatchDebitCached = 0;
|
||||
nWatchCreditCached = 0;
|
||||
nAvailableWatchCreditCached = 0;
|
||||
nImmatureWatchCreditCached = 0;
|
||||
nChangeCached = 0;
|
||||
nDebitCached = CAmountMap();
|
||||
nCreditCached = CAmountMap();
|
||||
nImmatureCreditCached = CAmountMap();
|
||||
nAvailableCreditCached = CAmountMap();
|
||||
nWatchDebitCached = CAmountMap();
|
||||
nWatchCreditCached = CAmountMap();
|
||||
nAvailableWatchCreditCached = CAmountMap();
|
||||
nImmatureWatchCreditCached = CAmountMap();
|
||||
nChangeCached = CAmountMap();
|
||||
nOrderPos = -1;
|
||||
}
|
||||
|
||||
|
|
@ -448,6 +454,7 @@ public:
|
|||
fImmatureWatchCreditCached = false;
|
||||
fDebitCached = false;
|
||||
fChangeCached = false;
|
||||
WipeUnknownBlindingData();
|
||||
}
|
||||
|
||||
void BindWallet(CWallet *pwalletIn)
|
||||
|
|
@ -457,12 +464,12 @@ public:
|
|||
}
|
||||
|
||||
//! filter decides which addresses will count towards the debit
|
||||
CAmount GetDebit(const isminefilter& filter) const;
|
||||
CAmount GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
CAmount 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);
|
||||
CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
CAmount GetChange() const;
|
||||
CAmountMap GetDebit(const isminefilter& filter) const;
|
||||
CAmountMap GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
CAmountMap GetImmatureCredit(bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
CAmountMap GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
CAmountMap GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
CAmountMap GetChange() const;
|
||||
|
||||
// Get the marginal bytes if spending the specified output from this transaction
|
||||
int GetSpendSize(unsigned int out, bool use_max_sig = false) const
|
||||
|
|
@ -475,7 +482,7 @@ public:
|
|||
|
||||
bool IsFromMe(const isminefilter& filter) const
|
||||
{
|
||||
return (GetDebit(filter) > 0);
|
||||
return (GetDebit(filter) > CAmountMap());
|
||||
}
|
||||
|
||||
// True if only scriptSigs are different
|
||||
|
|
@ -493,6 +500,48 @@ public:
|
|||
bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
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
|
||||
|
|
@ -535,7 +584,7 @@ public:
|
|||
|
||||
inline CInputCoin GetInputCoin() const
|
||||
{
|
||||
return CInputCoin(tx->tx, i, nInputBytes);
|
||||
return CInputCoin(tx, i, nInputBytes);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -578,6 +627,40 @@ struct 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
|
||||
/**
|
||||
* 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
|
||||
* 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;
|
||||
|
||||
/** 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`
|
||||
CExtPubKey offline_xpub;
|
||||
|
||||
// Master derivation blinding key
|
||||
uint256 blinding_derivation_key;
|
||||
// Specifically imported blinding keys
|
||||
std::map<CScriptID, uint256> mapSpecificBlindingKeys;
|
||||
|
||||
// END ELEMENTS
|
||||
|
||||
const CWalletTx* GetWalletTx(const uint256& hash) const;
|
||||
|
|
@ -775,7 +863,7 @@ public:
|
|||
/**
|
||||
* 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.
|
||||
|
|
@ -793,8 +881,8 @@ public:
|
|||
* completion the coin set and corresponding actual target value is
|
||||
* assembled
|
||||
*/
|
||||
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
|
||||
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
|
||||
bool SelectCoinsMinConf(const CAmountMap& mapTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
|
||||
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);
|
||||
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);
|
||||
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
|
||||
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;
|
||||
CAmount GetUnconfirmedBalance() const;
|
||||
CAmount GetImmatureBalance() const;
|
||||
CAmount GetUnconfirmedWatchOnlyBalance() const;
|
||||
CAmount GetImmatureWatchOnlyBalance() const;
|
||||
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const;
|
||||
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
|
||||
CAmountMap GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
|
||||
CAmountMap GetUnconfirmedBalance() const;
|
||||
CAmountMap GetImmatureBalance() const;
|
||||
CAmountMap GetUnconfirmedWatchOnlyBalance() const;
|
||||
CAmountMap GetImmatureWatchOnlyBalance() const;
|
||||
CAmountMap GetLegacyBalance(const isminefilter& filter, int minDepth) const;
|
||||
CAmountMap GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
|
||||
|
||||
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
|
||||
* @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,
|
||||
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
|
||||
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
|
||||
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, 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, 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
|
||||
{
|
||||
|
|
@ -973,19 +1061,24 @@ public:
|
|||
* Returns amount of debit if the input matches the
|
||||
* 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;
|
||||
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
|
||||
CAmountMap GetCredit(const CTxOut& txout, const isminefilter& filter) 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;
|
||||
/** should probably be renamed to IsRelevantToMe */
|
||||
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 */
|
||||
bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
|
||||
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
|
||||
CAmount GetChange(const CTransaction& tx) const;
|
||||
CAmountMap GetCredit(const CTransaction& tx, const isminefilter& filter) 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;
|
||||
|
||||
DBErrors LoadWallet(bool& fFirstRunRet);
|
||||
|
|
@ -1149,6 +1242,20 @@ public:
|
|||
bool SetOfflineCounter(int counter);
|
||||
bool SetOfflineDescriptor(const std::string& offline_desc_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. */
|
||||
|
|
@ -1170,6 +1277,18 @@ public:
|
|||
CReserveKey() = default;
|
||||
CReserveKey(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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -174,6 +174,14 @@ bool WalletBatch::WriteOfflineCounter(int 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 {
|
||||
public:
|
||||
unsigned int nKeys;
|
||||
|
|
@ -466,6 +474,24 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
|||
std::string descriptor;
|
||||
ssValue >> 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" &&
|
||||
strType != "minversion" && strType != "acentry") {
|
||||
wss.m_unknown_records++;
|
||||
|
|
@ -555,6 +581,16 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
|
|||
if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
|
||||
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
|
||||
// upgrading, we don't want to make it worse.
|
||||
if (result != DBErrors::LOAD_OK)
|
||||
|
|
|
|||
|
|
@ -206,8 +206,9 @@ public:
|
|||
bool WriteOnlineKey(const CPubKey& online_key);
|
||||
bool WriteOfflineCounter(int counter);
|
||||
bool WriteOfflineDescriptor(const std::string& offline_desc);
|
||||
// DEPRECATED
|
||||
bool WriteOfflineXPubKey(const CExtPubKey& offline_xpub);
|
||||
bool WriteBlindingDerivationKey(const uint256& key);
|
||||
bool WriteSpecificBlindingKey(const uint160& scriptid, const uint256& key);
|
||||
|
||||
DBErrors LoadWallet(CWallet* pwallet);
|
||||
DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue