@@ -261,7 +261,6 @@ BITCOIN_CORE_H = \
|
||||
wallet/wallet.h \
|
||||
wallet/wallet_ismine.h \
|
||||
wallet/walletdb.h \
|
||||
veruslaunch.h \
|
||||
zmq/zmqabstractnotifier.h \
|
||||
zmq/zmqconfig.h\
|
||||
zmq/zmqnotificationinterface.h \
|
||||
@@ -483,7 +482,6 @@ libbitcoin_common_a_SOURCES = \
|
||||
script/script_error.cpp \
|
||||
script/sign.cpp \
|
||||
script/standard.cpp \
|
||||
veruslaunch.cpp \
|
||||
transaction_builder.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
@@ -10,7 +10,6 @@ komodo_test_SOURCES = \
|
||||
test-komodo/test_coinimport.cpp \
|
||||
test-komodo/test_eval_bet.cpp \
|
||||
test-komodo/test_eval_notarisation.cpp \
|
||||
test-komodo/test_crosschain.cpp \
|
||||
test-komodo/test_parse_notarisation.cpp
|
||||
|
||||
komodo_test_CPPFLAGS = $(komodod_CPPFLAGS)
|
||||
|
||||
@@ -11,7 +11,7 @@ if [ -z "$delay" ]; then delay=20; fi
|
||||
./listassetchainparams | while read args; do
|
||||
gen=""
|
||||
if [ $[RANDOM % 10] == 1 ]; then
|
||||
gen=" -gen"
|
||||
gen=" -gen -genproclimit=1"
|
||||
fi
|
||||
|
||||
./komodod $gen $args $overide_args -pubkey=$pubkey -addnode=$seed_ip &
|
||||
|
||||
@@ -62,8 +62,26 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
|
||||
GetCCaddress(cp,myaddr,mypk);
|
||||
mycond = MakeCCcond1(cp->evalcode,mypk);
|
||||
GetCCaddress(cp,unspendable,unspendablepk);
|
||||
othercond = MakeCCcond1(cp->evalcode,unspendablepk);
|
||||
//fprintf(stderr,"myCCaddr.(%s) %p vs unspendable.(%s) %p\n",myaddr,mycond,unspendable,othercond);
|
||||
othercond = MakeCCcond1(cp->evalcode,unspendablepk);
|
||||
//Reorder vins so that for multiple normal vins all other except vin0 goes to the end
|
||||
//This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation.
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
if ( GetTransaction(mtx.vin[i].prevout.hash,vintx,hashBlock,false) != 0 )
|
||||
{
|
||||
if ( vintx.vout[mtx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition() == 0 && ccvins==0)
|
||||
normalvins++;
|
||||
else ccvins++;
|
||||
}
|
||||
}
|
||||
if (normalvins>1 && ccvins)
|
||||
{
|
||||
for(i=1;i<normalvins;i++)
|
||||
{
|
||||
mtx.vin.push_back(mtx.vin[1]);
|
||||
mtx.vin.erase(mtx.vin.begin() + 1);
|
||||
}
|
||||
}
|
||||
memset(utxovalues,0,sizeof(utxovalues));
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
@@ -74,14 +92,12 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
|
||||
totalinputs += utxovalues[i];
|
||||
if ( vintx.vout[utxovout].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
{
|
||||
//fprintf(stderr,"vin.%d is normal %.8f\n",i,(double)utxovalues[i]/COIN);
|
||||
if (ccvins==0) normalvins++;
|
||||
//fprintf(stderr,"vin.%d is normal %.8f\n",i,(double)utxovalues[i]/COIN);
|
||||
normalinputs += utxovalues[i];
|
||||
vinimask |= (1LL << i);
|
||||
}
|
||||
else
|
||||
{
|
||||
ccvins++;
|
||||
{
|
||||
mask |= (1LL << i);
|
||||
}
|
||||
} else fprintf(stderr,"FinalizeCCTx couldnt find %s\n",mtx.vin[i].prevout.hash.ToString().c_str());
|
||||
@@ -98,16 +114,6 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
|
||||
mtx.vout.push_back(CTxOut(0,opret));
|
||||
PrecomputedTransactionData txdata(mtx);
|
||||
n = mtx.vin.size();
|
||||
//Reorder vins so that for multiple normal vins all other except vin0 goes to the end
|
||||
//This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation.
|
||||
if (normalvins>1)
|
||||
{
|
||||
for(i=1;i<normalvins;i++)
|
||||
{
|
||||
mtx.vin.push_back(mtx.vin[1]);
|
||||
mtx.vin.erase(mtx.vin.begin() + 1);
|
||||
}
|
||||
}
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
if ( GetTransaction(mtx.vin[i].prevout.hash,vintx,hashBlock,false) != 0 )
|
||||
|
||||
@@ -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:
|
||||
@@ -192,17 +192,6 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get MoMoM corresponding to a notarisation tx hash (on assetchain)
|
||||
*/
|
||||
bool Eval::GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const
|
||||
{
|
||||
std::pair<uint256,NotarisationData> out;
|
||||
if (!GetNextBacknotarisation(kmdNotarisationHash, out)) return false;
|
||||
momom = out.second.MoMoM;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Eval::GetAssetchainsCC() const
|
||||
{
|
||||
|
||||
@@ -104,7 +104,6 @@ public:
|
||||
virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const;
|
||||
virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const;
|
||||
virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const;
|
||||
virtual bool GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const;
|
||||
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
|
||||
virtual uint32_t GetAssetchainsCC() const;
|
||||
virtual std::string GetAssetchainsSymbol() const;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "cc/eval.h"
|
||||
#include "cc/utils.h"
|
||||
#include "importcoin.h"
|
||||
#include "crosschain.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
@@ -75,12 +76,8 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &imp
|
||||
|
||||
// Check proof confirms existance of burnTx
|
||||
{
|
||||
uint256 momom, target;
|
||||
if (!GetProofRoot(proof.first, momom))
|
||||
return Invalid("coudnt-load-momom");
|
||||
|
||||
target = proof.second.Exec(burnTx.GetHash());
|
||||
if (momom != proof.second.Exec(burnTx.GetHash()))
|
||||
uint256 target = proof.second.Exec(burnTx.GetHash());
|
||||
if (!CheckMoMoM(proof.first, target))
|
||||
return Invalid("momom-check-fail");
|
||||
}
|
||||
|
||||
|
||||
@@ -557,7 +557,7 @@ extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
||||
const CScript &CCoinsViewCache::GetSpendFor(const CCoins *coins, const CTxIn& input)
|
||||
{
|
||||
assert(coins);
|
||||
if (coins->nHeight < 6400 && !strcmp(ASSETCHAINS_SYMBOL, "VRSC"))
|
||||
/*if (coins->nHeight < 6400 && !strcmp(ASSETCHAINS_SYMBOL, "VRSC"))
|
||||
{
|
||||
std::string hc = input.prevout.hash.ToString();
|
||||
if (LaunchMap().lmap.count(hc))
|
||||
@@ -568,7 +568,7 @@ const CScript &CCoinsViewCache::GetSpendFor(const CCoins *coins, const CTxIn& in
|
||||
return txData.scriptPubKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
return coins->vout[input.prevout.n].scriptPubKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "zcash/IncrementalMerkleTree.hpp"
|
||||
#include "veruslaunch.h"
|
||||
//#include "veruslaunch.h"
|
||||
|
||||
/**
|
||||
* Pruned version of CTransaction: only retains metadata and unspent transaction outputs
|
||||
@@ -456,7 +456,7 @@ class CTransactionExceptionData
|
||||
CTransactionExceptionData() : scriptPubKey(), voutMask() {}
|
||||
};
|
||||
|
||||
class CLaunchMap
|
||||
/*class CLaunchMap
|
||||
{
|
||||
public:
|
||||
std::unordered_map<std::string, CTransactionExceptionData> lmap;
|
||||
@@ -477,7 +477,7 @@ class CLaunchMap
|
||||
}
|
||||
}
|
||||
};
|
||||
static CLaunchMap launchMap = CLaunchMap();
|
||||
static CLaunchMap launchMap = CLaunchMap();*/
|
||||
|
||||
/** CCoinsView that adds a memory cache for transactions to another CCoinsView */
|
||||
class CCoinsViewCache : public CCoinsViewBacked
|
||||
@@ -507,7 +507,7 @@ public:
|
||||
~CCoinsViewCache();
|
||||
|
||||
// Standard CCoinsView methods
|
||||
static CLaunchMap &LaunchMap() { return launchMap; }
|
||||
//static CLaunchMap &LaunchMap() { return launchMap; }
|
||||
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const;
|
||||
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const;
|
||||
bool GetNullifier(const uint256 &nullifier, ShieldedType type) const;
|
||||
|
||||
@@ -69,7 +69,7 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh
|
||||
destNotarisationTxid = nota.first;
|
||||
else if (seenOwnNotarisations == 2)
|
||||
goto end;
|
||||
break;
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +96,9 @@ template <typename IsTarget>
|
||||
int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &found)
|
||||
{
|
||||
int limit = std::min(nHeight + NOTARISATION_SCAN_LIMIT_BLOCKS, chainActive.Height());
|
||||
|
||||
for (int h=nHeight; h<limit; h++) {
|
||||
int start = std::max(nHeight, 1);
|
||||
|
||||
for (int h=start; h<limit; h++) {
|
||||
NotarisationsInBlock notarisations;
|
||||
|
||||
if (!GetBlockNotarisations(*chainActive[h]->phashBlock, notarisations))
|
||||
@@ -125,7 +126,7 @@ TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_
|
||||
*/
|
||||
EvalRef eval;
|
||||
uint256 MoM = assetChainProof.second.Exec(txid);
|
||||
|
||||
|
||||
// Get a kmd height for given notarisation Txid
|
||||
int kmdHeight;
|
||||
{
|
||||
@@ -157,7 +158,7 @@ TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_
|
||||
uint256 MoMoM = CalculateProofRoot(targetSymbol, targetCCid, kmdHeight, moms, targetChainNotarisationTxid);
|
||||
if (MoMoM.IsNull())
|
||||
throw std::runtime_error("No MoMs found");
|
||||
|
||||
|
||||
// Find index of source MoM in MoMoM
|
||||
int nIndex;
|
||||
for (nIndex=0; nIndex<moms.size(); nIndex++) {
|
||||
@@ -246,6 +247,38 @@ bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out)
|
||||
}
|
||||
|
||||
|
||||
bool CheckMoMoM(uint256 kmdNotarisationHash, uint256 momom)
|
||||
{
|
||||
/*
|
||||
* Given a notarisation hash and an MoMoM. Backnotarisations may arrive out of order
|
||||
* or multiple in the same block. So dereference the notarisation hash to the corresponding
|
||||
* backnotarisation and scan around the kmdheight to see if the MoMoM is a match.
|
||||
* This is a sledgehammer approach...
|
||||
*/
|
||||
|
||||
Notarisation bn;
|
||||
if (!GetBackNotarisation(kmdNotarisationHash, bn))
|
||||
return false;
|
||||
|
||||
// Need to get block height of that backnotarisation
|
||||
EvalRef eval;
|
||||
CBlockIndex block;
|
||||
CTransaction tx;
|
||||
if (!eval->GetTxConfirmed(bn.first, tx, block)){
|
||||
fprintf(stderr, "Can't get height of backnotarisation, this should not happen\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Notarisation nota;
|
||||
auto checkMoMoM = [&](Notarisation ¬a) {
|
||||
return nota.second.MoMoM == momom;
|
||||
};
|
||||
|
||||
return (bool) ScanNotarisationsFromHeight(block.GetHeight()-100, checkMoMoM, nota);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* On assetchain
|
||||
* in: txid
|
||||
@@ -278,7 +311,7 @@ TxProof GetAssetchainProof(uint256 hash)
|
||||
};
|
||||
if (!ScanNotarisationsFromHeight(blockIndex->GetHeight(), isTarget, nota))
|
||||
throw std::runtime_error("backnotarisation not yet confirmed");
|
||||
|
||||
|
||||
// index of block in MoM leaves
|
||||
nIndex = nota.second.height - blockIndex->GetHeight();
|
||||
}
|
||||
@@ -292,7 +325,7 @@ TxProof GetAssetchainProof(uint256 hash)
|
||||
}
|
||||
bool fMutated;
|
||||
BuildMerkleTree(&fMutated, leaves, tree);
|
||||
branch = GetMerkleBranch(nIndex, leaves.size(), tree);
|
||||
branch = GetMerkleBranch(nIndex, leaves.size(), tree);
|
||||
|
||||
// Check branch
|
||||
uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex);
|
||||
@@ -331,7 +364,7 @@ TxProof GetAssetchainProof(uint256 hash)
|
||||
}
|
||||
|
||||
// Check the proof
|
||||
if (nota.second.MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
|
||||
if (nota.second.MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
|
||||
throw std::runtime_error("Failed validating MoM");
|
||||
|
||||
// All done!
|
||||
|
||||
@@ -15,7 +15,7 @@ TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_
|
||||
void CompleteImportTransaction(CTransaction &importTx);
|
||||
|
||||
/* On assetchain */
|
||||
bool GetNextBacknotarisation(uint256 txid, std::pair<uint256,NotarisationData> &bn);
|
||||
bool CheckMoMoM(uint256 kmdNotarisationHash, uint256 momom);
|
||||
|
||||
|
||||
#endif /* CROSSCHAIN_H */
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-10-14.11; # UTC
|
||||
scriptversion=2018-03-07.03; # UTC
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,7 +17,7 @@ scriptversion=2012-10-14.11; # UTC
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
@@ -255,7 +255,8 @@ EOF
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
|
||||
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
@@ -339,9 +340,9 @@ exit $ret
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
||||
@@ -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;
|
||||
|
||||
31
src/main.cpp
31
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"),
|
||||
@@ -1725,7 +1731,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
if (pfMissingInputs)
|
||||
*pfMissingInputs = true;
|
||||
//fprintf(stderr,"missing inputs\n");
|
||||
return state.DoS(0, error("AcceptToMemoryPool: tx inputs not found"),REJECT_INVALID, "bad-txns-inputs-missing");
|
||||
//return state.DoS(0, error("AcceptToMemoryPool: tx inputs not found"),REJECT_INVALID, "bad-txns-inputs-missing");
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1973,7 +1980,7 @@ bool myAddtomempool(CTransaction &tx, CValidationState *pstate)
|
||||
pstate = &state;
|
||||
CTransaction Ltx; bool fMissingInputs,fOverrideFees = false;
|
||||
if ( mempool.lookup(tx.GetHash(),Ltx) == 0 )
|
||||
return(AcceptToMemoryPool(mempool, *pstate, tx, false, &fMissingInputs, !fOverrideFees));
|
||||
return(AcceptToMemoryPool(mempool, *pstate, tx, true, &fMissingInputs, !fOverrideFees));
|
||||
else return(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -737,7 +737,7 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isStake)
|
||||
//if ( !isStake || ASSETCHAINS_STAKED != 0 )
|
||||
{
|
||||
if (!reservekey.GetReservedKey(pubkey))
|
||||
{
|
||||
|
||||
@@ -136,7 +136,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "z_shieldcoinbase", 3},
|
||||
{ "z_getoperationstatus", 0},
|
||||
{ "z_getoperationresult", 0},
|
||||
{ "z_importkey", 1 },
|
||||
{ "paxprice", 4 },
|
||||
{ "paxprices", 3 },
|
||||
{ "paxpending", 0 },
|
||||
|
||||
@@ -56,7 +56,7 @@ extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
||||
uint32_t komodo_segid32(char *coinaddr);
|
||||
int64_t komodo_coinsupply(int64_t *zfundsp,int32_t height);
|
||||
int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp);
|
||||
#define KOMODO_VERSION "0.3.2"
|
||||
#define KOMODO_VERSION "0.3.0"
|
||||
#define VERUS_VERSION "0.4.0g"
|
||||
extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT;
|
||||
extern uint32_t ASSETCHAINS_CC;
|
||||
|
||||
@@ -1274,7 +1274,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
|
||||
// push to local node and sync with wallets
|
||||
CValidationState state;
|
||||
bool fMissingInputs;
|
||||
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) {
|
||||
if (!AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, !fOverrideFees)) {
|
||||
if (state.IsInvalid()) {
|
||||
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
|
||||
} else {
|
||||
|
||||
@@ -33,7 +33,10 @@ extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue&
|
||||
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
|
||||
int32_t komodo_longestchain();
|
||||
int32_t komodo_dpowconfs(int32_t height,int32_t numconfs);
|
||||
<<<<<<< HEAD:src/rpcblockchain.old
|
||||
extern int32_t KOMODO_LONGESTCHAIN;
|
||||
=======
|
||||
>>>>>>> master:src/rpcblockchain.cpp
|
||||
|
||||
double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficulty)
|
||||
{
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
#include <zmq.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "cc/eval.h"
|
||||
#include "importcoin.h"
|
||||
#include "base58.h"
|
||||
#include "core_io.h"
|
||||
#include "crosschain.h"
|
||||
#include "key.h"
|
||||
#include "komodo_structs.h"
|
||||
#include "main.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/cc.h"
|
||||
#include "script/interpreter.h"
|
||||
#include "script/serverchecker.h"
|
||||
#include "txmempool.h"
|
||||
#include "crosschain.h"
|
||||
|
||||
#include "testutils.h"
|
||||
|
||||
|
||||
extern uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth);
|
||||
extern bool KOMODO_TEST_ASSETCHAIN_SKIP_POW;
|
||||
|
||||
|
||||
/*
|
||||
* Tests for the whole process of creating and validating notary proofs
|
||||
* using proof roots (MoMoMs). This is to support coin imports.
|
||||
*/
|
||||
|
||||
namespace TestCrossChainProof {
|
||||
|
||||
|
||||
class TestCrossChain : public ::testing::Test, public Eval {
|
||||
public:
|
||||
bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
|
||||
{
|
||||
NotarisationData data(2);
|
||||
return ParseNotarisationOpReturn(tx, data); // If it parses it's valid
|
||||
}
|
||||
protected:
|
||||
static void SetUpTestCase() { }
|
||||
virtual void SetUp() {
|
||||
KOMODO_TEST_ASSETCHAIN_SKIP_POW = 1;
|
||||
ASSETCHAINS_CC = 1;
|
||||
EVAL_TEST = this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
uint256 endianHash(uint256 h)
|
||||
{
|
||||
uint256 out;
|
||||
for (int i=0; i<32; i++) {
|
||||
out.begin()[31-i] = h.begin()[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestCrossChain, testCreateAndValidateImportProof)
|
||||
{
|
||||
/*
|
||||
* This tests the full process of creation of a cross chain proof.
|
||||
* For the purposes of the test we will use one assetchain and a KMD chain.
|
||||
*
|
||||
* In order to do this test, we need 2 blockchains, so we'll fork and make a socket
|
||||
* for IPC.
|
||||
*/
|
||||
|
||||
int childPid = fork();
|
||||
void *ctx = zmq_ctx_new();
|
||||
void *socket = zmq_socket(ctx, ZMQ_PAIR);
|
||||
if (!childPid)
|
||||
strcpy(ASSETCHAINS_SYMBOL, "PIZZA");
|
||||
setupChain();
|
||||
std::vector<CBlock> blocks;
|
||||
blocks.resize(1000);
|
||||
NotarisationData a2kmd(0), kmd2a(1);
|
||||
int numTestNotarisations = 10;
|
||||
|
||||
|
||||
auto SendIPC = [&] (std::vector<uint8_t> v) {
|
||||
assert(v.size() == zmq_send(socket, v.data(), v.size(), 0));
|
||||
};
|
||||
|
||||
auto RecvIPC = [&] () {
|
||||
std::vector<uint8_t> out;
|
||||
out.resize(100000);
|
||||
int len = zmq_recv(socket, out.data(), out.size(), 0);
|
||||
assert(len != -1);
|
||||
out.resize(len);
|
||||
return out;
|
||||
};
|
||||
|
||||
auto RecordNotarisation = [&] (CTransaction inputTx, NotarisationData data) {
|
||||
CMutableTransaction mtx = spendTx(inputTx);
|
||||
mtx.vout.resize(2);
|
||||
mtx.vout[0].scriptPubKey << VCH(notaryKey.GetPubKey().begin(), 33) << OP_CHECKSIG;
|
||||
mtx.vout[1].scriptPubKey << OP_RETURN << E_MARSHAL(ss << data);
|
||||
mtx.vout[1].nValue = 0;
|
||||
mtx.vin[0].scriptSig << getSig(mtx, inputTx.vout[0].scriptPubKey);
|
||||
|
||||
acceptTxFail(CTransaction(mtx));
|
||||
return mtx.GetHash();
|
||||
};
|
||||
|
||||
auto RunTestAssetchain = [&] ()
|
||||
{
|
||||
NotarisationData n(0), back(1);
|
||||
strcpy(n.symbol, "PIZZA");
|
||||
n.ccId = 2;
|
||||
int height = 0;
|
||||
|
||||
/*
|
||||
* Send notarisations and write backnotarisations
|
||||
*/
|
||||
for (int ni=0; ni<numTestNotarisations; ni++)
|
||||
{
|
||||
generateBlock(&blocks[++height]);
|
||||
generateBlock(&blocks[++height]);
|
||||
n.blockHash = blocks[height].GetHash();
|
||||
n.MoM = endianHash(komodo_calcMoM(n.height=height, n.MoMDepth=2));
|
||||
SendIPC(E_MARSHAL(ss << n));
|
||||
assert(E_UNMARSHAL(RecvIPC(), ss >> back));
|
||||
RecordNotarisation(blocks[height].vtx[0], back);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test a proof
|
||||
*/
|
||||
uint256 txid = blocks[7].vtx[0].GetHash();
|
||||
TxProof proof = GetAssetchainProof(txid);
|
||||
SendIPC(E_MARSHAL(ss << txid; ss << proof));
|
||||
E_UNMARSHAL(RecvIPC(), ss >> proof);
|
||||
|
||||
std::pair<uint256,NotarisationData> bn;
|
||||
if (!GetNextBacknotarisation(proof.first, bn)) {
|
||||
printf("GetNextBackNotarisation failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (proof.second.Exec(txid) != bn.second.MoMoM) {
|
||||
printf("MoMom incorrect\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto RunTestKmd = [&] ()
|
||||
{
|
||||
NotarisationData n(0);
|
||||
int height = 0;
|
||||
|
||||
/*
|
||||
* Write notarisations and send backnotarisations
|
||||
*/
|
||||
for (int ni=0; ni<numTestNotarisations; ni++)
|
||||
{
|
||||
n.IsBackNotarisation = 0;
|
||||
E_UNMARSHAL(RecvIPC(), ss >> n);
|
||||
// Grab a coinbase input to fund notarisation
|
||||
generateBlock(&blocks[++height]);
|
||||
n.txHash = RecordNotarisation(blocks[height].vtx[0], n);
|
||||
{
|
||||
std::vector<uint256> moms;
|
||||
uint256 destNotarisationTxid;
|
||||
n.MoMoM = CalculateProofRoot(n.symbol, 2, height, moms, destNotarisationTxid);
|
||||
}
|
||||
n.IsBackNotarisation = 1;
|
||||
SendIPC(E_MARSHAL(ss << n));
|
||||
}
|
||||
|
||||
/*
|
||||
* Extend proof
|
||||
*/
|
||||
TxProof proof;
|
||||
uint256 txid;
|
||||
// Extend proof to MoMoM
|
||||
assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> proof));
|
||||
proof = GetCrossChainProof(txid, (char*)"PIZZA", 2, proof);
|
||||
SendIPC(E_MARSHAL(ss << proof));
|
||||
};
|
||||
|
||||
const char endpoint[] = "ipc://tmpKomodoTestCrossChainSock";
|
||||
|
||||
if (!childPid) {
|
||||
assert(0 == zmq_connect(socket, endpoint));
|
||||
usleep(20000);
|
||||
int out = RunTestAssetchain();
|
||||
if (!out) printf("Assetchain success\n");
|
||||
exit(out);
|
||||
}
|
||||
else {
|
||||
assert(0 == zmq_bind(socket, endpoint));
|
||||
RunTestKmd();
|
||||
int returnStatus;
|
||||
waitpid(childPid, &returnStatus, 0);
|
||||
unlink("tmpKomodoTestCrossChainSock");
|
||||
ASSERT_EQ(0, returnStatus);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} /* namespace TestCrossChainProof */
|
||||
@@ -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())
|
||||
|
||||
1424
src/veruslaunch.cpp
1424
src/veruslaunch.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,17 +0,0 @@
|
||||
|
||||
// Copyright (c) 2018 The Verus developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef VERUS_LAUNCH_H
|
||||
#define VERUS_LAUNCH_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#define WHITELIST_COUNT 704
|
||||
|
||||
extern const char *whitelist_ids[WHITELIST_COUNT];
|
||||
extern const char *whitelist_address;
|
||||
extern uint64_t whitelist_masks[WHITELIST_COUNT];
|
||||
|
||||
#endif
|
||||
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);
|
||||
|
||||
@@ -111,7 +111,6 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
|
||||
entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
|
||||
entry.push_back(Pair("expiryheight", (int64_t)wtx.nExpiryHeight));
|
||||
} else entry.push_back(Pair("confirmations", confirms));
|
||||
|
||||
uint256 hash = wtx.GetHash();
|
||||
entry.push_back(Pair("txid", hash.GetHex()));
|
||||
UniValue conflicts(UniValue::VARR);
|
||||
@@ -2444,6 +2443,7 @@ UniValue walletlock(const UniValue& params, bool fHelp)
|
||||
return NullUniValue;
|
||||
}
|
||||
|
||||
int32_t komodo_acpublic(uint32_t tiptime);
|
||||
|
||||
UniValue encryptwallet(const UniValue& params, bool fHelp)
|
||||
{
|
||||
@@ -2451,7 +2451,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) {
|
||||
@@ -2951,8 +2952,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));
|
||||
@@ -3788,8 +3789,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);
|
||||
@@ -4121,8 +4122,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);
|
||||
@@ -4212,15 +4213,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 );
|
||||
@@ -4432,8 +4428,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);
|
||||
@@ -4621,160 +4617,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) {
|
||||
@@ -4784,7 +4779,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();
|
||||
@@ -4792,19 +4787,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.");
|
||||
@@ -4813,75 +4811,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 ||
|
||||
@@ -4891,34 +4870,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 {
|
||||
@@ -4926,31 +4922,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
|
||||
@@ -4958,42 +4972,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)));
|
||||
|
||||
@@ -3615,7 +3615,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
||||
if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) {
|
||||
max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING;
|
||||
}
|
||||
|
||||
/*
|
||||
// Discourage fee sniping.
|
||||
//
|
||||
// However because of a off-by-one-error in previous versions we need to
|
||||
@@ -3636,7 +3636,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
||||
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));
|
||||
|
||||
assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
|
||||
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
|
||||
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);*/
|
||||
|
||||
{
|
||||
LOCK2(cs_main, cs_wallet);
|
||||
@@ -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 {
|
||||
|
||||
@@ -46,7 +46,7 @@ extern bool fSendFreeTransactions;
|
||||
extern bool fPayAtLeastCustomFee;
|
||||
|
||||
//! -paytxfee default
|
||||
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
|
||||
static const CAmount DEFAULT_TRANSACTION_FEE = 10000;
|
||||
//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB
|
||||
static const CAmount nHighTransactionFeeWarning = 0.01 * COIN;
|
||||
//! -maxtxfee default
|
||||
|
||||
Reference in New Issue
Block a user