@@ -94,7 +94,8 @@
|
||||
},
|
||||
{
|
||||
"ac_name": "OOT",
|
||||
"ac_supply": "216000000"
|
||||
"ac_supply": "216000000",
|
||||
"ac_sapling": "5000000"
|
||||
},
|
||||
{
|
||||
"ac_name": "BNTN",
|
||||
|
||||
@@ -27,7 +27,7 @@ echo $pubkey
|
||||
./komodod -pubkey=$pubkey -ac_name=BEER -ac_supply=100000000 -addnode=78.47.196.146 &
|
||||
./komodod -pubkey=$pubkey -ac_name=PIZZA -ac_supply=100000000 -addnode=78.47.196.146 &
|
||||
./komodod -pubkey=$pubkey -ac_name=NINJA -ac_supply=100000000 -addnode=78.47.196.146 &
|
||||
./komodod -pubkey=$pubkey -ac_name=OOT -ac_supply=216000000 -addnode=174.138.107.226 &
|
||||
./komodod -pubkey=$pubkey -ac_name=OOT -ac_supply=216000000 -ac_saplinig=5000000 -addnode=174.138.107.226 &
|
||||
./komodod -pubkey=$pubkey -ac_name=BNTN -ac_supply=500000000 -addnode=94.130.169.205 &
|
||||
./komodod -pubkey=$pubkey -ac_name=CHAIN -ac_supply=999999 -addnode=78.47.146.222 &
|
||||
./komodod -pubkey=$pubkey -ac_name=PRLPAY -ac_supply=500000000 -addnode=13.250.226.125 &
|
||||
|
||||
@@ -74,11 +74,11 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
|
||||
switch ( ecode )
|
||||
{
|
||||
case EVAL_IMPORTPAYOUT:
|
||||
//return ImportPayout(vparams, txTo, nIn);
|
||||
return ImportPayout(vparams, txTo, nIn);
|
||||
break;
|
||||
|
||||
case EVAL_IMPORTCOIN:
|
||||
//return ImportCoin(vparams, txTo, nIn);
|
||||
return ImportCoin(vparams, txTo, nIn);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -291,7 +291,7 @@ TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesRejectOtherTx) {
|
||||
|
||||
{
|
||||
SCOPED_TRACE("BlockOverwinterRulesRejectSaplingTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-overwinter-tx-version-group-id");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "bad-overwinter-tx-version-group-id");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,6 +319,6 @@ TEST_F(ContextualCheckBlockTest, BlockSaplingRulesRejectOtherTx) {
|
||||
|
||||
{
|
||||
SCOPED_TRACE("BlockSaplingRulesRejectOverwinterTx");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-sapling-tx-version-group-id");
|
||||
ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "bad-sapling-tx-version-group-id");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,7 +844,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
const CChainParams& chainparams = Params();
|
||||
|
||||
// Set this early so that experimental features are correctly enabled/disabled
|
||||
fExperimentalMode = GetBoolArg("-experimentalfeatures", false);
|
||||
fExperimentalMode = GetBoolArg("-experimentalfeatures", true);
|
||||
|
||||
// Fail early if user has set experimental options without the global flag
|
||||
if (!fExperimentalMode) {
|
||||
|
||||
@@ -1832,6 +1832,22 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
||||
else return(0);
|
||||
}
|
||||
|
||||
int32_t komodo_acpublic(uint32_t tiptime)
|
||||
{
|
||||
int32_t acpublic = ASSETCHAINS_PUBLIC; CBlockIndex *pindex;
|
||||
if ( acpublic == 0 )
|
||||
{
|
||||
if ( tiptime == 0 )
|
||||
{
|
||||
if ( (pindex= chainActive.LastTip()) != 0 )
|
||||
tiptime = pindex->nTime;
|
||||
}
|
||||
if ( (ASSETCHAINS_SYMBOL[0] == 0 || strcmp(ASSETCHAINS_SYMBOL,"ZEX") == 0) && tiptime >= KOMODO_SAPLING_DEADLINE )
|
||||
acpublic = 1;
|
||||
}
|
||||
return(acpublic);
|
||||
}
|
||||
|
||||
int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock)
|
||||
{
|
||||
CTxDestination address; int32_t i,j,m,n,vout; uint8_t *script; uint256 txid,hashBlock; int64_t zfunds=0,vinsum=0,voutsum=0;
|
||||
|
||||
26
src/main.cpp
26
src/main.cpp
@@ -77,7 +77,7 @@ static int64_t nTimeBestReceived = 0;
|
||||
CWaitableCriticalSection csBestBlock;
|
||||
CConditionVariable cvBlockChange;
|
||||
int nScriptCheckThreads = 0;
|
||||
bool fExperimentalMode = false;
|
||||
bool fExperimentalMode = true;
|
||||
bool fImporting = false;
|
||||
bool fReindex = false;
|
||||
bool fTxIndex = false;
|
||||
@@ -1034,9 +1034,12 @@ bool ContextualCheckTransaction(
|
||||
}
|
||||
|
||||
// Reject transactions with non-Sapling version group ID
|
||||
if (tx.fOverwintered && tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID) {
|
||||
return state.DoS(dosLevel, error("CheckTransaction(): invalid Sapling tx version"),
|
||||
REJECT_INVALID, "bad-sapling-tx-version-group-id");
|
||||
if (tx.fOverwintered && tx.nVersionGroupId != SAPLING_VERSION_GROUP_ID)
|
||||
{
|
||||
//return state.DoS(dosLevel, error("CheckTransaction(): invalid Sapling tx version"),REJECT_INVALID, "bad-sapling-tx-version-group-id");
|
||||
return state.DoS(isInitBlockDownload() ? 0 : dosLevel,
|
||||
error("CheckTransaction(): invalid Sapling tx version"),
|
||||
REJECT_INVALID, "bad-sapling-tx-version-group-id");
|
||||
}
|
||||
|
||||
// Reject transactions with invalid version
|
||||
@@ -1058,9 +1061,12 @@ bool ContextualCheckTransaction(
|
||||
}
|
||||
|
||||
// Reject transactions with non-Overwinter version group ID
|
||||
if (tx.fOverwintered && tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID) {
|
||||
return state.DoS(dosLevel, error("CheckTransaction(): invalid Overwinter tx version"),
|
||||
REJECT_INVALID, "bad-overwinter-tx-version-group-id");
|
||||
if (tx.fOverwintered && tx.nVersionGroupId != OVERWINTER_VERSION_GROUP_ID)
|
||||
{
|
||||
//return state.DoS(dosLevel, error("CheckTransaction(): invalid Overwinter tx version"),REJECT_INVALID, "bad-overwinter-tx-version-group-id");
|
||||
return state.DoS(isInitBlockDownload() ? 0 : dosLevel,
|
||||
error("CheckTransaction(): invalid Overwinter tx version"),
|
||||
REJECT_INVALID, "bad-overwinter-tx-version-group-id");
|
||||
}
|
||||
|
||||
// Reject transactions with invalid version
|
||||
@@ -1253,10 +1259,12 @@ int32_t komodo_isnotaryvout(char *coinaddr) // from ac_private chains only
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t komodo_acpublic(uint32_t tiptime);
|
||||
|
||||
bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransaction& tx, CValidationState &state)
|
||||
{
|
||||
// Basic checks that don't depend on any context
|
||||
int32_t invalid_private_taddr=0,z_z=0,z_t=0,t_z=0,acpublic = ASSETCHAINS_PUBLIC;
|
||||
int32_t invalid_private_taddr=0,z_z=0,z_t=0,t_z=0,acpublic = komodo_acpublic(tiptime);
|
||||
/**
|
||||
* Previously:
|
||||
* 1. The consensus rule below was:
|
||||
@@ -1359,8 +1367,6 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio
|
||||
return state.DoS(100, error("CheckTransaction(): tx.valueBalance has no sources or sinks"),
|
||||
REJECT_INVALID, "bad-txns-valuebalance-nonzero");
|
||||
}
|
||||
if ( (ASSETCHAINS_SYMBOL[0] == 0 || strcmp(ASSETCHAINS_SYMBOL,"ZEX") == 0) && tiptime >= KOMODO_SAPLING_DEADLINE )
|
||||
acpublic = 1;
|
||||
if ( acpublic != 0 && (tx.vShieldedSpend.empty() == 0 || tx.vShieldedOutput.empty() == 0) )
|
||||
{
|
||||
return state.DoS(100, error("CheckTransaction(): this is a public chain, no sapling allowed"),
|
||||
|
||||
@@ -670,7 +670,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
|
||||
pindexNew->nCachedBranchId = diskindex.nCachedBranchId;
|
||||
pindexNew->nTx = diskindex.nTx;
|
||||
pindexNew->nSproutValue = diskindex.nSproutValue;
|
||||
|
||||
pindexNew->nSaplingValue = diskindex.nSaplingValue;
|
||||
|
||||
// Consistency checks
|
||||
auto header = pindexNew->GetBlockHeader();
|
||||
if (header.GetHash() != pindexNew->GetBlockHash())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
#include "asyncrpcoperation.h"
|
||||
#include "paymentdisclosure.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "transaction_builder.h"
|
||||
#include "wallet.h"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "zcash/JoinSplit.hpp"
|
||||
@@ -24,11 +25,13 @@
|
||||
|
||||
using namespace libzcash;
|
||||
|
||||
// Input UTXO is a tuple of txid, vout, amount
|
||||
typedef std::tuple<COutPoint, CAmount> MergeToAddressInputUTXO;
|
||||
// Input UTXO is a tuple of txid, vout, amount, script
|
||||
typedef std::tuple<COutPoint, CAmount, CScript> MergeToAddressInputUTXO;
|
||||
|
||||
// Input JSOP is a tuple of JSOutpoint, note, amount, spending key
|
||||
typedef std::tuple<JSOutPoint, SproutNote, CAmount, SpendingKey> MergeToAddressInputNote;
|
||||
typedef std::tuple<JSOutPoint, SproutNote, CAmount, SproutSpendingKey> MergeToAddressInputSproutNote;
|
||||
|
||||
typedef std::tuple<SaplingOutPoint, SaplingNote, CAmount, SaplingExpandedSpendingKey> MergeToAddressInputSaplingNote;
|
||||
|
||||
// A recipient is a tuple of address, memo (optional if zaddr)
|
||||
typedef std::tuple<std::string, std::string> MergeToAddressRecipient;
|
||||
@@ -53,33 +56,36 @@ class AsyncRPCOperation_mergetoaddress : public AsyncRPCOperation
|
||||
{
|
||||
public:
|
||||
AsyncRPCOperation_mergetoaddress(
|
||||
CMutableTransaction contextualTx,
|
||||
std::vector<MergeToAddressInputUTXO> utxoInputs,
|
||||
std::vector<MergeToAddressInputNote> noteInputs,
|
||||
MergeToAddressRecipient recipient,
|
||||
CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE,
|
||||
UniValue contextInfo = NullUniValue);
|
||||
boost::optional<TransactionBuilder> builder,
|
||||
CMutableTransaction contextualTx,
|
||||
std::vector<MergeToAddressInputUTXO> utxoInputs,
|
||||
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs,
|
||||
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs,
|
||||
MergeToAddressRecipient recipient,
|
||||
CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE,
|
||||
UniValue contextInfo = NullUniValue);
|
||||
virtual ~AsyncRPCOperation_mergetoaddress();
|
||||
|
||||
|
||||
// We don't want to be copied or moved around
|
||||
AsyncRPCOperation_mergetoaddress(AsyncRPCOperation_mergetoaddress const&) = delete; // Copy construct
|
||||
AsyncRPCOperation_mergetoaddress(AsyncRPCOperation_mergetoaddress&&) = delete; // Move construct
|
||||
AsyncRPCOperation_mergetoaddress& operator=(AsyncRPCOperation_mergetoaddress const&) = delete; // Copy assign
|
||||
AsyncRPCOperation_mergetoaddress& operator=(AsyncRPCOperation_mergetoaddress&&) = delete; // Move assign
|
||||
|
||||
|
||||
virtual void main();
|
||||
|
||||
|
||||
virtual UniValue getStatus() const;
|
||||
|
||||
|
||||
bool testmode = false; // Set to true to disable sending txs and generating proofs
|
||||
|
||||
bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database.
|
||||
|
||||
|
||||
bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database.
|
||||
|
||||
private:
|
||||
friend class TEST_FRIEND_AsyncRPCOperation_mergetoaddress; // class for unit testing
|
||||
|
||||
|
||||
UniValue contextinfo_; // optional data to include in return value from getStatus()
|
||||
|
||||
|
||||
bool isUsingBuilder_; // Indicates that no Sprout addresses are involved
|
||||
uint32_t consensusBranchId_;
|
||||
CAmount fee_;
|
||||
int mindepth_;
|
||||
@@ -88,43 +94,45 @@ private:
|
||||
bool isToZaddr_;
|
||||
CTxDestination toTaddr_;
|
||||
PaymentAddress toPaymentAddress_;
|
||||
|
||||
|
||||
uint256 joinSplitPubKey_;
|
||||
unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES];
|
||||
|
||||
|
||||
// The key is the result string from calling JSOutPoint::ToString()
|
||||
std::unordered_map<std::string, MergeToAddressWitnessAnchorData> jsopWitnessAnchorMap;
|
||||
|
||||
|
||||
std::vector<MergeToAddressInputUTXO> utxoInputs_;
|
||||
std::vector<MergeToAddressInputNote> noteInputs_;
|
||||
|
||||
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs_;
|
||||
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs_;
|
||||
|
||||
TransactionBuilder builder_;
|
||||
CTransaction tx_;
|
||||
|
||||
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
|
||||
bool main_impl();
|
||||
|
||||
|
||||
// JoinSplit without any input notes to spend
|
||||
UniValue perform_joinsplit(MergeToAddressJSInfo&);
|
||||
|
||||
|
||||
// JoinSplit with input notes to spend (JSOutPoints))
|
||||
UniValue perform_joinsplit(MergeToAddressJSInfo&, std::vector<JSOutPoint>&);
|
||||
|
||||
|
||||
// JoinSplit where you have the witnesses and anchor
|
||||
UniValue perform_joinsplit(
|
||||
MergeToAddressJSInfo& info,
|
||||
std::vector<boost::optional<SproutWitness>> witnesses,
|
||||
uint256 anchor);
|
||||
|
||||
MergeToAddressJSInfo& info,
|
||||
std::vector<boost::optional<SproutWitness>> witnesses,
|
||||
uint256 anchor);
|
||||
|
||||
void sign_send_raw_transaction(UniValue obj); // throws exception if there was an error
|
||||
|
||||
|
||||
void lock_utxos();
|
||||
|
||||
|
||||
void unlock_utxos();
|
||||
|
||||
|
||||
void lock_notes();
|
||||
|
||||
|
||||
void unlock_notes();
|
||||
|
||||
|
||||
// payment disclosure!
|
||||
std::vector<PaymentDisclosureKeyInfo> paymentDisclosureData_;
|
||||
};
|
||||
@@ -135,54 +143,54 @@ class TEST_FRIEND_AsyncRPCOperation_mergetoaddress
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<AsyncRPCOperation_mergetoaddress> delegate;
|
||||
|
||||
|
||||
TEST_FRIEND_AsyncRPCOperation_mergetoaddress(std::shared_ptr<AsyncRPCOperation_mergetoaddress> ptr) : delegate(ptr) {}
|
||||
|
||||
|
||||
CTransaction getTx()
|
||||
{
|
||||
return delegate->tx_;
|
||||
}
|
||||
|
||||
|
||||
void setTx(CTransaction tx)
|
||||
{
|
||||
delegate->tx_ = tx;
|
||||
}
|
||||
|
||||
|
||||
// Delegated methods
|
||||
|
||||
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s)
|
||||
{
|
||||
return delegate->get_memo_from_hex_string(s);
|
||||
}
|
||||
|
||||
|
||||
bool main_impl()
|
||||
{
|
||||
return delegate->main_impl();
|
||||
}
|
||||
|
||||
|
||||
UniValue perform_joinsplit(MergeToAddressJSInfo& info)
|
||||
{
|
||||
return delegate->perform_joinsplit(info);
|
||||
}
|
||||
|
||||
|
||||
UniValue perform_joinsplit(MergeToAddressJSInfo& info, std::vector<JSOutPoint>& v)
|
||||
{
|
||||
return delegate->perform_joinsplit(info, v);
|
||||
}
|
||||
|
||||
|
||||
UniValue perform_joinsplit(
|
||||
MergeToAddressJSInfo& info,
|
||||
std::vector<boost::optional<SproutWitness>> witnesses,
|
||||
uint256 anchor)
|
||||
MergeToAddressJSInfo& info,
|
||||
std::vector<boost::optional<SproutWitness>> witnesses,
|
||||
uint256 anchor)
|
||||
{
|
||||
return delegate->perform_joinsplit(info, witnesses, anchor);
|
||||
}
|
||||
|
||||
|
||||
void sign_send_raw_transaction(UniValue obj)
|
||||
{
|
||||
delegate->sign_send_raw_transaction(obj);
|
||||
}
|
||||
|
||||
|
||||
void set_state(OperationStatus state)
|
||||
{
|
||||
delegate->state_.store(state);
|
||||
|
||||
@@ -123,7 +123,7 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
|
||||
|
||||
|
||||
// Enable payment disclosure if requested
|
||||
paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false);
|
||||
paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true);
|
||||
}
|
||||
|
||||
AsyncRPCOperation_sendmany::~AsyncRPCOperation_sendmany() {
|
||||
@@ -1366,7 +1366,8 @@ void AsyncRPCOperation_sendmany::add_taddr_change_output_to_tx(CBitcoinAddress *
|
||||
}
|
||||
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> AsyncRPCOperation_sendmany::get_memo_from_hex_string(std::string s) {
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0x00}};
|
||||
// initialize to default memo (no_memo), see section 5.5 of the protocol spec
|
||||
std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}};
|
||||
|
||||
std::vector<unsigned char> rawMemo = ParseHex(s.c_str());
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
bool testmode = false; // Set to true to disable sending txs and generating proofs
|
||||
|
||||
bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database.
|
||||
bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database.
|
||||
|
||||
private:
|
||||
friend class TEST_FRIEND_AsyncRPCOperation_sendmany; // class for unit testing
|
||||
|
||||
@@ -93,7 +93,7 @@ AsyncRPCOperation_shieldcoinbase::AsyncRPCOperation_shieldcoinbase(
|
||||
lock_utxos();
|
||||
|
||||
// Enable payment disclosure if requested
|
||||
paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", false);
|
||||
paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true);
|
||||
}
|
||||
|
||||
AsyncRPCOperation_shieldcoinbase::~AsyncRPCOperation_shieldcoinbase() {
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
bool testmode = false; // Set to true to disable sending txs and generating proofs
|
||||
bool cheatSpend = false; // set when this is shielding a cheating coinbase
|
||||
|
||||
bool paymentDisclosureMode = false; // Set to true to save esk for encrypted notes in payment disclosure database.
|
||||
bool paymentDisclosureMode = true; // Set to true to save esk for encrypted notes in payment disclosure database.
|
||||
|
||||
private:
|
||||
friend class ShieldToAddress;
|
||||
|
||||
@@ -42,7 +42,7 @@ UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp)
|
||||
return NullUniValue;
|
||||
|
||||
string enableArg = "paymentdisclosure";
|
||||
auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, false);
|
||||
auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true);
|
||||
string strPaymentDisclosureDisabledMsg = "";
|
||||
if (!fEnablePaymentDisclosure) {
|
||||
strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_getpaymentdisclosure", enableArg);
|
||||
@@ -149,7 +149,7 @@ UniValue z_validatepaymentdisclosure(const UniValue& params, bool fHelp)
|
||||
return NullUniValue;
|
||||
|
||||
string enableArg = "paymentdisclosure";
|
||||
auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, false);
|
||||
auto fEnablePaymentDisclosure = fExperimentalMode && GetBoolArg("-" + enableArg, true);
|
||||
string strPaymentDisclosureDisabledMsg = "";
|
||||
if (!fEnablePaymentDisclosure) {
|
||||
strPaymentDisclosureDisabledMsg = experimentalDisabledHelpMsg("z_validatepaymentdisclosure", enableArg);
|
||||
|
||||
@@ -2341,6 +2341,7 @@ UniValue walletlock(const UniValue& params, bool fHelp)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
int32_t komodo_acpublic(uint32_t tiptime);
|
||||
|
||||
UniValue encryptwallet(const UniValue& params, bool fHelp)
|
||||
{
|
||||
@@ -2348,7 +2349,8 @@ UniValue encryptwallet(const UniValue& params, bool fHelp)
|
||||
return NullUniValue;
|
||||
|
||||
string enableArg = "developerencryptwallet";
|
||||
auto fEnableWalletEncryption = fExperimentalMode && GetBoolArg("-" + enableArg, false);
|
||||
int32_t flag = (komodo_acpublic(0) || ASSETCHAINS_SYMBOL[0] == 0);
|
||||
auto fEnableWalletEncryption = fExperimentalMode && GetBoolArg("-" + enableArg, flag);
|
||||
|
||||
std::string strWalletEncryptionDisabledMsg = "";
|
||||
if (!fEnableWalletEncryption) {
|
||||
@@ -2848,8 +2850,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
|
||||
|
||||
"\nExamples\n"
|
||||
+ HelpExampleCli("z_listunspent", "")
|
||||
+ HelpExampleCli("z_listunspent", "6 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"")
|
||||
+ HelpExampleRpc("z_listunspent", "6 9999999 false \"[\\\"ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9\\\",\\\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\\\"]\"")
|
||||
+ HelpExampleCli("z_listunspent", "6 9999999 false \"[\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\",\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\"]\"")
|
||||
+ HelpExampleRpc("z_listunspent", "6 9999999 false \"[\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\",\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\"]\"")
|
||||
);
|
||||
|
||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VBOOL)(UniValue::VARR));
|
||||
@@ -3685,8 +3687,8 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
|
||||
" \"change\": true|false, (boolean) true if the address that received the note is also one of the sending addresses\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"")
|
||||
+ HelpExampleRpc("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"")
|
||||
+ HelpExampleCli("z_listreceivedbyaddress", "\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"")
|
||||
+ HelpExampleRpc("z_listreceivedbyaddress", "\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
@@ -4018,8 +4020,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
||||
"\nResult:\n"
|
||||
"\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]'")
|
||||
+ HelpExampleRpc("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]")
|
||||
+ HelpExampleCli("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" '[{\"address\": \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\" ,\"amount\": 5.0}]'")
|
||||
+ HelpExampleRpc("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", [{\"address\": \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\" ,\"amount\": 5.0}]")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
@@ -4109,15 +4111,10 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
||||
}
|
||||
// If we are sending from a shielded address, all recipient
|
||||
// shielded addresses must be of the same type.
|
||||
if (fromSprout && toSapling) {
|
||||
if ((fromSprout && toSapling) || (fromSapling && toSprout)) {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
"Cannot send from a Sprout address to a Sapling address using z_sendmany");
|
||||
}
|
||||
if (fromSapling && toSprout) {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
"Cannot send from a Sapling address to a Sprout address using z_sendmany");
|
||||
"Cannot send between Sprout and Sapling addresses using z_sendmany");
|
||||
}
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address );
|
||||
@@ -4329,8 +4326,8 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
|
||||
" \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"")
|
||||
+ HelpExampleRpc("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"")
|
||||
+ HelpExampleCli("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"")
|
||||
+ HelpExampleRpc("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
@@ -4518,160 +4515,159 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
#define MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT 50
|
||||
#define MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT 10
|
||||
#define MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT 10
|
||||
#define MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT 90
|
||||
|
||||
#define JOINSPLIT_SIZE GetSerializeSize(JSDescription(), SER_NETWORK, PROTOCOL_VERSION)
|
||||
#define OUTPUTDESCRIPTION_SIZE GetSerializeSize(OutputDescription(), SER_NETWORK, PROTOCOL_VERSION)
|
||||
#define SPENDDESCRIPTION_SIZE GetSerializeSize(SpendDescription(), SER_NETWORK, PROTOCOL_VERSION)
|
||||
|
||||
UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
|
||||
string enableArg = "zmergetoaddress";
|
||||
auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, false);
|
||||
auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true);
|
||||
std::string strDisabledMsg = "";
|
||||
if (!fEnableMergeToAddress) {
|
||||
strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg);
|
||||
}
|
||||
|
||||
|
||||
if (fHelp || params.size() < 2 || params.size() > 6)
|
||||
throw runtime_error(
|
||||
"z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n"
|
||||
+ strDisabledMsg +
|
||||
"\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`"
|
||||
"\nto combine those into a single note."
|
||||
"\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they"
|
||||
"\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs."
|
||||
"\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit"
|
||||
"\nparameter is set to zero, and Overwinter is not yet active, the -mempooltxinputlimit option will determine the"
|
||||
"\nnumber of UTXOs. Any limit is constrained by the consensus rule defining a maximum transaction size of"
|
||||
+ strprintf("\n%d bytes before Sapling, and %d bytes once Sapling activates.", MAX_TX_SIZE_BEFORE_SAPLING, MAX_TX_SIZE_AFTER_SAPLING)
|
||||
+ HelpRequiringPassphrase() + "\n"
|
||||
"\nArguments:\n"
|
||||
"1. fromaddresses (string, required) A JSON array with addresses.\n"
|
||||
" The following special strings are accepted inside the array:\n"
|
||||
" - \"*\": Merge both UTXOs and notes from all addresses belonging to the wallet.\n"
|
||||
" - \"ANY_TADDR\": Merge UTXOs from all t-addrs belonging to the wallet.\n"
|
||||
" - \"ANY_ZADDR\": Merge notes from all z-addrs belonging to the wallet.\n"
|
||||
" If a special string is given, any given addresses of that type will be ignored.\n"
|
||||
" [\n"
|
||||
" \"address\" (string) Can be a t-addr or a z-addr\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"2. \"toaddress\" (string, required) The t-addr or z-addr to send the funds to.\n"
|
||||
"3. fee (numeric, optional, default="
|
||||
+ strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n"
|
||||
"4. transparent_limit (numeric, optional, default="
|
||||
+ strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use node option -mempooltxinputlimit (before Overwinter), or as many as will fit in the transaction (after Overwinter).\n"
|
||||
"4. shielded_limit (numeric, optional, default="
|
||||
+ strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n"
|
||||
"5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n"
|
||||
" \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n"
|
||||
" \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n"
|
||||
" \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n"
|
||||
" \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n"
|
||||
" \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n"
|
||||
" \"mergingNotes\": xxx (numeric) Number of notes being merged.\n"
|
||||
" \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n"
|
||||
" \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_mergetoaddress", "'[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf")
|
||||
+ HelpExampleRpc("z_mergetoaddress", "[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"], \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"")
|
||||
);
|
||||
|
||||
"z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n"
|
||||
+ strDisabledMsg +
|
||||
"\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`"
|
||||
"\nto combine those into a single note."
|
||||
"\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they"
|
||||
"\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs."
|
||||
"\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit"
|
||||
"\nparameter is set to zero, and Overwinter is not yet active, the -mempooltxinputlimit option will determine the"
|
||||
"\nnumber of UTXOs. Any limit is constrained by the consensus rule defining a maximum transaction size of"
|
||||
+ strprintf("\n%d bytes before Sapling, and %d bytes once Sapling activates.", MAX_TX_SIZE_BEFORE_SAPLING, MAX_TX_SIZE_AFTER_SAPLING)
|
||||
+ HelpRequiringPassphrase() + "\n"
|
||||
"\nArguments:\n"
|
||||
"1. fromaddresses (string, required) A JSON array with addresses.\n"
|
||||
" The following special strings are accepted inside the array:\n"
|
||||
" - \"ANY_TADDR\": Merge UTXOs from any t-addrs belonging to the wallet.\n"
|
||||
" - \"ANY_SPROUT\": Merge notes from any Sprout z-addrs belonging to the wallet.\n"
|
||||
" - \"ANY_SAPLING\": Merge notes from any Sapling z-addrs belonging to the wallet.\n"
|
||||
" If a special string is given, any given addresses of that type will be ignored.\n"
|
||||
" [\n"
|
||||
" \"address\" (string) Can be a t-addr or a z-addr\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
"2. \"toaddress\" (string, required) The t-addr or z-addr to send the funds to.\n"
|
||||
"3. fee (numeric, optional, default="
|
||||
+ strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n"
|
||||
"4. transparent_limit (numeric, optional, default="
|
||||
+ strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use node option -mempooltxinputlimit (before Overwinter), or as many as will fit in the transaction (after Overwinter).\n"
|
||||
"4. shielded_limit (numeric, optional, default="
|
||||
+ strprintf("%d Sprout or %d Sapling Notes", MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT, MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n"
|
||||
"5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n"
|
||||
" \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n"
|
||||
" \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n"
|
||||
" \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n"
|
||||
" \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n"
|
||||
" \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n"
|
||||
" \"mergingNotes\": xxx (numeric) Number of notes being merged.\n"
|
||||
" \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n"
|
||||
" \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("z_mergetoaddress", "'[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf")
|
||||
+ HelpExampleRpc("z_mergetoaddress", "[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"], \"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\"")
|
||||
);
|
||||
|
||||
if (!fEnableMergeToAddress) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_mergetoaddress is disabled.");
|
||||
}
|
||||
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
bool useAny = false;
|
||||
|
||||
bool useAnyUTXO = false;
|
||||
bool useAnyNote = false;
|
||||
bool useAnySprout = false;
|
||||
bool useAnySapling = false;
|
||||
std::set<CTxDestination> taddrs = {};
|
||||
std::set<libzcash::PaymentAddress> zaddrs = {};
|
||||
|
||||
uint32_t branchId = CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus());
|
||||
|
||||
|
||||
UniValue addresses = params[0].get_array();
|
||||
if (addresses.size()==0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, fromaddresses array is empty.");
|
||||
|
||||
|
||||
// Keep track of addresses to spot duplicates
|
||||
std::set<std::string> setAddress;
|
||||
|
||||
|
||||
// Sources
|
||||
bool containsSaplingZaddrSource = false;
|
||||
for (const UniValue& o : addresses.getValues()) {
|
||||
if (!o.isStr())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string");
|
||||
|
||||
|
||||
std::string address = o.get_str();
|
||||
if (address == "*") {
|
||||
useAny = true;
|
||||
} else if (address == "ANY_TADDR") {
|
||||
|
||||
if (address == "ANY_TADDR") {
|
||||
useAnyUTXO = true;
|
||||
} else if (address == "ANY_ZADDR") {
|
||||
useAnyNote = true;
|
||||
} else if (address == "ANY_SPROUT") {
|
||||
useAnySprout = true;
|
||||
} else if (address == "ANY_SAPLING") {
|
||||
useAnySapling = true;
|
||||
} else {
|
||||
CTxDestination taddr = DecodeDestination(address);
|
||||
if (IsValidDestination(taddr)) {
|
||||
// Ignore any listed t-addrs if we are using all of them
|
||||
if (!(useAny || useAnyUTXO)) {
|
||||
taddrs.insert(taddr);
|
||||
}
|
||||
taddrs.insert(taddr);
|
||||
} else {
|
||||
auto zaddr = DecodePaymentAddress(address);
|
||||
if (IsValidPaymentAddress(zaddr, branchId)) {
|
||||
// Ignore listed z-addrs if we are using all of them
|
||||
if (!(useAny || useAnyNote)) {
|
||||
zaddrs.insert(zaddr);
|
||||
}
|
||||
// Check if z-addr is Sapling
|
||||
bool isSapling = boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr;
|
||||
containsSaplingZaddrSource |= isSapling;
|
||||
if (IsValidPaymentAddress(zaddr)) {
|
||||
zaddrs.insert(zaddr);
|
||||
} else {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
string("Invalid parameter, unknown address format: ") + address);
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unknown address format: ") + address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (setAddress.count(address))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address);
|
||||
setAddress.insert(address);
|
||||
}
|
||||
|
||||
|
||||
if (useAnyUTXO && taddrs.size() > 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific t-addrs when using \"ANY_TADDR\"");
|
||||
}
|
||||
if ((useAnySprout || useAnySapling) && zaddrs.size() > 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SPROUT\" or \"ANY_SAPLING\"");
|
||||
}
|
||||
|
||||
const int nextBlockHeight = chainActive.Height() + 1;
|
||||
const bool overwinterActive = NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER);
|
||||
const bool saplingActive = NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING);
|
||||
|
||||
// Validate the destination address
|
||||
auto destaddress = params[1].get_str();
|
||||
bool isToZaddr = false;
|
||||
bool isToSproutZaddr = false;
|
||||
bool isToSaplingZaddr = false;
|
||||
CTxDestination taddr = DecodeDestination(destaddress);
|
||||
if (!IsValidDestination(taddr)) {
|
||||
if (IsValidPaymentAddressString(destaddress, branchId)) {
|
||||
isToZaddr = true;
|
||||
|
||||
// Is this a Sapling address?
|
||||
auto res = DecodePaymentAddress(destaddress);
|
||||
if (IsValidPaymentAddress(res)) {
|
||||
isToSaplingZaddr = boost::get<libzcash::SaplingPaymentAddress>(&res) != nullptr;
|
||||
auto decodeAddr = DecodePaymentAddress(destaddress);
|
||||
if (IsValidPaymentAddress(decodeAddr)) {
|
||||
if (boost::get<libzcash::SaplingPaymentAddress>(&decodeAddr) != nullptr) {
|
||||
isToSaplingZaddr = true;
|
||||
// If Sapling is not active, do not allow sending to a sapling addresses.
|
||||
if (!saplingActive) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated");
|
||||
}
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress );
|
||||
isToSproutZaddr = true;
|
||||
}
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress );
|
||||
}
|
||||
}
|
||||
else if ( ASSETCHAINS_PRIVATE != 0 )
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain");
|
||||
|
||||
|
||||
// Convert fee from currency format to zatoshis
|
||||
CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE;
|
||||
if (params.size() > 2) {
|
||||
@@ -4681,7 +4677,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
||||
nFee = AmountFromValue( params[2] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT;
|
||||
if (params.size() > 3) {
|
||||
nUTXOLimit = params[3].get_int();
|
||||
@@ -4689,19 +4685,22 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative");
|
||||
}
|
||||
}
|
||||
|
||||
int nNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT;
|
||||
|
||||
int sproutNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT;
|
||||
int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT;
|
||||
if (params.size() > 4) {
|
||||
nNoteLimit = params[4].get_int();
|
||||
int nNoteLimit = params[4].get_int();
|
||||
if (nNoteLimit < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative");
|
||||
}
|
||||
sproutNoteLimit = nNoteLimit;
|
||||
saplingNoteLimit = nNoteLimit;
|
||||
}
|
||||
|
||||
|
||||
std::string memo;
|
||||
if (params.size() > 5) {
|
||||
memo = params[5].get_str();
|
||||
if (!isToZaddr) {
|
||||
if (!(isToSproutZaddr || isToSaplingZaddr)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr.");
|
||||
} else if (!IsHex(memo)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format.");
|
||||
@@ -4710,75 +4709,56 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE ));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MergeToAddressRecipient recipient(destaddress, memo);
|
||||
|
||||
int nextBlockHeight = chainActive.Height() + 1;
|
||||
bool overwinterActive = NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER);
|
||||
unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING;
|
||||
if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) {
|
||||
max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING;
|
||||
}
|
||||
|
||||
// This RPC does not support Sapling yet.
|
||||
if (isToSaplingZaddr || containsSaplingZaddrSource) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling is not supported yet by z_mergetoadress");
|
||||
}
|
||||
|
||||
// If this RPC does support Sapling...
|
||||
// If Sapling is not active, do not allow sending from or sending to Sapling addresses.
|
||||
if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) {
|
||||
if (isToSaplingZaddr || containsSaplingZaddrSource) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prepare to get UTXOs and notes
|
||||
std::vector<MergeToAddressInputUTXO> utxoInputs;
|
||||
std::vector<MergeToAddressInputNote> noteInputs;
|
||||
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs;
|
||||
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs;
|
||||
CAmount mergedUTXOValue = 0;
|
||||
CAmount mergedNoteValue = 0;
|
||||
CAmount remainingUTXOValue = 0;
|
||||
CAmount remainingNoteValue = 0;
|
||||
#ifdef __LP64__
|
||||
uint64_t utxoCounter = 0;
|
||||
uint64_t noteCounter = 0;
|
||||
#else
|
||||
size_t utxoCounter = 0;
|
||||
size_t noteCounter = 0;
|
||||
#endif
|
||||
bool maxedOutUTXOsFlag = false;
|
||||
bool maxedOutNotesFlag = false;
|
||||
size_t mempoolLimit = (nUTXOLimit != 0) ? nUTXOLimit : (overwinterActive ? 0 : (size_t)GetArg("-mempooltxinputlimit", 0));
|
||||
|
||||
|
||||
unsigned int max_tx_size = saplingActive ? MAX_TX_SIZE_AFTER_SAPLING : MAX_TX_SIZE_BEFORE_SAPLING;
|
||||
size_t estimatedTxSize = 200; // tx overhead + wiggle room
|
||||
if (isToZaddr) {
|
||||
if (isToSproutZaddr) {
|
||||
estimatedTxSize += JOINSPLIT_SIZE;
|
||||
} else if (isToSaplingZaddr) {
|
||||
estimatedTxSize += OUTPUTDESCRIPTION_SIZE;
|
||||
}
|
||||
|
||||
if (useAny || useAnyUTXO || taddrs.size() > 0) {
|
||||
|
||||
if (useAnyUTXO || taddrs.size() > 0) {
|
||||
// Get available utxos
|
||||
vector<COutput> vecOutputs;
|
||||
pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false);
|
||||
|
||||
|
||||
// Find unspent utxos and update estimated size
|
||||
for (const COutput& out : vecOutputs) {
|
||||
if (!out.fSpendable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
CScript scriptPubKey = out.tx->vout[out.i].scriptPubKey;
|
||||
|
||||
CTxDestination address;
|
||||
if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
|
||||
if (!ExtractDestination(scriptPubKey, address)) {
|
||||
continue;
|
||||
}
|
||||
// If taddr is not wildcard "*", filter utxos
|
||||
if (taddrs.size() > 0 && !taddrs.count(address)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
utxoCounter++;
|
||||
CAmount nValue = out.tx->vout[out.i].nValue;
|
||||
|
||||
|
||||
if (!maxedOutUTXOsFlag) {
|
||||
size_t increase = (boost::get<CScriptID>(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE;
|
||||
if (estimatedTxSize + increase >= max_tx_size ||
|
||||
@@ -4788,34 +4768,51 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
||||
} else {
|
||||
estimatedTxSize += increase;
|
||||
COutPoint utxo(out.tx->GetHash(), out.i);
|
||||
utxoInputs.emplace_back(utxo, nValue);
|
||||
utxoInputs.emplace_back(utxo, nValue, scriptPubKey);
|
||||
mergedUTXOValue += nValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (maxedOutUTXOsFlag) {
|
||||
remainingUTXOValue += nValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useAny || useAnyNote || zaddrs.size() > 0) {
|
||||
|
||||
if (useAnySprout || useAnySapling || zaddrs.size() > 0) {
|
||||
// Get available notes
|
||||
std::vector<CSproutNotePlaintextEntry> sproutEntries;
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs);
|
||||
|
||||
|
||||
// If Sapling is not active, do not allow sending from a sapling addresses.
|
||||
if (!saplingActive && saplingEntries.size() > 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated");
|
||||
}
|
||||
// Sending from both Sprout and Sapling is currently unsupported using z_mergetoaddress
|
||||
if (sproutEntries.size() > 0 && saplingEntries.size() > 0) {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
"Cannot send from both Sprout and Sapling addresses using z_mergetoaddress");
|
||||
}
|
||||
// If sending between shielded addresses, they must be the same type
|
||||
if ((saplingEntries.size() > 0 && isToSproutZaddr) || (sproutEntries.size() > 0 && isToSaplingZaddr)) {
|
||||
throw JSONRPCError(
|
||||
RPC_INVALID_PARAMETER,
|
||||
"Cannot send between Sprout and Sapling addresses using z_mergetoaddress");
|
||||
}
|
||||
|
||||
// Find unspent notes and update estimated size
|
||||
for (CSproutNotePlaintextEntry& entry : sproutEntries) {
|
||||
for (const CSproutNotePlaintextEntry& entry : sproutEntries) {
|
||||
noteCounter++;
|
||||
CAmount nValue = entry.plaintext.value();
|
||||
|
||||
|
||||
if (!maxedOutNotesFlag) {
|
||||
// If we haven't added any notes yet and the merge is to a
|
||||
// z-address, we have already accounted for the first JoinSplit.
|
||||
size_t increase = (noteInputs.empty() && !isToZaddr) || (noteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0;
|
||||
size_t increase = (sproutNoteInputs.empty() && !isToSproutZaddr) || (sproutNoteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0;
|
||||
if (estimatedTxSize + increase >= max_tx_size ||
|
||||
(nNoteLimit > 0 && noteCounter > nNoteLimit))
|
||||
(sproutNoteLimit > 0 && noteCounter > sproutNoteLimit))
|
||||
{
|
||||
maxedOutNotesFlag = true;
|
||||
} else {
|
||||
@@ -4823,31 +4820,49 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
||||
auto zaddr = entry.address;
|
||||
SproutSpendingKey zkey;
|
||||
pwalletMain->GetSproutSpendingKey(zaddr, zkey);
|
||||
noteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey);
|
||||
sproutNoteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey);
|
||||
mergedNoteValue += nValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (maxedOutNotesFlag) {
|
||||
remainingNoteValue += nValue;
|
||||
}
|
||||
}
|
||||
|
||||
for (const SaplingNoteEntry& entry : saplingEntries) {
|
||||
noteCounter++;
|
||||
CAmount nValue = entry.note.value();
|
||||
if (!maxedOutNotesFlag) {
|
||||
size_t increase = SPENDDESCRIPTION_SIZE;
|
||||
if (estimatedTxSize + increase >= max_tx_size ||
|
||||
(saplingNoteLimit > 0 && noteCounter > saplingNoteLimit))
|
||||
{
|
||||
maxedOutNotesFlag = true;
|
||||
} else {
|
||||
estimatedTxSize += increase;
|
||||
libzcash::SaplingExtendedSpendingKey extsk;
|
||||
if (!pwalletMain->GetSaplingExtendedSpendingKey(entry.address, extsk)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Could not find spending key for payment address.");
|
||||
}
|
||||
saplingNoteInputs.emplace_back(entry.op, entry.note, nValue, extsk.expsk);
|
||||
mergedNoteValue += nValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxedOutNotesFlag) {
|
||||
remainingNoteValue += nValue;
|
||||
}
|
||||
}
|
||||
// TODO: Add Sapling support
|
||||
}
|
||||
|
||||
#ifdef __LP64__
|
||||
uint64_t numUtxos = utxoInputs.size(); //ca333
|
||||
uint64_t numNotes = noteInputs.size();
|
||||
#else
|
||||
|
||||
size_t numUtxos = utxoInputs.size();
|
||||
size_t numNotes = noteInputs.size();
|
||||
#endif
|
||||
|
||||
|
||||
size_t numNotes = sproutNoteInputs.size() + saplingNoteInputs.size();
|
||||
|
||||
if (numUtxos == 0 && numNotes == 0) {
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any funds to merge.");
|
||||
}
|
||||
|
||||
|
||||
// Sanity check: Don't do anything if:
|
||||
// - We only have one from address
|
||||
// - It's equal to toaddress
|
||||
@@ -4855,42 +4870,47 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
|
||||
if (setAddress.size() == 1 && setAddress.count(destaddress) && (numUtxos + numNotes) == 1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged.");
|
||||
}
|
||||
|
||||
|
||||
CAmount mergedValue = mergedUTXOValue + mergedNoteValue;
|
||||
if (mergedValue < nFee) {
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
|
||||
strprintf("Insufficient funds, have %s, which is less than miners fee %s",
|
||||
FormatMoney(mergedValue), FormatMoney(nFee)));
|
||||
strprintf("Insufficient funds, have %s, which is less than miners fee %s",
|
||||
FormatMoney(mergedValue), FormatMoney(nFee)));
|
||||
}
|
||||
|
||||
|
||||
// Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee)
|
||||
CAmount netAmount = mergedValue - nFee;
|
||||
if (nFee > netAmount) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount)));
|
||||
}
|
||||
|
||||
|
||||
// Keep record of parameters in context object
|
||||
UniValue contextInfo(UniValue::VOBJ);
|
||||
contextInfo.push_back(Pair("fromaddresses", params[0]));
|
||||
contextInfo.push_back(Pair("toaddress", params[1]));
|
||||
contextInfo.push_back(Pair("fee", ValueFromAmount(nFee)));
|
||||
|
||||
|
||||
// Contextual transaction we will build on
|
||||
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(
|
||||
Params().GetConsensus(),
|
||||
nextBlockHeight);
|
||||
bool isShielded = numNotes > 0 || isToZaddr;
|
||||
if (contextualTx.nVersion == 1 && isShielded) {
|
||||
Params().GetConsensus(),
|
||||
nextBlockHeight);
|
||||
bool isSproutShielded = sproutNoteInputs.size() > 0 || isToSproutZaddr;
|
||||
if (contextualTx.nVersion == 1 && isSproutShielded) {
|
||||
contextualTx.nVersion = 2; // Tx format should support vjoinsplit
|
||||
}
|
||||
|
||||
|
||||
// Builder (used if Sapling addresses are involved)
|
||||
boost::optional<TransactionBuilder> builder;
|
||||
if (isToSaplingZaddr || saplingNoteInputs.size() > 0) {
|
||||
builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain);
|
||||
}
|
||||
// Create operation and add to global queue
|
||||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
|
||||
std::shared_ptr<AsyncRPCOperation> operation(
|
||||
new AsyncRPCOperation_mergetoaddress(contextualTx, utxoInputs, noteInputs, recipient, nFee, contextInfo) );
|
||||
new AsyncRPCOperation_mergetoaddress(builder, contextualTx, utxoInputs, sproutNoteInputs, saplingNoteInputs, recipient, nFee, contextInfo) );
|
||||
q->addOperation(operation);
|
||||
AsyncRPCOperationId operationId = operation->getId();
|
||||
|
||||
|
||||
// Return continuation information
|
||||
UniValue o(UniValue::VOBJ);
|
||||
o.push_back(Pair("remainingUTXOs", static_cast<uint64_t>(utxoCounter - numUtxos)));
|
||||
|
||||
@@ -4960,10 +4960,10 @@ void CWallet::GetFilteredNotes(
|
||||
}
|
||||
|
||||
// skip locked notes
|
||||
// TODO: Add locking for Sapling notes
|
||||
// if (ignoreLocked && IsLockedNote(op)) {
|
||||
// continue;
|
||||
// }
|
||||
// TODO: Add locking for Sapling notes -> done
|
||||
if (ignoreLocked && IsLockedNote(op)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto note = notePt.note(nd.ivk).get();
|
||||
saplingEntries.push_back(SaplingNoteEntry {
|
||||
|
||||
Reference in New Issue
Block a user