Teach RPC interface about dpow-enabled minconfs (#1231)

* Make minconfs dpow-aware in z_listunspent + z_listreceivedbyaddress

* Add dpow-related test files to test suite

* Add dpow simulation to regtest every 7 blocks

* Fix compiler errors

* Fix link error

* Fix stdout spam when running regtests

* Dpowminconfs for listreceivedbyaddress

* dpowconfs tests

* Start adding specific tests for dpowminconfs in listreceivedbyaddress

* Get dpowminconfs tests for listreceivedbyaddress working

* Add dpowminconfs to getreceivedbyaddress + listunspent

* Add test for listtransactions + getreceivedbyaddress support

* Reliably passing dpowminconf tests. We only check for notarized-ness now, not exact confirmation numbers, to avoid race conditions

* Poll for the expected notarization info before running further tests; add support for getbalance

* Migrate tx_height() to a place where asyncrpcoperation_sendmany.cpp can use it

* fix

* Teach GetFilteredNotes about dpowconfs

Many RPCs rely on this internal function, which now correctly uses
dpowconfs to filter by the minconf/maxconf parameters.

* Fix sendmany when using non-default minconf

* inline seems to make things happy

* cleanup

* Add some code to test z_sendmany, which points out https://github.com/jl777/komodo/issues/1247

* try this

* Use already calculated value of dpowconfs instead of calculating it again
This commit is contained in:
Duke Leto
2019-03-03 18:27:50 -05:00
committed by jl777
parent 5cb774d1fe
commit c99801952b
12 changed files with 324 additions and 98 deletions

View File

@@ -27,6 +27,8 @@
#define KOMODO_ASSETCHAINS_WAITNOTARIZE
#define KOMODO_PAXMAX (10000 * COIN)
extern int32_t NOTARIZED_HEIGHT;
uint256 NOTARIZED_HASH,NOTARIZED_DESTTXID;
#include <stdint.h>
#include <stdio.h>
@@ -930,6 +932,13 @@ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block)
}
notarized = 1;
}
// simulate DPoW in regtest mode for dpowconfs tests/etc
if ( Params().NetworkIDString() == "regtest" && ( height%7 == 0) ) {
notarized = 1;
sp->NOTARIZED_HEIGHT = height;
sp->NOTARIZED_HASH = block.GetHash();
sp->NOTARIZED_DESTTXID = txhash;
}
if ( IS_KOMODO_NOTARY != 0 && ASSETCHAINS_SYMBOL[0] == 0 )
printf("(tx.%d: ",i);
for (j=0; j<numvouts; j++)

View File

@@ -62,6 +62,25 @@ void init_string(struct return_string *s)
s->ptr[0] = '\0';
}
int tx_height( const uint256 &hash ){
int nHeight = 0;
CTransaction tx;
uint256 hashBlock;
if (!GetTransaction(hash, tx, hashBlock, true)) {
fprintf(stderr,"tx hash %s does not exist!\n", hash.ToString().c_str() );
}
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
return nHeight;
}
/************************************************************************
*
* Use the "writer" to accumulate text until done

View File

@@ -78,6 +78,7 @@ extern int32_t VERUS_MIN_STAKEAGE;
extern std::string DONATION_PUBKEY;
extern uint8_t ASSETCHAINS_PRIVATE;
extern int32_t USE_EXTERNAL_PUBKEY;
int tx_height( const uint256 &hash );
extern char NOTARYADDRS[64][36];
extern uint8_t NUM_NOTARIES;

View File

@@ -15,7 +15,6 @@
#include "komodo_defs.h"
#include "komodo_cJSON.h"
#include "notaries_staked.h"

View File

@@ -1366,7 +1366,8 @@ void komodo_statefname(char *fname,char *symbol,char *str)
fname[len - n] = 0;
else
{
printf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,ASSETCHAINS_SYMBOL,n,len,&fname[len - n]);
if ( strcmp(symbol,"REGTEST") != 0 )
printf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,ASSETCHAINS_SYMBOL,n,len,&fname[len - n]);
return;
}
}

View File

@@ -54,6 +54,8 @@ using namespace libzcash;
extern char ASSETCHAINS_SYMBOL[65];
int32_t komodo_dpowconfs(int32_t height,int32_t numconfs);
int tx_height( const uint256 &hash );
extern UniValue signrawtransaction(const UniValue& params, bool fHelp);
extern UniValue sendrawtransaction(const UniValue& params, bool fHelp);
@@ -1049,8 +1051,16 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
continue;
}
if (out.nDepth < mindepth_) {
continue;
if( mindepth_ > 1 ) {
int nHeight = tx_height(out.tx->GetHash());
int dpowconfs = komodo_dpowconfs(nHeight, out.nDepth);
if (dpowconfs < mindepth_) {
continue;
}
} else {
if (out.nDepth < mindepth_) {
continue;
}
}
const CScript &scriptPubKey = out.tx->vout[out.i].scriptPubKey;

View File

@@ -84,6 +84,8 @@ UniValue z_getoperationstatus_IMPL(const UniValue&, bool);
#define PLAN_NAME_MAX 8
#define VALID_PLAN_NAME(x) (strlen(x) <= PLAN_NAME_MAX)
int tx_height( const uint256 &hash );
std::string HelpRequiringPassphrase()
{
return pwalletMain && pwalletMain->IsCrypted()
@@ -933,9 +935,20 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp)
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (txout.scriptPubKey == scriptPubKey)
if (wtx.GetDepthInMainChain() >= nMinDepth)
nAmount += txout.nValue; // komodo_interest?
if (txout.scriptPubKey == scriptPubKey) {
int nDepth = wtx.GetDepthInMainChain();
if( nMinDepth > 1 ) {
int nHeight = tx_height(wtx.GetHash());
int dpowconfs = komodo_dpowconfs(nHeight, nDepth);
if (dpowconfs >= nMinDepth) {
nAmount += txout.nValue; // komodo_interest?
}
} else {
if (nDepth >= nMinDepth) {
nAmount += txout.nValue; // komodo_interest?
}
}
}
}
return ValueFromAmount(nAmount);
@@ -1013,8 +1026,18 @@ CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi
CAmount nReceived, nSent, nFee;
wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter);
if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
nBalance += nReceived;
int nDepth = wtx.GetDepthInMainChain();
if( nMinDepth > 1 ) {
int nHeight = tx_height(wtx.GetHash());
int dpowconfs = komodo_dpowconfs(nHeight, nDepth);
if (nReceived != 0 && dpowconfs >= nMinDepth) {
nBalance += nReceived;
}
} else {
if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) {
nBalance += nReceived;
}
}
nBalance -= nSent + nFee;
}
@@ -1186,10 +1209,20 @@ UniValue getbalance(const UniValue& params, bool fHelp)
list<COutputEntry> listReceived;
list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (wtx.GetDepthInMainChain() >= nMinDepth)
{
BOOST_FOREACH(const COutputEntry& r, listReceived)
nBalance += r.amount;
int nDepth = wtx.GetDepthInMainChain();
if( nMinDepth > 1 ) {
int nHeight = tx_height(wtx.GetHash());
int dpowconfs = komodo_dpowconfs(nHeight, nDepth);
if (dpowconfs >= nMinDepth) {
BOOST_FOREACH(const COutputEntry& r, listReceived)
nBalance += r.amount;
}
} else {
if (nDepth >= nMinDepth) {
BOOST_FOREACH(const COutputEntry& r, listReceived)
nBalance += r.amount;
}
}
BOOST_FOREACH(const COutputEntry& s, listSent)
nBalance -= s.amount;
@@ -1460,8 +1493,7 @@ UniValue sendmany(const UniValue& params, bool fHelp)
EnsureWalletIsUnlocked();
// Check funds
CAmount nBalance = pwalletMain->GetBalance();
//CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (totalAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@@ -1572,9 +1604,16 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts)
if (wtx.IsCoinBase() || !CheckFinalTx(wtx))
continue;
int nDepth = wtx.GetDepthInMainChain();
if (nDepth < nMinDepth)
continue;
int nDepth = wtx.GetDepthInMainChain();
if( nMinDepth > 1 ) {
int nHeight = tx_height(wtx.GetHash());
int dpowconfs = komodo_dpowconfs(nHeight, nDepth);
if (dpowconfs < nMinDepth)
continue;
} else {
if (nDepth < nMinDepth)
continue;
}
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
@@ -2851,8 +2890,16 @@ UniValue listunspent(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
BOOST_FOREACH(const COutput& out, vecOutputs) {
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue;
int nDepth = out.tx->GetDepthInMainChain();
if( nMinDepth > 1 ) {
int nHeight = tx_height(out.tx->GetHash());
int dpowconfs = komodo_dpowconfs(nHeight, nDepth);
if (dpowconfs < nMinDepth || dpowconfs > nMaxDepth)
continue;
} else {
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue;
}
CTxDestination address;
const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey;
@@ -3068,28 +3115,17 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
for (auto & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ);
int nHeight = 0;
CTransaction tx;
uint256 hashBlock;
int nHeight = tx_height(entry.jsop.hash);
int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations);
// Only return notarized results when minconf>1
if (nMinDepth > 1 && dpowconfs == 1)
continue;
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
obj.push_back(Pair("jsindex", (int)entry.jsop.js ));
obj.push_back(Pair("jsoutindex", (int)entry.jsop.n));
if (!GetTransaction(entry.jsop.hash, tx, hashBlock, true)) {
// TODO: should we throw JSONRPCError ?
fprintf(stderr,"tx hash %s does not exist!\n", entry.jsop.hash.ToString().c_str() );
}
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
// TODO: should we throw JSONRPCError ?
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations)));
obj.push_back(Pair("confirmations", dpowconfs));
obj.push_back(Pair("rawconfirmations", entry.confirmations));
bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(boost::get<libzcash::SproutPaymentAddress>(entry.address));
obj.push_back(Pair("spendable", hasSproutSpendingKey));
@@ -3105,25 +3141,17 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
for (auto & entry : saplingEntries) {
UniValue obj(UniValue::VOBJ);
int nHeight = tx_height(entry.op.hash);
int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations);
// Only return notarized results when minconf>1
if (nMinDepth > 1 && dpowconfs == 1)
continue;
obj.push_back(Pair("txid", entry.op.hash.ToString()));
obj.push_back(Pair("outindex", (int)entry.op.n));
int nHeight = 0;
CTransaction tx;
uint256 hashBlock;
if (!GetTransaction(entry.op.hash, tx, hashBlock, true)) {
// TODO: should we throw JSONRPCError ?
fprintf(stderr,"tx hash %s does not exist!\n", entry.op.hash.ToString().c_str() );
}
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
// TODO: should we throw JSONRPCError ?
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations)));
obj.push_back(Pair("confirmations", dpowconfs));
obj.push_back(Pair("rawconfirmations", entry.confirmations));
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
@@ -3144,7 +3172,6 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
return results;
}
UniValue fundrawtransaction(const UniValue& params, bool fHelp)
{
if (!EnsureWalletIsAvailable(fHelp))
@@ -3795,8 +3822,17 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign
pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
BOOST_FOREACH(const COutput& out, vecOutputs) {
if (out.nDepth < minDepth) {
continue;
int nDepth = out.tx->GetDepthInMainChain();
if( minDepth > 1 ) {
int nHeight = tx_height(out.tx->GetHash());
int dpowconfs = komodo_dpowconfs(nHeight, nDepth);
if (dpowconfs < minDepth) {
continue;
}
} else {
if (out.nDepth < minDepth) {
continue;
}
}
if (ignoreUnspendable && !out.fSpendable) {
@@ -3901,21 +3937,11 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
if (boost::get<libzcash::SproutPaymentAddress>(&zaddr) != nullptr) {
for (CSproutNotePlaintextEntry & entry : sproutEntries) {
UniValue obj(UniValue::VOBJ);
int nHeight = 0;
CTransaction tx;
uint256 hashBlock;
if (GetTransaction(entry.jsop.hash, tx, hashBlock, true)) {
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
} else {
fprintf(stderr,"tx hash %s does not exist!\n", entry.jsop.hash.ToString().c_str() );
}
int nHeight = tx_height(entry.jsop.hash);
int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations);
// Only return notarized results when minconf>1
if (nMinDepth > 1 && dpowconfs == 1)
continue;
obj.push_back(Pair("txid", entry.jsop.hash.ToString()));
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value()))));
@@ -3924,7 +3950,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
obj.push_back(Pair("jsindex", entry.jsop.js));
obj.push_back(Pair("jsoutindex", entry.jsop.n));
obj.push_back(Pair("rawconfirmations", entry.confirmations));
obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations)));
obj.push_back(Pair("confirmations", dpowconfs));
if (hasSpendingKey) {
obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop)));
}
@@ -3933,27 +3959,19 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
} else if (boost::get<libzcash::SaplingPaymentAddress>(&zaddr) != nullptr) {
for (SaplingNoteEntry & entry : saplingEntries) {
UniValue obj(UniValue::VOBJ);
int nHeight = 0;
CTransaction tx;
uint256 hashBlock;
if (GetTransaction(entry.op.hash, tx, hashBlock, true)) {
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
} else {
fprintf(stderr,"tx hash %s does not exist!\n", entry.op.hash.ToString().c_str() );
}
int nHeight = tx_height(entry.op.hash);
int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations);
// Only return notarized results when minconf>1
if (nMinDepth > 1 && dpowconfs == 1)
continue;
obj.push_back(Pair("txid", entry.op.hash.ToString()));
obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value()))));
obj.push_back(Pair("memo", HexStr(entry.memo)));
obj.push_back(Pair("outindex", (int)entry.op.n));
obj.push_back(Pair("rawconfirmations", entry.confirmations));
obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations)));
obj.push_back(Pair("confirmations", dpowconfs));
if (hasSpendingKey) {
obj.push_back(Pair("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op)));
}
@@ -8024,8 +8042,6 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp)
cp = CCinit(&C, EVAL_HEIR);
return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret));
}

View File

@@ -60,6 +60,9 @@ bool fPayAtLeastCustomFee = true;
#include "komodo_defs.h"
CBlockIndex *komodo_chainactive(int32_t height);
extern std::string DONATION_PUBKEY;
int32_t komodo_dpowconfs(int32_t height,int32_t numconfs);
int tx_height( const uint256 &hash );
/**
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
@@ -4966,11 +4969,21 @@ void CWallet::GetFilteredNotes(
CWalletTx wtx = p.second;
// Filter the transactions before checking for notes
if (!CheckFinalTx(wtx) ||
wtx.GetBlocksToMaturity() > 0 ||
wtx.GetDepthInMainChain() < minDepth ||
wtx.GetDepthInMainChain() > maxDepth) {
if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0)
continue;
if (minDepth > 1) {
int nHeight = tx_height(wtx.GetHash());
int nDepth = wtx.GetDepthInMainChain();
int dpowconfs = komodo_dpowconfs(nHeight,nDepth);
if ( dpowconfs < minDepth || dpowconfs > maxDepth) {
continue;
}
} else {
if ( wtx.GetDepthInMainChain() < minDepth ||
wtx.GetDepthInMainChain() > maxDepth) {
continue;
}
}
for (auto & pair : wtx.mapSproutNoteData) {

View File

@@ -60,6 +60,7 @@ extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
extern bool fPayAtLeastCustomFee;
//! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB