Merge branch 'blackjok3rtt-FSM'

a
This commit is contained in:
blackjok3r
2018-12-17 12:41:07 +08:00
14 changed files with 195 additions and 510 deletions

2
.gitignore vendored
View File

@@ -124,3 +124,5 @@ src/komodo-tx.exe
#output during builds, symbol tables? #output during builds, symbol tables?
*.dSYM *.dSYM
src/cryptoconditions/compile

View File

@@ -12,7 +12,7 @@ This tool converts Sprout zaddress funds into Sapling funds in a new Sapling add
### Usage ### Usage
./zmigrate zsaplingaddr ./zmigrate COIN zsaplingaddr
The above command may need to be run multiple times to complete the process. The above command may need to be run multiple times to complete the process.

View File

@@ -940,6 +940,11 @@ again:
if ( amount == lastamount3 && amount == lastamount4 ) if ( amount == lastamount3 && amount == lastamount4 )
stdamount /= 10; stdamount /= 10;
} }
if ( stdamount < SATOSHIDEN )
{
stdamount = SATOSHIDEN;
refamount = SATOSHIDEN * 50;
}
if ( stdamount < refamount ) if ( stdamount < refamount )
refamount = stdamount; refamount = stdamount;
lastamount4 = lastamount3; lastamount4 = lastamount3;

View File

@@ -219,7 +219,7 @@ public:
CBlockIndex* pskip; CBlockIndex* pskip;
//! height of the entry in the chain. The genesis block has height 0 //! height of the entry in the chain. The genesis block has height 0
int64_t newcoins,zfunds; int8_t segid; // jl777 fields int64_t newcoins,zfunds,sproutfunds; int8_t segid; // jl777 fields
//! Which # file this block is stored in (blk?????.dat) //! Which # file this block is stored in (blk?????.dat)
int nFile; int nFile;

View File

@@ -1,348 +0,0 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2018-03-07.03; # UTC
# 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
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# 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 <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
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

View File

@@ -1849,9 +1849,9 @@ int32_t komodo_acpublic(uint32_t tiptime)
return(acpublic); return(acpublic);
} }
int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock) int64_t komodo_newcoins(int64_t *zfundsp,int64_t *sproutfundsp,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; CTxDestination address; int32_t i,j,m,n,vout; uint8_t *script; uint256 txid,hashBlock; int64_t zfunds=0,vinsum=0,voutsum=0,sproutfunds=0;
n = pblock->vtx.size(); n = pblock->vtx.size();
for (i=0; i<n; i++) for (i=0; i<n; i++)
{ {
@@ -1891,10 +1891,13 @@ int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock)
{ {
zfunds -= joinsplit.vpub_new; zfunds -= joinsplit.vpub_new;
zfunds += joinsplit.vpub_old; zfunds += joinsplit.vpub_old;
sproutfunds -= joinsplit.vpub_new;
sproutfunds += joinsplit.vpub_old;
} }
zfunds -= tx.valueBalance; zfunds -= tx.valueBalance;
} }
*zfundsp = zfunds; *zfundsp = zfunds;
*sproutfundsp = sproutfunds;
if ( ASSETCHAINS_SYMBOL[0] == 0 && (voutsum-vinsum) == 100003*SATOSHIDEN ) // 15 times if ( ASSETCHAINS_SYMBOL[0] == 0 && (voutsum-vinsum) == 100003*SATOSHIDEN ) // 15 times
return(3 * SATOSHIDEN); return(3 * SATOSHIDEN);
//if ( voutsum-vinsum+zfunds > 100000*SATOSHIDEN || voutsum-vinsum+zfunds < 0 ) //if ( voutsum-vinsum+zfunds > 100000*SATOSHIDEN || voutsum-vinsum+zfunds < 0 )
@@ -1902,11 +1905,11 @@ int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock)
return(voutsum - vinsum); return(voutsum - vinsum);
} }
int64_t komodo_coinsupply(int64_t *zfundsp,int32_t height) int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height)
{ {
CBlockIndex *pindex; CBlock block; int64_t zfunds=0,supply = 0; CBlockIndex *pindex; CBlock block; int64_t zfunds=0,sproutfunds=0,supply = 0;
//fprintf(stderr,"coinsupply %d\n",height); //fprintf(stderr,"coinsupply %d\n",height);
*zfundsp = 0; *zfundsp = *sproutfundsp = 0;
if ( (pindex= komodo_chainactive(height)) != 0 ) if ( (pindex= komodo_chainactive(height)) != 0 )
{ {
while ( pindex != 0 && pindex->GetHeight() > 0 ) while ( pindex != 0 && pindex->GetHeight() > 0 )
@@ -1914,7 +1917,7 @@ int64_t komodo_coinsupply(int64_t *zfundsp,int32_t height)
if ( pindex->newcoins == 0 && pindex->zfunds == 0 ) if ( pindex->newcoins == 0 && pindex->zfunds == 0 )
{ {
if ( komodo_blockload(block,pindex) == 0 ) if ( komodo_blockload(block,pindex) == 0 )
pindex->newcoins = komodo_newcoins(&pindex->zfunds,pindex->GetHeight(),&block); pindex->newcoins = komodo_newcoins(&pindex->zfunds,&pindex->sproutfunds,pindex->GetHeight(),&block);
else else
{ {
fprintf(stderr,"error loading block.%d\n",pindex->GetHeight()); fprintf(stderr,"error loading block.%d\n",pindex->GetHeight());
@@ -1923,10 +1926,12 @@ int64_t komodo_coinsupply(int64_t *zfundsp,int32_t height)
} }
supply += pindex->newcoins; supply += pindex->newcoins;
zfunds += pindex->zfunds; zfunds += pindex->zfunds;
sproutfunds += pindex->sproutfunds;
//printf("start ht.%d new %.8f -> supply %.8f zfunds %.8f -> %.8f\n",pindex->GetHeight(),dstr(pindex->newcoins),dstr(supply),dstr(pindex->zfunds),dstr(zfunds)); //printf("start ht.%d new %.8f -> supply %.8f zfunds %.8f -> %.8f\n",pindex->GetHeight(),dstr(pindex->newcoins),dstr(supply),dstr(pindex->zfunds),dstr(zfunds));
pindex = pindex->pprev; pindex = pindex->pprev;
} }
} }
*zfundsp = zfunds; *zfundsp = zfunds;
*sproutfundsp = sproutfunds;
return(supply); return(supply);
} }

View File

@@ -1373,7 +1373,7 @@ void komodo_passport_iteration()
{ {
static long lastpos[34]; static char userpass[33][1024]; static uint32_t lasttime,callcounter,lastinterest; static long lastpos[34]; static char userpass[33][1024]; static uint32_t lasttime,callcounter,lastinterest;
int32_t maxseconds = 10; int32_t maxseconds = 10;
FILE *fp; uint8_t *filedata; long fpos,datalen,lastfpos; int32_t baseid,limit,n,ht,isrealtime,expired,refid,blocks,longest; struct komodo_state *sp,*refsp; char *retstr,fname[512],*base,symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t buf[3],starttime; cJSON *infoobj,*result; uint64_t RTmask = 0; FILE *fp; uint8_t *filedata; long fpos,datalen,lastfpos; int32_t baseid,limit,n,ht,isrealtime,expired,refid,blocks,longest; struct komodo_state *sp,*refsp; char *retstr,fname[512],*base,symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t buf[3],starttime; cJSON *infoobj,*result; uint64_t RTmask = 0; //CBlockIndex *pindex;
expired = 0; expired = 0;
while ( KOMODO_INITDONE == 0 ) while ( KOMODO_INITDONE == 0 )
{ {

View File

@@ -1077,11 +1077,14 @@ bool ContextualCheckTransaction(
} }
// Rules that apply to Overwinter or later: // Rules that apply to Overwinter or later:
if (overwinterActive) { if (overwinterActive)
{
// Reject transactions intended for Sprout // Reject transactions intended for Sprout
if (!tx.fOverwintered) { if (!tx.fOverwintered)
{
int32_t ht = Params().GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight; int32_t ht = Params().GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight;
return state.DoS((ht < 0 || nHeight < ht) ? 0 : dosLevel, error("ContextualCheckTransaction: overwinter is active"),REJECT_INVALID, "tx-overwinter-active"); fprintf(stderr,"overwinter is active tx.%s not, ht.%d vs %d\n",tx.GetHash().ToString().c_str(),nHeight,ht);
return state.DoS((ASSETCHAINS_PRIVATE != 0 || ht < 0 || nHeight < ht) ? 0 : dosLevel, error("ContextualCheckTransaction: overwinter is active"),REJECT_INVALID, "tx-overwinter-active");
} }
// Check that all transactions are unexpired // Check that all transactions are unexpired
@@ -4961,7 +4964,7 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat
*ppindex = pindex; *ppindex = pindex;
if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK ) if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK )
{ {
if ( ASSETCHAINS_CC == 0 ) if ( ASSETCHAINS_CC == 0 )//&& (ASSETCHAINS_PRIVATE == 0 || KOMODO_INSYNC >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight) )
return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate");
else else
{ {
@@ -5083,6 +5086,19 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C
auto verifier = libzcash::ProofVerifier::Disabled(); auto verifier = libzcash::ProofVerifier::Disabled();
if ((!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) if ((!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev))
{ {
static int32_t saplinght = -1;
CBlockIndex *tmpptr;
if ( saplinght == -1 )
saplinght = Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight;
if ( saplinght < 0 )
*futureblockp = 1;
// the problem is when a future sapling block comes in before we detected saplinght
if ( saplinght > 0 && (tmpptr= chainActive.LastTip()) != 0 )
{
fprintf(stderr,"saplinght.%d tipht.%d blockht.%d cmp.%d\n",saplinght,(int32_t)tmpptr->GetHeight(),pindex->GetHeight(),pindex->GetHeight() < 0 || pindex->GetHeight() >= saplinght || (tmpptr->GetHeight() > saplinght-720 && tmpptr->GetHeight() < saplinght+720));
if ( pindex->GetHeight() < 0 || pindex->GetHeight() >= saplinght || (tmpptr->GetHeight() > saplinght-720 && tmpptr->GetHeight() < saplinght+720) )
*futureblockp = 1;
}
if ( *futureblockp == 0 ) if ( *futureblockp == 0 )
{ {
if (state.IsInvalid() && !state.CorruptionPossible()) { if (state.IsInvalid() && !state.CorruptionPossible()) {

View File

@@ -55,7 +55,7 @@ extern uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE;
extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN,IS_STAKED_NOTARY,IS_KOMODO_NOTARY,STAKED_ERA; extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN,IS_STAKED_NOTARY,IS_KOMODO_NOTARY,STAKED_ERA;
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
uint32_t komodo_segid32(char *coinaddr); uint32_t komodo_segid32(char *coinaddr);
int64_t komodo_coinsupply(int64_t *zfundsp,int32_t height); int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height);
int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp); int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp);
int8_t StakedNotaryID(std::string &notaryname, char *Raddress); int8_t StakedNotaryID(std::string &notaryname, char *Raddress);
#define KOMODO_VERSION "0.3.1" #define KOMODO_VERSION "0.3.1"
@@ -363,7 +363,7 @@ public:
UniValue coinsupply(const UniValue& params, bool fHelp) UniValue coinsupply(const UniValue& params, bool fHelp)
{ {
int32_t height = 0; int32_t currentHeight; int64_t zfunds,supply = 0; UniValue result(UniValue::VOBJ); int32_t height = 0; int32_t currentHeight; int64_t sproutfunds,zfunds,supply = 0; UniValue result(UniValue::VOBJ);
if (fHelp || params.size() > 1) if (fHelp || params.size() > 1)
throw runtime_error("coinsupply <height>\n" throw runtime_error("coinsupply <height>\n"
"\nReturn coin supply information at a given block height. If no height is given, the current height is used.\n" "\nReturn coin supply information at a given block height. If no height is given, the current height is used.\n"
@@ -376,7 +376,8 @@ UniValue coinsupply(const UniValue& params, bool fHelp)
" \"height\" : 420, (integer) The height of this coin supply data\n" " \"height\" : 420, (integer) The height of this coin supply data\n"
" \"supply\" : \"777.0\", (float) The transparent coin supply\n" " \"supply\" : \"777.0\", (float) The transparent coin supply\n"
" \"zfunds\" : \"0.777\", (float) The shielded coin supply (in zaddrs)\n" " \"zfunds\" : \"0.777\", (float) The shielded coin supply (in zaddrs)\n"
" \"total\" : \"777.777\", (float) The total coin supply, i.e. sum of supply + zfunds\n" " \"sprout\" : \"0.077\", (float) The sprout coin supply (in zcaddrs)\n"
" \"total\" : \"777.777\", (float) The total coin supply, i.e. sum of supply + zfunds\n"
"}\n" "}\n"
"\nExamples:\n" "\nExamples:\n"
+ HelpExampleCli("coinsupply", "420") + HelpExampleCli("coinsupply", "420")
@@ -388,13 +389,14 @@ UniValue coinsupply(const UniValue& params, bool fHelp)
currentHeight = chainActive.Height(); currentHeight = chainActive.Height();
if (height >= 0 && height <= currentHeight) { if (height >= 0 && height <= currentHeight) {
if ( (supply= komodo_coinsupply(&zfunds,height)) > 0 ) if ( (supply= komodo_coinsupply(&zfunds,&sproutfunds,height)) > 0 )
{ {
result.push_back(Pair("result", "success")); result.push_back(Pair("result", "success"));
result.push_back(Pair("coin", ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL)); result.push_back(Pair("coin", ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL));
result.push_back(Pair("height", (int)height)); result.push_back(Pair("height", (int)height));
result.push_back(Pair("supply", ValueFromAmount(supply))); result.push_back(Pair("supply", ValueFromAmount(supply)));
result.push_back(Pair("zfunds", ValueFromAmount(zfunds))); result.push_back(Pair("zfunds", ValueFromAmount(zfunds)));
result.push_back(Pair("sprout", ValueFromAmount(sproutfunds)));
result.push_back(Pair("total", ValueFromAmount(zfunds + supply))); result.push_back(Pair("total", ValueFromAmount(zfunds + supply)));
} else result.push_back(Pair("error", "couldnt calculate supply")); } else result.push_back(Pair("error", "couldnt calculate supply"));
} else { } else {

View File

@@ -42,7 +42,7 @@ int mta_find_output(UniValue obj, int n)
if (!outputMapValue.isArray()) { if (!outputMapValue.isArray()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation"); throw JSONRPCError(RPC_WALLET_ERROR, "Missing outputmap for JoinSplit operation");
} }
UniValue outputMap = outputMapValue.get_array(); UniValue outputMap = outputMapValue.get_array();
assert(outputMap.size() == ZC_NUM_JS_OUTPUTS); assert(outputMap.size() == ZC_NUM_JS_OUTPUTS);
for (size_t i = 0; i < outputMap.size(); i++) { for (size_t i = 0; i < outputMap.size(); i++) {
@@ -50,7 +50,7 @@ int mta_find_output(UniValue obj, int n)
return i; return i;
} }
} }
throw std::logic_error("n is not present in outputmap"); throw std::logic_error("n is not present in outputmap");
} }
@@ -69,33 +69,33 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context
if (fee < 0 || fee > MAX_MONEY) { if (fee < 0 || fee > MAX_MONEY) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range");
} }
if (utxoInputs.empty() && sproutNoteInputs.empty() && saplingNoteInputs.empty()) { if (utxoInputs.empty() && sproutNoteInputs.empty() && saplingNoteInputs.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs"); throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs");
} }
if (std::get<0>(recipient).size() == 0) { if (std::get<0>(recipient).size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing");
} }
if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) { if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress");
} }
if (sproutNoteInputs.size() > 0 && builder) { if (sproutNoteInputs.size() > 0 && builder) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Sprout notes are not supported by the TransactionBuilder"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Sprout notes are not supported by the TransactionBuilder");
} }
isUsingBuilder_ = false; isUsingBuilder_ = false;
if (builder) { if (builder) {
isUsingBuilder_ = true; isUsingBuilder_ = true;
builder_ = builder.get(); builder_ = builder.get();
} }
toTaddr_ = DecodeDestination(std::get<0>(recipient)); toTaddr_ = DecodeDestination(std::get<0>(recipient));
isToTaddr_ = IsValidDestination(toTaddr_); isToTaddr_ = IsValidDestination(toTaddr_);
isToZaddr_ = false; isToZaddr_ = false;
if (!isToTaddr_) { if (!isToTaddr_) {
auto address = DecodePaymentAddress(std::get<0>(recipient)); auto address = DecodePaymentAddress(std::get<0>(recipient));
if (IsValidPaymentAddress(address)) { if (IsValidPaymentAddress(address)) {
@@ -105,18 +105,18 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address");
} }
} }
// Log the context info i.e. the call parameters to z_mergetoaddress // Log the context info i.e. the call parameters to z_mergetoaddress
if (LogAcceptCategory("zrpcunsafe")) { if (LogAcceptCategory("zrpcunsafe")) {
LogPrint("zrpcunsafe", "%s: z_mergetoaddress initialized (params=%s)\n", getId(), contextInfo.write()); LogPrint("zrpcunsafe", "%s: z_mergetoaddress initialized (params=%s)\n", getId(), contextInfo.write());
} else { } else {
LogPrint("zrpc", "%s: z_mergetoaddress initialized\n", getId()); LogPrint("zrpc", "%s: z_mergetoaddress initialized\n", getId());
} }
// Lock UTXOs // Lock UTXOs
lock_utxos(); lock_utxos();
lock_notes(); lock_notes();
// Enable payment disclosure if requested // Enable payment disclosure if requested
paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true); paymentDisclosureMode = fExperimentalMode && GetBoolArg("-paymentdisclosure", true);
} }
@@ -132,12 +132,12 @@ void AsyncRPCOperation_mergetoaddress::main()
unlock_notes(); unlock_notes();
return; return;
} }
set_state(OperationStatus::EXECUTING); set_state(OperationStatus::EXECUTING);
start_execution_clock(); start_execution_clock();
bool success = false; bool success = false;
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
GenerateBitcoins(false, NULL, 0); GenerateBitcoins(false, NULL, 0);
@@ -145,7 +145,7 @@ void AsyncRPCOperation_mergetoaddress::main()
GenerateBitcoins(false, 0); GenerateBitcoins(false, 0);
#endif #endif
#endif #endif
try { try {
success = main_impl(); success = main_impl();
} catch (const UniValue& objError) { } catch (const UniValue& objError) {
@@ -166,7 +166,7 @@ void AsyncRPCOperation_mergetoaddress::main()
set_error_code(-2); set_error_code(-2);
set_error_message("unknown error"); set_error_message("unknown error");
} }
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1)); GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1));
@@ -174,15 +174,15 @@ void AsyncRPCOperation_mergetoaddress::main()
GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1)); GenerateBitcoins(GetBoolArg("-gen", false), GetArg("-genproclimit", 1));
#endif #endif
#endif #endif
stop_execution_clock(); stop_execution_clock();
if (success) { if (success) {
set_state(OperationStatus::SUCCESS); set_state(OperationStatus::SUCCESS);
} else { } else {
set_state(OperationStatus::FAILED); set_state(OperationStatus::FAILED);
} }
std::string s = strprintf("%s: z_mergetoaddress finished (status=%s", getId(), getStateAsString()); std::string s = strprintf("%s: z_mergetoaddress finished (status=%s", getId(), getStateAsString());
if (success) { if (success) {
s += strprintf(", txid=%s)\n", tx_.GetHash().ToString()); s += strprintf(", txid=%s)\n", tx_.GetHash().ToString());
@@ -190,10 +190,10 @@ void AsyncRPCOperation_mergetoaddress::main()
s += strprintf(", error=%s)\n", getErrorMessage()); s += strprintf(", error=%s)\n", getErrorMessage());
} }
LogPrintf("%s", s); LogPrintf("%s", s);
unlock_utxos(); // clean up unlock_utxos(); // clean up
unlock_notes(); // clean up unlock_notes(); // clean up
// !!! Payment disclosure START // !!! Payment disclosure START
if (success && paymentDisclosureMode && paymentDisclosureData_.size() > 0) { if (success && paymentDisclosureMode && paymentDisclosureData_.size() > 0) {
uint256 txidhash = tx_.GetHash(); uint256 txidhash = tx_.GetHash();
@@ -216,12 +216,12 @@ void AsyncRPCOperation_mergetoaddress::main()
bool AsyncRPCOperation_mergetoaddress::main_impl() bool AsyncRPCOperation_mergetoaddress::main_impl()
{ {
assert(isToTaddr_ != isToZaddr_); assert(isToTaddr_ != isToZaddr_);
bool isPureTaddrOnlyTx = (sproutNoteInputs_.empty() && saplingNoteInputs_.empty() && isToTaddr_); bool isPureTaddrOnlyTx = (sproutNoteInputs_.empty() && saplingNoteInputs_.empty() && isToTaddr_);
CAmount minersFee = fee_; CAmount minersFee = fee_;
size_t numInputs = utxoInputs_.size(); size_t numInputs = utxoInputs_.size();
// Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects
size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0);
{ {
@@ -235,31 +235,31 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
strprintf("Number of transparent inputs %d is greater than mempooltxinputlimit of %d", strprintf("Number of transparent inputs %d is greater than mempooltxinputlimit of %d",
numInputs, limit)); numInputs, limit));
} }
CAmount t_inputs_total = 0; CAmount t_inputs_total = 0;
for (MergeToAddressInputUTXO& t : utxoInputs_) { for (MergeToAddressInputUTXO& t : utxoInputs_) {
t_inputs_total += std::get<1>(t); t_inputs_total += std::get<1>(t);
} }
CAmount z_inputs_total = 0; CAmount z_inputs_total = 0;
for (const MergeToAddressInputSproutNote& t : sproutNoteInputs_) { for (const MergeToAddressInputSproutNote& t : sproutNoteInputs_) {
z_inputs_total += std::get<2>(t); z_inputs_total += std::get<2>(t);
} }
for (const MergeToAddressInputSaplingNote& t : saplingNoteInputs_) { for (const MergeToAddressInputSaplingNote& t : saplingNoteInputs_) {
z_inputs_total += std::get<2>(t); z_inputs_total += std::get<2>(t);
} }
CAmount targetAmount = z_inputs_total + t_inputs_total; CAmount targetAmount = z_inputs_total + t_inputs_total;
if (targetAmount <= minersFee) { if (targetAmount <= minersFee) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
strprintf("Insufficient funds, have %s and miners fee is %s", strprintf("Insufficient funds, have %s and miners fee is %s",
FormatMoney(targetAmount), FormatMoney(minersFee))); FormatMoney(targetAmount), FormatMoney(minersFee)));
} }
CAmount sendAmount = targetAmount - minersFee; CAmount sendAmount = targetAmount - minersFee;
// update the transaction with the UTXO inputs and output (if any) // update the transaction with the UTXO inputs and output (if any)
if (!isUsingBuilder_) { if (!isUsingBuilder_) {
CMutableTransaction rawTx(tx_); CMutableTransaction rawTx(tx_);
@@ -274,7 +274,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} }
tx_ = CTransaction(rawTx); tx_ = CTransaction(rawTx);
} }
LogPrint(isPureTaddrOnlyTx ? "zrpc" : "zrpcunsafe", "%s: spending %s to send %s with fee %s\n", LogPrint(isPureTaddrOnlyTx ? "zrpc" : "zrpcunsafe", "%s: spending %s to send %s with fee %s\n",
getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee)); getId(), FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee));
LogPrint("zrpc", "%s: transparent input: %s\n", getId(), FormatMoney(t_inputs_total)); LogPrint("zrpc", "%s: transparent input: %s\n", getId(), FormatMoney(t_inputs_total));
@@ -285,13 +285,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(sendAmount)); LogPrint("zrpcunsafe", "%s: private output: %s\n", getId(), FormatMoney(sendAmount));
} }
LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee)); LogPrint("zrpc", "%s: fee: %s\n", getId(), FormatMoney(minersFee));
// Grab the current consensus branch ID // Grab the current consensus branch ID
{ {
LOCK(cs_main); LOCK(cs_main);
consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); consensusBranchId_ = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
} }
/** /**
* SCENARIO #0 * SCENARIO #0
* *
@@ -301,15 +301,15 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
*/ */
if (isUsingBuilder_) { if (isUsingBuilder_) {
builder_.SetFee(minersFee); builder_.SetFee(minersFee);
for (const MergeToAddressInputUTXO& t : utxoInputs_) { for (const MergeToAddressInputUTXO& t : utxoInputs_) {
COutPoint outPoint = std::get<0>(t); COutPoint outPoint = std::get<0>(t);
CAmount amount = std::get<1>(t); CAmount amount = std::get<1>(t);
CScript scriptPubKey = std::get<2>(t); CScript scriptPubKey = std::get<2>(t);
builder_.AddTransparentInput(outPoint, scriptPubKey, amount); builder_.AddTransparentInput(outPoint, scriptPubKey, amount);
} }
boost::optional<uint256> ovk; boost::optional<uint256> ovk;
// Select Sapling notes // Select Sapling notes
std::vector<SaplingOutPoint> saplingOPs; std::vector<SaplingOutPoint> saplingOPs;
@@ -324,7 +324,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
ovk = expsk.full_viewing_key().ovk; ovk = expsk.full_viewing_key().ovk;
} }
} }
// Fetch Sapling anchor and witnesses // Fetch Sapling anchor and witnesses
uint256 anchor; uint256 anchor;
std::vector<boost::optional<SaplingWitness>> witnesses; std::vector<boost::optional<SaplingWitness>> witnesses;
@@ -332,7 +332,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetSaplingNoteWitnesses(saplingOPs, witnesses, anchor); pwalletMain->GetSaplingNoteWitnesses(saplingOPs, witnesses, anchor);
} }
// Add Sapling spends // Add Sapling spends
for (size_t i = 0; i < saplingNotes.size(); i++) { for (size_t i = 0; i < saplingNotes.size(); i++) {
if (!witnesses[i]) { if (!witnesses[i]) {
@@ -340,7 +340,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} }
assert(builder_.AddSaplingSpend(expsks[i], saplingNotes[i], anchor, witnesses[i].get())); assert(builder_.AddSaplingSpend(expsks[i], saplingNotes[i], anchor, witnesses[i].get()));
} }
if (isToTaddr_) { if (isToTaddr_) {
if (!builder_.AddTransparentOutput(toTaddr_, sendAmount)) { if (!builder_.AddTransparentOutput(toTaddr_, sendAmount)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid output address, not a valid taddr.");
@@ -372,15 +372,15 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} }
builder_.AddSaplingOutput(ovk.get(), *saplingPaymentAddress, sendAmount, hexMemo); builder_.AddSaplingOutput(ovk.get(), *saplingPaymentAddress, sendAmount, hexMemo);
} }
// Build the transaction // Build the transaction
auto maybe_tx = builder_.Build(); auto maybe_tx = builder_.Build();
if (!maybe_tx) { if (!maybe_tx) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction."); throw JSONRPCError(RPC_WALLET_ERROR, "Failed to build transaction.");
} }
tx_ = maybe_tx.get(); tx_ = maybe_tx.get();
// Send the transaction // Send the transaction
// TODO: Use CWallet::CommitTransaction instead of sendrawtransaction // TODO: Use CWallet::CommitTransaction instead of sendrawtransaction
auto signedtxn = EncodeHexTx(tx_); auto signedtxn = EncodeHexTx(tx_);
@@ -391,9 +391,9 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (sendResultValue.isNull()) { if (sendResultValue.isNull()) {
throw JSONRPCError(RPC_WALLET_ERROR, "sendrawtransaction did not return an error or a txid."); throw JSONRPCError(RPC_WALLET_ERROR, "sendrawtransaction did not return an error or a txid.");
} }
auto txid = sendResultValue.get_str(); auto txid = sendResultValue.get_str();
UniValue o(UniValue::VOBJ); UniValue o(UniValue::VOBJ);
o.push_back(Pair("txid", txid)); o.push_back(Pair("txid", txid));
set_result(o); set_result(o);
@@ -405,14 +405,14 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
o.push_back(Pair("hex", signedtxn)); o.push_back(Pair("hex", signedtxn));
set_result(o); set_result(o);
} }
return true; return true;
} }
/** /**
* END SCENARIO #0 * END SCENARIO #0
*/ */
/** /**
* SCENARIO #1 * SCENARIO #1
* *
@@ -429,16 +429,16 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
/** /**
* END SCENARIO #1 * END SCENARIO #1
*/ */
// Prepare raw transaction to handle JoinSplits // Prepare raw transaction to handle JoinSplits
CMutableTransaction mtx(tx_); CMutableTransaction mtx(tx_);
crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_); crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_);
mtx.joinSplitPubKey = joinSplitPubKey_; mtx.joinSplitPubKey = joinSplitPubKey_;
tx_ = CTransaction(mtx); tx_ = CTransaction(mtx);
std::string hexMemo = std::get<1>(recipient_); std::string hexMemo = std::get<1>(recipient_);
/** /**
* SCENARIO #2 * SCENARIO #2
* *
@@ -451,13 +451,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
MergeToAddressJSInfo info; MergeToAddressJSInfo info;
info.vpub_old = sendAmount; info.vpub_old = sendAmount;
info.vpub_new = 0; info.vpub_new = 0;
JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(toPaymentAddress_), sendAmount); JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(toPaymentAddress_), sendAmount);
if (hexMemo.size() > 0) { if (hexMemo.size() > 0) {
jso.memo = get_memo_from_hex_string(hexMemo); jso.memo = get_memo_from_hex_string(hexMemo);
} }
info.vjsout.push_back(jso); info.vjsout.push_back(jso);
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj = perform_joinsplit(info); obj = perform_joinsplit(info);
sign_send_raw_transaction(obj); sign_send_raw_transaction(obj);
@@ -466,14 +466,14 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
/** /**
* END SCENARIO #2 * END SCENARIO #2
*/ */
// Copy zinputs to more flexible containers // Copy zinputs to more flexible containers
std::deque<MergeToAddressInputSproutNote> zInputsDeque; std::deque<MergeToAddressInputSproutNote> zInputsDeque;
for (const auto& o : sproutNoteInputs_) { for (const auto& o : sproutNoteInputs_) {
zInputsDeque.push_back(o); zInputsDeque.push_back(o);
} }
// When spending notes, take a snapshot of note witnesses and anchors as the treestate will // When spending notes, take a snapshot of note witnesses and anchors as the treestate will
// change upon arrival of new blocks which contain joinsplit transactions. This is likely // change upon arrival of new blocks which contain joinsplit transactions. This is likely
// to happen as creating a chained joinsplit transaction can take longer than the block interval. // to happen as creating a chained joinsplit transaction can take longer than the block interval.
@@ -488,7 +488,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor}; jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor};
} }
} }
/** /**
* SCENARIO #3 * SCENARIO #3
* *
@@ -507,12 +507,12 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0 int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0
bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit
bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit
// At this point, we are guaranteed to have at least one input note. // At this point, we are guaranteed to have at least one input note.
// Use address of first input note as the temporary change address. // Use address of first input note as the temporary change address.
SproutSpendingKey changeKey = std::get<3>(zInputsDeque.front()); SproutSpendingKey changeKey = std::get<3>(zInputsDeque.front());
SproutPaymentAddress changeAddress = changeKey.address(); SproutPaymentAddress changeAddress = changeKey.address();
CAmount vpubOldTarget = 0; CAmount vpubOldTarget = 0;
CAmount vpubNewTarget = 0; CAmount vpubNewTarget = 0;
if (isToTaddr_) { if (isToTaddr_) {
@@ -524,16 +524,16 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
vpubOldTarget = t_inputs_total - minersFee; vpubOldTarget = t_inputs_total - minersFee;
} }
} }
// Keep track of treestate within this transaction // Keep track of treestate within this transaction
boost::unordered_map<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates; boost::unordered_map<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates;
std::vector<uint256> previousCommitments; std::vector<uint256> previousCommitments;
while (!vpubNewProcessed) { while (!vpubNewProcessed) {
MergeToAddressJSInfo info; MergeToAddressJSInfo info;
info.vpub_old = 0; info.vpub_old = 0;
info.vpub_new = 0; info.vpub_new = 0;
// Set vpub_old in the first joinsplit // Set vpub_old in the first joinsplit
if (!vpubOldProcessed) { if (!vpubOldProcessed) {
if (t_inputs_total < vpubOldTarget) { if (t_inputs_total < vpubOldTarget) {
@@ -544,30 +544,30 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
info.vpub_old += vpubOldTarget; // funds flowing from public pool info.vpub_old += vpubOldTarget; // funds flowing from public pool
vpubOldProcessed = true; vpubOldProcessed = true;
} }
CAmount jsInputValue = 0; CAmount jsInputValue = 0;
uint256 jsAnchor; uint256 jsAnchor;
std::vector<boost::optional<SproutWitness>> witnesses; std::vector<boost::optional<SproutWitness>> witnesses;
JSDescription prevJoinSplit; JSDescription prevJoinSplit;
// Keep track of previous JoinSplit and its commitments // Keep track of previous JoinSplit and its commitments
if (tx_.vjoinsplit.size() > 0) { if (tx_.vjoinsplit.size() > 0) {
prevJoinSplit = tx_.vjoinsplit.back(); prevJoinSplit = tx_.vjoinsplit.back();
} }
// If there is no change, the chain has terminated so we can reset the tracked treestate. // If there is no change, the chain has terminated so we can reset the tracked treestate.
if (jsChange == 0 && tx_.vjoinsplit.size() > 0) { if (jsChange == 0 && tx_.vjoinsplit.size() > 0) {
intermediates.clear(); intermediates.clear();
previousCommitments.clear(); previousCommitments.clear();
} }
// //
// Consume change as the first input of the JoinSplit. // Consume change as the first input of the JoinSplit.
// //
if (jsChange > 0) { if (jsChange > 0) {
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
// Update tree state with previous joinsplit // Update tree state with previous joinsplit
SproutMerkleTree tree; SproutMerkleTree tree;
auto it = intermediates.find(prevJoinSplit.anchor); auto it = intermediates.find(prevJoinSplit.anchor);
@@ -576,7 +576,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) { } else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor"); throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor");
} }
assert(changeOutputIndex != -1); assert(changeOutputIndex != -1);
boost::optional<SproutWitness> changeWitness; boost::optional<SproutWitness> changeWitness;
int n = 0; int n = 0;
@@ -594,7 +594,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} }
jsAnchor = tree.root(); jsAnchor = tree.root();
intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries) intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries)
// Decrypt the change note's ciphertext to retrieve some data we need // Decrypt the change note's ciphertext to retrieve some data we need
ZCNoteDecryption decryptor(changeKey.receiving_key()); ZCNoteDecryption decryptor(changeKey.receiving_key());
auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey); auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey);
@@ -605,23 +605,23 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
prevJoinSplit.ephemeralKey, prevJoinSplit.ephemeralKey,
hSig, hSig,
(unsigned char)changeOutputIndex); (unsigned char)changeOutputIndex);
SproutNote note = plaintext.note(changeAddress); SproutNote note = plaintext.note(changeAddress);
info.notes.push_back(note); info.notes.push_back(note);
info.zkeys.push_back(changeKey); info.zkeys.push_back(changeKey);
jsInputValue += plaintext.value(); jsInputValue += plaintext.value();
LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n", LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n",
getId(), getId(),
FormatMoney(plaintext.value())); FormatMoney(plaintext.value()));
} catch (const std::exception& e) { } catch (const std::exception& e) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what())); throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what()));
} }
} }
// //
// Consume spendable non-change notes // Consume spendable non-change notes
// //
@@ -638,7 +638,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
CAmount noteFunds = std::get<2>(t); CAmount noteFunds = std::get<2>(t);
SproutSpendingKey zkey = std::get<3>(t); SproutSpendingKey zkey = std::get<3>(t);
zInputsDeque.pop_front(); zInputsDeque.pop_front();
MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap[jso.ToString()]; MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap[jso.ToString()];
vInputWitnesses.push_back(wad.witness); vInputWitnesses.push_back(wad.witness);
if (inputAnchor.IsNull()) { if (inputAnchor.IsNull()) {
@@ -646,13 +646,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} else if (inputAnchor != wad.anchor) { } else if (inputAnchor != wad.anchor) {
throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor"); throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor");
} }
vOutPoints.push_back(jso); vOutPoints.push_back(jso);
vInputNotes.push_back(note); vInputNotes.push_back(note);
vInputZKeys.push_back(zkey); vInputZKeys.push_back(zkey);
jsInputValue += noteFunds; jsInputValue += noteFunds;
int wtxHeight = -1; int wtxHeight = -1;
int wtxDepth = -1; int wtxDepth = -1;
{ {
@@ -674,13 +674,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
wtxHeight, wtxHeight,
wtxDepth); wtxDepth);
} }
// Add history of previous commitments to witness // Add history of previous commitments to witness
if (vInputNotes.size() > 0) { if (vInputNotes.size() > 0) {
if (vInputWitnesses.size() == 0) { if (vInputWitnesses.size() == 0) {
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment");
} }
for (auto& optionalWitness : vInputWitnesses) { for (auto& optionalWitness : vInputWitnesses) {
if (!optionalWitness) { if (!optionalWitness) {
throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null");
@@ -696,20 +696,20 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} }
witnesses.push_back(w); witnesses.push_back(w);
} }
// The jsAnchor is null if this JoinSplit is at the start of a new chain // The jsAnchor is null if this JoinSplit is at the start of a new chain
if (jsAnchor.IsNull()) { if (jsAnchor.IsNull()) {
jsAnchor = inputAnchor; jsAnchor = inputAnchor;
} }
// Add spendable notes as inputs // Add spendable notes as inputs
std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes)); std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes));
std::copy(vInputZKeys.begin(), vInputZKeys.end(), std::back_inserter(info.zkeys)); std::copy(vInputZKeys.begin(), vInputZKeys.end(), std::back_inserter(info.zkeys));
} }
// Accumulate change // Accumulate change
jsChange = jsInputValue + info.vpub_old; jsChange = jsInputValue + info.vpub_old;
// Set vpub_new in the last joinsplit (when there are no more notes to spend) // Set vpub_new in the last joinsplit (when there are no more notes to spend)
if (zInputsDeque.empty()) { if (zInputsDeque.empty()) {
assert(!vpubNewProcessed); assert(!vpubNewProcessed);
@@ -724,10 +724,10 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
// If we are merging to a t-addr, there should be no change // If we are merging to a t-addr, there should be no change
if (isToTaddr_) assert(jsChange == 0); if (isToTaddr_) assert(jsChange == 0);
} }
// create dummy output // create dummy output
info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new
// create output for any change // create output for any change
if (jsChange > 0) { if (jsChange > 0) {
std::string outputType = "change"; std::string outputType = "change";
@@ -741,24 +741,24 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} }
} }
info.vjsout.push_back(jso); info.vjsout.push_back(jso);
LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n", LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n",
getId(), getId(),
outputType, outputType,
FormatMoney(jsChange)); FormatMoney(jsChange));
} }
obj = perform_joinsplit(info, witnesses, jsAnchor); obj = perform_joinsplit(info, witnesses, jsAnchor);
if (jsChange > 0) { if (jsChange > 0) {
changeOutputIndex = mta_find_output(obj, 1); changeOutputIndex = mta_find_output(obj, 1);
} }
} }
// Sanity check in case changes to code block above exits loop by invoking 'break' // Sanity check in case changes to code block above exits loop by invoking 'break'
assert(zInputsDeque.size() == 0); assert(zInputsDeque.size() == 0);
assert(vpubNewProcessed); assert(vpubNewProcessed);
sign_send_raw_transaction(obj); sign_send_raw_transaction(obj);
return true; return true;
} }
@@ -778,7 +778,7 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj)
throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for raw transaction"); throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for raw transaction");
} }
std::string rawtxn = rawtxnValue.get_str(); std::string rawtxn = rawtxnValue.get_str();
UniValue params = UniValue(UniValue::VARR); UniValue params = UniValue(UniValue::VARR);
params.push_back(rawtxn); params.push_back(rawtxn);
UniValue signResultValue = signrawtransaction(params, false); UniValue signResultValue = signrawtransaction(params, false);
@@ -789,13 +789,13 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj)
// TODO: #1366 Maybe get "errors" and print array vErrors into a string // TODO: #1366 Maybe get "errors" and print array vErrors into a string
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to sign transaction"); throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to sign transaction");
} }
UniValue hexValue = find_value(signResultObject, "hex"); UniValue hexValue = find_value(signResultObject, "hex");
if (hexValue.isNull()) { if (hexValue.isNull()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction"); throw JSONRPCError(RPC_WALLET_ERROR, "Missing hex data for signed transaction");
} }
std::string signedtxn = hexValue.get_str(); std::string signedtxn = hexValue.get_str();
// Send the signed transaction // Send the signed transaction
if (!testmode) { if (!testmode) {
params.clear(); params.clear();
@@ -805,26 +805,26 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj)
if (sendResultValue.isNull()) { if (sendResultValue.isNull()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Send raw transaction did not return an error or a txid."); throw JSONRPCError(RPC_WALLET_ERROR, "Send raw transaction did not return an error or a txid.");
} }
std::string txid = sendResultValue.get_str(); std::string txid = sendResultValue.get_str();
UniValue o(UniValue::VOBJ); UniValue o(UniValue::VOBJ);
o.push_back(Pair("txid", txid)); o.push_back(Pair("txid", txid));
set_result(o); set_result(o);
} else { } else {
// Test mode does not send the transaction to the network. // Test mode does not send the transaction to the network.
CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx; CTransaction tx;
stream >> tx; stream >> tx;
UniValue o(UniValue::VOBJ); UniValue o(UniValue::VOBJ);
o.push_back(Pair("test", 1)); o.push_back(Pair("test", 1));
o.push_back(Pair("txid", tx.GetHash().ToString())); o.push_back(Pair("txid", tx.GetHash().ToString()));
o.push_back(Pair("hex", signedtxn)); o.push_back(Pair("hex", signedtxn));
set_result(o); set_result(o);
} }
// Keep the signed transaction so we can hash to the same txid // Keep the signed transaction so we can hash to the same txid
CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(ParseHex(signedtxn), SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx; CTransaction tx;
@@ -864,52 +864,52 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
if (anchor.IsNull()) { if (anchor.IsNull()) {
throw std::runtime_error("anchor is null"); throw std::runtime_error("anchor is null");
} }
if (witnesses.size() != info.notes.size()) { if (witnesses.size() != info.notes.size()) {
throw runtime_error("number of notes and witnesses do not match"); throw runtime_error("number of notes and witnesses do not match");
} }
if (info.notes.size() != info.zkeys.size()) { if (info.notes.size() != info.zkeys.size()) {
throw runtime_error("number of notes and spending keys do not match"); throw runtime_error("number of notes and spending keys do not match");
} }
for (size_t i = 0; i < witnesses.size(); i++) { for (size_t i = 0; i < witnesses.size(); i++) {
if (!witnesses[i]) { if (!witnesses[i]) {
throw runtime_error("joinsplit input could not be found in tree"); throw runtime_error("joinsplit input could not be found in tree");
} }
info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], info.zkeys[i])); info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], info.zkeys[i]));
} }
// Make sure there are two inputs and two outputs // Make sure there are two inputs and two outputs
while (info.vjsin.size() < ZC_NUM_JS_INPUTS) { while (info.vjsin.size() < ZC_NUM_JS_INPUTS) {
info.vjsin.push_back(JSInput()); info.vjsin.push_back(JSInput());
} }
while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) { while (info.vjsout.size() < ZC_NUM_JS_OUTPUTS) {
info.vjsout.push_back(JSOutput()); info.vjsout.push_back(JSOutput());
} }
if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) { if (info.vjsout.size() != ZC_NUM_JS_INPUTS || info.vjsin.size() != ZC_NUM_JS_OUTPUTS) {
throw runtime_error("unsupported joinsplit input/output counts"); throw runtime_error("unsupported joinsplit input/output counts");
} }
CMutableTransaction mtx(tx_); CMutableTransaction mtx(tx_);
LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n", LogPrint("zrpcunsafe", "%s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s)\n",
getId(), getId(),
tx_.vjoinsplit.size(), tx_.vjoinsplit.size(),
FormatMoney(info.vpub_old), FormatMoney(info.vpub_new), FormatMoney(info.vpub_old), FormatMoney(info.vpub_new),
FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()), FormatMoney(info.vjsin[0].note.value()), FormatMoney(info.vjsin[1].note.value()),
FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value)); FormatMoney(info.vjsout[0].value), FormatMoney(info.vjsout[1].value));
// Generate the proof, this can take over a minute. // Generate the proof, this can take over a minute.
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs{info.vjsin[0], info.vjsin[1]}; std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs{info.vjsin[0], info.vjsin[1]};
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs{info.vjsout[0], info.vjsout[1]}; std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs{info.vjsout[0], info.vjsout[1]};
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap; std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap; std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
uint256 esk; // payment disclosure - secret uint256 esk; // payment disclosure - secret
JSDescription jsdesc = JSDescription::Randomized( JSDescription jsdesc = JSDescription::Randomized(
mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION), mtx.fOverwintered && (mtx.nVersion >= SAPLING_TX_VERSION),
*pzcashParams, *pzcashParams,
@@ -929,34 +929,34 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
throw std::runtime_error("error verifying joinsplit"); throw std::runtime_error("error verifying joinsplit");
} }
} }
mtx.vjoinsplit.push_back(jsdesc); mtx.vjoinsplit.push_back(jsdesc);
// Empty output script. // Empty output script.
CScript scriptCode; CScript scriptCode;
CTransaction signTx(mtx); CTransaction signTx(mtx);
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_); uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId_);
// Add the signature // Add the signature
if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, if (!(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
dataToBeSigned.begin(), 32, dataToBeSigned.begin(), 32,
joinSplitPrivKey_) == 0)) { joinSplitPrivKey_) == 0)) {
throw std::runtime_error("crypto_sign_detached failed"); throw std::runtime_error("crypto_sign_detached failed");
} }
// Sanity check // Sanity check
if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0], if (!(crypto_sign_verify_detached(&mtx.joinSplitSig[0],
dataToBeSigned.begin(), 32, dataToBeSigned.begin(), 32,
mtx.joinSplitPubKey.begin()) == 0)) { mtx.joinSplitPubKey.begin()) == 0)) {
throw std::runtime_error("crypto_sign_verify_detached failed"); throw std::runtime_error("crypto_sign_verify_detached failed");
} }
CTransaction rawTx(mtx); CTransaction rawTx(mtx);
tx_ = rawTx; tx_ = rawTx;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << rawTx; ss << rawTx;
std::string encryptedNote1; std::string encryptedNote1;
std::string encryptedNote2; std::string encryptedNote2;
{ {
@@ -965,7 +965,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ephemeralKey;
ss2 << jsdesc.ciphertexts[0]; ss2 << jsdesc.ciphertexts[0];
ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_);
encryptedNote1 = HexStr(ss2.begin(), ss2.end()); encryptedNote1 = HexStr(ss2.begin(), ss2.end());
} }
{ {
@@ -974,10 +974,10 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
ss2 << jsdesc.ephemeralKey; ss2 << jsdesc.ephemeralKey;
ss2 << jsdesc.ciphertexts[1]; ss2 << jsdesc.ciphertexts[1];
ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_); ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey_);
encryptedNote2 = HexStr(ss2.begin(), ss2.end()); encryptedNote2 = HexStr(ss2.begin(), ss2.end());
} }
UniValue arrInputMap(UniValue::VARR); UniValue arrInputMap(UniValue::VARR);
UniValue arrOutputMap(UniValue::VARR); UniValue arrOutputMap(UniValue::VARR);
for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) { for (size_t i = 0; i < ZC_NUM_JS_INPUTS; i++) {
@@ -986,8 +986,8 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) { for (size_t i = 0; i < ZC_NUM_JS_OUTPUTS; i++) {
arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i])); arrOutputMap.push_back(static_cast<uint64_t>(outputMap[i]));
} }
// !!! Payment disclosure START // !!! Payment disclosure START
unsigned char buffer[32] = {0}; unsigned char buffer[32] = {0};
memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer memcpy(&buffer[0], &joinSplitPrivKey_[0], 32); // private key in first half of 64 byte buffer
@@ -1003,11 +1003,11 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output libzcash::SproutPaymentAddress zaddr = output.addr; // randomized output
PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr}; PaymentDisclosureInfo pdInfo = {PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL, esk, joinSplitPrivKey, zaddr};
paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo)); paymentDisclosureData_.push_back(PaymentDisclosureKeyInfo(pdKey, pdInfo));
LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr)); LogPrint("paymentdisclosure", "%s: Payment Disclosure: js=%d, n=%d, zaddr=%s\n", getId(), js_index, int(mapped_index), EncodePaymentAddress(zaddr));
} }
// !!! Payment disclosure END // !!! Payment disclosure END
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("encryptednote1", encryptedNote1)); obj.push_back(Pair("encryptednote1", encryptedNote1));
obj.push_back(Pair("encryptednote2", encryptedNote2)); obj.push_back(Pair("encryptednote2", encryptedNote2));
@@ -1020,19 +1020,19 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
std::array<unsigned char, ZC_MEMO_SIZE> AsyncRPCOperation_mergetoaddress::get_memo_from_hex_string(std::string s) std::array<unsigned char, ZC_MEMO_SIZE> AsyncRPCOperation_mergetoaddress::get_memo_from_hex_string(std::string s)
{ {
std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0x00}}; std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0x00}};
std::vector<unsigned char> rawMemo = ParseHex(s.c_str()); std::vector<unsigned char> rawMemo = ParseHex(s.c_str());
// If ParseHex comes across a non-hex char, it will stop but still return results so far. // If ParseHex comes across a non-hex char, it will stop but still return results so far.
size_t slen = s.length(); size_t slen = s.length();
if (slen % 2 != 0 || (slen > 0 && rawMemo.size() != slen / 2)) { if (slen % 2 != 0 || (slen > 0 && rawMemo.size() != slen / 2)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo must be in hexadecimal format"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo must be in hexadecimal format");
} }
if (rawMemo.size() > ZC_MEMO_SIZE) { if (rawMemo.size() > ZC_MEMO_SIZE) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Memo size of %d is too big, maximum allowed is %d", rawMemo.size(), ZC_MEMO_SIZE)); throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Memo size of %d is too big, maximum allowed is %d", rawMemo.size(), ZC_MEMO_SIZE));
} }
// copy vector into boost array // copy vector into boost array
int lenMemo = rawMemo.size(); int lenMemo = rawMemo.size();
for (int i = 0; i < ZC_MEMO_SIZE && i < lenMemo; i++) { for (int i = 0; i < ZC_MEMO_SIZE && i < lenMemo; i++) {
@@ -1050,7 +1050,7 @@ UniValue AsyncRPCOperation_mergetoaddress::getStatus() const
if (contextinfo_.isNull()) { if (contextinfo_.isNull()) {
return v; return v;
} }
UniValue obj = v.get_obj(); UniValue obj = v.get_obj();
obj.push_back(Pair("method", "z_mergetoaddress")); obj.push_back(Pair("method", "z_mergetoaddress"));
obj.push_back(Pair("params", contextinfo_)); obj.push_back(Pair("params", contextinfo_));

View File

@@ -169,9 +169,9 @@ void AsyncRPCOperation_sendmany::main() {
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 0)); GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 1));
#else #else
GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 0)); GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 1));
#endif #endif
#endif #endif
@@ -807,9 +807,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
vOutPoints.push_back(jso); vOutPoints.push_back(jso);
vInputNotes.push_back(note); vInputNotes.push_back(note);
jsInputValue += noteFunds; jsInputValue += noteFunds;
int wtxHeight = -1; int wtxHeight = -1;
int wtxDepth = -1; int wtxDepth = -1;
{ {
@@ -832,14 +832,14 @@ bool AsyncRPCOperation_sendmany::main_impl() {
wtxDepth wtxDepth
); );
} }
// Add history of previous commitments to witness // Add history of previous commitments to witness
if (vInputNotes.size() > 0) { if (vInputNotes.size() > 0) {
if (vInputWitnesses.size()==0) { if (vInputWitnesses.size()==0) {
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment"); throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment");
} }
for (auto & optionalWitness : vInputWitnesses) { for (auto & optionalWitness : vInputWitnesses) {
if (!optionalWitness) { if (!optionalWitness) {
throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null"); throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null");
@@ -1061,7 +1061,7 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
continue; continue;
CAmount nValue = out.tx->vout[out.i].nValue; CAmount nValue = out.tx->vout[out.i].nValue;
SendManyInputUTXO utxo(out.tx->GetHash(), out.i, nValue, isCoinbase, dest); SendManyInputUTXO utxo(out.tx->GetHash(), out.i, nValue, isCoinbase, dest);
t_inputs_.push_back(utxo); t_inputs_.push_back(utxo);
} }
@@ -1368,7 +1368,7 @@ 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> AsyncRPCOperation_sendmany::get_memo_from_hex_string(std::string s) {
// initialize to default memo (no_memo), see section 5.5 of the protocol spec // initialize to default memo (no_memo), see section 5.5 of the protocol spec
std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}}; std::array<unsigned char, ZC_MEMO_SIZE> memo = {{0xF6}};
std::vector<unsigned char> rawMemo = ParseHex(s.c_str()); std::vector<unsigned char> rawMemo = ParseHex(s.c_str());
// If ParseHex comes across a non-hex char, it will stop but still return results so far. // If ParseHex comes across a non-hex char, it will stop but still return results so far.

View File

@@ -141,9 +141,9 @@ void AsyncRPCOperation_shieldcoinbase::main() {
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 0)); GenerateBitcoins(GetBoolArg("-gen",false), pwalletMain, GetArg("-genproclimit", 1));
#else #else
GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 0)); GenerateBitcoins(GetBoolArg("-gen",false), GetArg("-genproclimit", 1));
#endif #endif
#endif #endif

View File

@@ -4216,6 +4216,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
if ( fromSprout || toSprout ) if ( fromSprout || toSprout )
throw JSONRPCError(RPC_INVALID_PARAMETER,"Sprout usage has expired"); throw JSONRPCError(RPC_INVALID_PARAMETER,"Sprout usage has expired");
} }
if ( toSapling && ASSETCHAINS_SYMBOL[0] == 0 )
throw JSONRPCError(RPC_INVALID_PARAMETER,"Sprout usage will expire soon");
// If we are sending from a shielded address, all recipient // If we are sending from a shielded address, all recipient
// shielded addresses must be of the same type. // shielded addresses must be of the same type.
if ((fromSprout && toSapling) || (fromSapling && toSprout)) { if ((fromSprout && toSapling) || (fromSapling && toSprout)) {

View File

@@ -63,8 +63,8 @@ void post_wallet_load(){
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
// Generate coins in the background // Generate coins in the background
if (pwalletMain || !GetArg("-mineraddress", "").empty()) if (pwalletMain || !GetArg("-mineraddress", "").empty())
GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 0)); GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", 1));
#endif #endif
} }