diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index 3aa076bb1..531cab7a6 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -7,7 +7,7 @@ komodo_gtest_SOURCES = \ gtest/test_checktransaction.cpp \ gtest/json_test_vectors.cpp \ gtest/json_test_vectors.h \ - gtest/test_foundersreward.cpp \ + # gtest/test_foundersreward.cpp \ gtest/test_wallet_zkeys.cpp \ gtest/test_jsonspirit.cpp \ gtest/test_tautology.cpp \ diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 319be2991..19f838908 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -23,6 +23,8 @@ using namespace std; +#include "komodo_interest.h" + static bool fCreateBlank; static map registers; @@ -322,6 +324,7 @@ vector ParseHexUO(map& o, string strKey) return ParseHexUV(o[strKey], strKey); } + static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) { int nHashType = SIGHASH_ALL; diff --git a/src/coins.cpp b/src/coins.cpp index fbc088d5f..4272ed611 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -383,18 +383,24 @@ const CScript &CCoinsViewCache::GetSpendFor(const CTxIn& input) const return coins->vout[input.prevout.n].scriptPubKey; } -uint32_t komodo_txtime(uint256 hash); +uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); -CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const +CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const { - if (tx.IsCoinBase()) + uint32_t timestamp,minutes; int64_t interest; + *interestp = 0; + if ( tx.IsCoinBase() != 0 ) return 0; - - CAmount nResult = 0; + CAmount value,nResult = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { - //fprintf(stderr,"i.%d time.%u\n",i,komodo_txtime(tx.vin[i].prevout.hash)); - nResult += GetOutputFor(tx.vin[i]).nValue; + value = GetOutputFor(tx.vin[i]).nValue; + nResult += value; + interest = komodo_interest(nHeight,value,tx.nLockTime,tiptime); +#ifdef KOMODO_ENABLE_INTEREST + nResult += interest; +#endif + (*interestp) += interest; } nResult += tx.GetJoinSplitValueIn(); diff --git a/src/coins.h b/src/coins.h index 7f1fc6124..123d8b4eb 100644 --- a/src/coins.h +++ b/src/coins.h @@ -85,12 +85,14 @@ public: //! version of the CTransaction; accesses to this value should probably check for nHeight as well, //! as new tx version will probably only be introduced at certain heights int nVersion; + uint32_t nLockTime; void FromTx(const CTransaction &tx, int nHeightIn) { fCoinBase = tx.IsCoinBase(); vout = tx.vout; nHeight = nHeightIn; nVersion = tx.nVersion; + nLockTime = tx.nLockTime; ClearUnspendable(); } @@ -510,7 +512,7 @@ public: * @param[in] tx transaction for which we are checking input total * @return Sum of value of all inputs (scriptSigs) */ - CAmount GetValueIn(const CTransaction& tx) const; + CAmount GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t prevblocktime) const; //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx) const; diff --git a/src/gtest/test_foundersreward.cpp b/src/gtest/test_foundersreward.cpp index b7515f55d..59f49dce8 100644 --- a/src/gtest/test_foundersreward.cpp +++ b/src/gtest/test_foundersreward.cpp @@ -14,6 +14,7 @@ #include #include "util.h" +#ifndef disable_founders // To run tests: // ./zcash-gtest --gtest_filter="founders_reward_test.*" @@ -178,3 +179,4 @@ TEST(founders_reward_test, per_address_reward_testnet) { SelectParams(CBaseChainParams::TESTNET); verifyNumberOfRewards(); } +#endif diff --git a/src/komodo.h b/src/komodo.h index cc4a085b2..c983fde35 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -19,29 +19,62 @@ #include #include #include +#include #include "uthash.h" +#include "komodo_interest.h" + #define KOMODO_TESTNET_EXPIRATION 60000 #define KOMODO_ELECTION_GAP 1000 #define KOMODO_PUBKEYS_HEIGHT(height) ((int32_t)(((((height)+KOMODO_ELECTION_GAP*.5)/KOMODO_ELECTION_GAP) + 1) * KOMODO_ELECTION_GAP)) -int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,NOTARIZED_HEIGHT,Num_nutxos,KOMODO_NUMNOTARIES = 64; +int32_t NUM_PRICES,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,NOTARIZED_HEIGHT,Num_nutxos,KOMODO_NUMNOTARIES = 64; std::string NOTARY_PUBKEY; uint8_t NOTARY_PUBKEY33[33]; uint256 NOTARIZED_HASH,NOTARIZED_BTCTXID; pthread_mutex_t komodo_mutex; +uint32_t *PVALS; struct nutxo_entry { UT_hash_handle hh; uint256 txhash; uint64_t voutmask; int32_t notaryid,height; } *NUTXOS; struct knotary_entry { UT_hash_handle hh; uint8_t pubkey[33],notaryid; }; struct knotaries_entry { int32_t height,numnotaries; struct knotary_entry *Notaries; } Pubkeys[10000]; struct notarized_checkpoint { uint256 notarized_hash,notarized_btctxid; int32_t nHeight,notarized_height; } *NPOINTS; int32_t NUM_NPOINTS; -int32_t komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts); +int32_t komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals); // add opreturn funcid // pricefeeds #define CRYPTO777_PUBSECPSTR "020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9" +#define MAX_CURRENCIES 32 +char CURRENCIES[][8] = { "USD", "EUR", "JPY", "GBP", "AUD", "CAD", "CHF", "NZD", // major currencies + "CNY", "RUB", "MXN", "BRL", "INR", "HKD", "TRY", "ZAR", "PLN", "NOK", "SEK", "DKK", "CZK", "HUF", "ILS", "KRW", "MYR", "PHP", "RON", "SGD", "THB", "BGN", "IDR", "HRK", + "KMD" }; + +uint32_t MINDENOMS[] = { 1000, 1000, 100000, 1000, 1000, 1000, 1000, 1000, // major currencies + 10000, 100000, 10000, 1000, 100000, 10000, 1000, 10000, 1000, 10000, 10000, 10000, 10000, 100000, 1000, 1000000, 1000, 10000, 1000, 1000, 10000, 1000, 10000000, 10000, // end of currencies +}; + +/*uint32_t PAX_val32(double val) +{ + uint32_t val32 = 0; struct price_resolution price; + if ( (price.Pval= val*1000000000) != 0 ) + { + if ( price.Pval > 0xffffffff ) + printf("Pval overflow error %lld\n",(long long)price.Pval); + else val32 = (uint32_t)price.Pval; + } + return(val32); +}*/ + +double PAX_val(uint32_t pval,int32_t baseid) +{ + //printf("PAX_val baseid.%d pval.%u\n",baseid,pval); + if ( baseid >= 0 && baseid < MAX_CURRENCIES ) + return(((double)pval / 1000000000.) / MINDENOMS[baseid]); + return(0.); +} + const char *Notaries[][2] = { { "jl777_testA", "03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828" }, @@ -81,37 +114,63 @@ const char *Notaries[][2] = { "titomane_SH", "035f49d7a308dd9a209e894321f010d21b7793461b0c89d6d9231a3fe5f68d9960" }, }; -uint64_t komodo_accrued_interest(int32_t height,int64_t paidinterest) -{ - static uint64_t *interests; static int32_t maxheight; - int32_t ind,incr = 100000; - if ( height >= maxheight ) - { - interests = (uint64_t *)realloc(interests,(maxheight + incr) * sizeof(*interests) * 2); - memset(&interests[maxheight << 1],0,incr * sizeof(*interests) * 2); - maxheight += incr; - } - ind = (height << 1); - if ( paidinterest < 0 ) // request - return(interests[ind]); - else - { - if ( interests[ind + 1] != paidinterest ) - { - interests[ind + 1] = paidinterest; - if ( height == 0 ) - height++; - for (; height> 8); + + return crc ^ ~0U; } int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp) @@ -165,6 +224,29 @@ int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t * return(len); } +int32_t dpow_readprices(uint8_t *data,uint32_t *timestampp,double *KMDBTCp,double *BTCUSDp,double *CNYUSDp,uint32_t *pvals) +{ + uint32_t kmdbtc,btcusd,cnyusd; int32_t i,n,len = 0; + len += iguana_rwnum(0,&data[len],sizeof(uint32_t),(void *)timestampp); + len += iguana_rwnum(0,&data[len],sizeof(uint32_t),(void *)&n); + len += iguana_rwnum(0,&data[len],sizeof(uint32_t),(void *)&kmdbtc); // /= 1000 + len += iguana_rwnum(0,&data[len],sizeof(uint32_t),(void *)&btcusd); // *= 1000 + len += iguana_rwnum(0,&data[len],sizeof(uint32_t),(void *)&cnyusd); + *KMDBTCp = ((double)kmdbtc / (1000000000. * 1000.)); + *BTCUSDp = ((double)btcusd / (1000000000. / 1000.)); + *CNYUSDp = ((double)cnyusd / 1000000000.); + for (i=0; i= '0' && c <= '9' ) @@ -241,7 +323,7 @@ int32_t komodo_threshold(int32_t height,uint64_t signedmask) for (i=0; i (numnotaries >> 1) || (wt > numnotaries/5 && (signedmask & 3) != 0) ) + if ( wt > (numnotaries >> 1) || (wt > 7 && (signedmask & 3) != 0) ) return(1); // N/2+1 || N/3 + devsig else return(0); } @@ -253,19 +335,8 @@ uint32_t komodo_txtime(uint256 hash) if (!GetTransaction(hash, tx, hashBlock, true)) { //printf("null GetTransaction\n"); - return(0); + return(tx.nLockTime); } - if (!hashBlock.IsNull()) { - BlockMap::iterator mi = mapBlockIndex.find(hashBlock); - if (mi != mapBlockIndex.end() && (*mi).second) - { - CBlockIndex* pindex = (*mi).second; - if (chainActive.Contains(pindex)) - return(pindex->GetBlockTime()); - } - //printf("cant find in iterator\n"); - } - //printf("null hashBlock\n"); return(0); } @@ -283,7 +354,7 @@ void komodo_nutxoadd(int32_t addflag,int32_t height,int32_t notaryid,uint256 txh HASH_ADD_KEYPTR(hh,NUTXOS,&np->txhash,sizeof(np->txhash),np); printf("Add NUTXO[%d] <- %s notaryid.%d t%u %s %llx\n",Num_nutxos,Notaries[notaryid][0],notaryid,komodo_txtime(txhash),txhash.ToString().c_str(),(long long)voutmask); if ( addflag != 0 ) - komodo_stateupdate(height,0,0,notaryid,txhash,voutmask,numvouts); + komodo_stateupdate(height,0,0,notaryid,txhash,voutmask,numvouts,0,0); Num_nutxos++; pthread_mutex_unlock(&komodo_mutex); } @@ -353,6 +424,11 @@ int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33) void komodo_notarized_update(int32_t nHeight,int32_t notarized_height,uint256 notarized_hash,uint256 notarized_btctxid) { struct notarized_checkpoint *np; + if ( notarized_height > nHeight ) + { + printf("komodo_notarized_update REJECT notarized_height %d > %d nHeight\n",notarized_height,nHeight); + return; + } NPOINTS = (struct notarized_checkpoint *)realloc(NPOINTS,(NUM_NPOINTS+1) * sizeof(*NPOINTS)); np = &NPOINTS[NUM_NPOINTS++]; memset(np,0,sizeof(*np)); @@ -379,14 +455,97 @@ int32_t komodo_notarizeddata(int32_t nHeight,uint256 *notarized_hashp,uint256 *n *notarized_hashp = np->notarized_hash; *notarized_btctxidp = np->notarized_btctxid; return(np->notarized_height); - } + } memset(notarized_hashp,0,sizeof(*notarized_hashp)); + return(0); +} + +void komodo_pvals(int32_t height,uint32_t *pvals,uint8_t numpvals) +{ + int32_t i,nonz; double KMDBTC,BTCUSD,CNYUSD; uint32_t kmdbtc,btcusd,cnyusd; + if ( numpvals >= 35 ) + { + for (nonz=i=0; i<32; i++) + { + if ( pvals[i] != 0 ) + nonz++; + printf("%u ",pvals[i]); + } + if ( nonz == 32 ) + { + kmdbtc = pvals[i++]; + btcusd = pvals[i++]; + cnyusd = pvals[i++]; + KMDBTC = ((double)kmdbtc / (1000000000. * 1000.)); + BTCUSD = ((double)btcusd / (1000000000. / 1000.)); + CNYUSD = ((double)cnyusd / 1000000000.); + PVALS = (uint32_t *)realloc(PVALS,(NUM_PRICES+1) * sizeof(*PVALS) * 36); + PVALS[36 * NUM_PRICES] = height; + memcpy(&PVALS[36 * NUM_PRICES + 1],pvals,sizeof(*pvals) * 35); + NUM_PRICES++; + printf("OP_RETURN.%d KMD %.8f BTC %.6f CNY %.6f NUM_PRICES.%d\n",height,KMDBTC,BTCUSD,CNYUSD,NUM_PRICES); + } + } +} + +int32_t komodo_baseid(char *origbase) +{ + int32_t i; char base[64]; + for (i=0; origbase[i]!=0&&i= 0 && (relid= komodo_baseid(rel)) >= 0 ) + { + for (i=NUM_PRICES-1; i>=0; i--) + { + ptr = &PVALS[36 * i]; + if ( *ptr < height ) + { + if ( (pvalb= ptr[1 + baseid]) != 0 ) + { + baseval = PAX_val(pvalb,baseid); + if ( relid == MAX_CURRENCIES ) + { + kmdbtc = ptr[1 + MAX_CURRENCIES]; + btcusd = ptr[1 + MAX_CURRENCIES + 1]; + if ( ptr[1 + USD] != 0 && kmdbtc != 0 && btcusd != 0 ) + { + usdval = PAX_val(ptr[1 + USD],USD); + KMDBTC = ((double)kmdbtc / (1000000000. * 1000.)); + BTCUSD = ((double)btcusd / (1000000000. / 1000.)); + KMDUSD = (KMDBTC * BTCUSD); + printf("base -> USD %f, BTC %f KMDUSD %f\n",baseval/usdval,BTCUSD,KMDUSD); + return(volume * (baseval / usdval) / KMDUSD); + } + } + else if ( (pvalr= ptr[1 + relid]) != 0 ) + { + relval = PAX_val(pvalr,relid); + printf("ht.%d [%d] base.(%u %f) rel.(%u %f) -> %llu\n",height,*ptr,pvalb,baseval,pvalr,relval,(long long)(COIN * (baseval / relval))); + return(volume * (baseval / relval)); + } + } + return(0); + } + } + } else printf("paxprice invalid base.%s %d, rel.%s %d\n",base,baseid,rel,relid); + return(0); +} + +int32_t komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numnotaries,uint8_t notaryid,uint256 txhash,uint64_t voutmask,uint8_t numvouts,uint32_t *pvals,uint8_t numpvals) +{ + static FILE *fp; static int32_t errs,didinit; char fname[512]; int32_t ht,k,i,func; uint8_t num,pubkeys[64][33]; #ifdef WIN32 sprintf(fname,"%s\\%s",GetDataDir(false).string().c_str(),(char *)"komodostate"); #else @@ -442,6 +601,16 @@ int32_t komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numno { //printf("D[%d]\n",ht); } + else if ( func == 'V' ) + { + int32_t numpvals; uint32_t pvals[128]; + numpvals = fgetc(fp); + if ( numpvals*sizeof(uint32_t) <= sizeof(pvals) && fread(pvals,sizeof(uint32_t),numpvals,fp) == numpvals ) + { + komodo_pvals(ht,pvals,numpvals); + printf("load pvals ht.%d numpvals.%d\n",ht,numpvals); + } else printf("error loading pvals[%d]\n",numpvals); + } else printf("illegal func.(%d %c)\n",func,func); } } else fp = fopen(fname,"wb+"); @@ -480,6 +649,17 @@ int32_t komodo_stateupdate(int32_t height,uint8_t notarypubs[][33],uint8_t numno if ( fwrite(&txhash,1,sizeof(txhash),fp) != sizeof(txhash) ) errs++; } + else if ( pvals != 0 && numpvals > 0 ) + { + fputc('V',fp); + if ( fwrite(&height,1,sizeof(height),fp) != sizeof(height) ) + errs++; + fputc(numpvals,fp); + if ( fwrite(pvals,sizeof(uint32_t),numpvals,fp) != numpvals ) + errs++; + komodo_pvals(height,pvals,numpvals); + printf("save pvals height.%d numpvals.%d\n",height,numpvals); + } else if ( height != 0 ) { //printf("func N ht.%d errs.%d\n",NOTARIZED_HEIGHT,errs); @@ -515,13 +695,13 @@ void komodo_init() } komodo_notarysinit(0,pubkeys,KOMODO_NUMNOTARIES); memset(&zero,0,sizeof(zero)); - komodo_stateupdate(0,0,0,0,zero,0,0); + komodo_stateupdate(0,0,0,0,zero,0,0,0,0); } } int32_t komodo_voutupdate(int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen,int32_t height,uint256 txhash,int32_t i,int32_t j,uint64_t *voutmaskp,int32_t *specialtxp,int32_t *notarizedheightp) { - int32_t k,opretlen,nid,len = 0; uint256 kmdtxid,btctxid; uint8_t crypto777[33]; + static uint256 zero; int32_t k,opretlen,nid,len = 0; uint256 kmdtxid,btctxid; uint8_t crypto777[33]; if ( scriptlen == 35 && scriptbuf[0] == 33 && scriptbuf[34] == 0xac ) { decode_hex(crypto777,33,(char *)CRYPTO777_PUBSECPSTR); @@ -565,7 +745,7 @@ int32_t komodo_voutupdate(int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen, opretlen = scriptbuf[len++]; opretlen = (opretlen << 8) + scriptbuf[len++]; } - if ( opretlen >= 32*2+4 ) + if ( opretlen >= 32*2+4 && strcmp("KMD",(char *)&scriptbuf[len+32*2+4]) == 0 ) { len += iguana_rwbignum(0,&scriptbuf[len],32,(uint8_t *)&kmdtxid); len += iguana_rwnum(0,&scriptbuf[len],4,(uint8_t *)notarizedheightp); @@ -573,15 +753,21 @@ int32_t komodo_voutupdate(int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen, //for (k=0; k NOTARIZED_HEIGHT ) + if ( *notarizedheightp > NOTARIZED_HEIGHT && *notarizedheightp < height ) { - static uint256 zero; + printf("ht.%d NOTARIZED.%d KMD.%s BTCTXID.%s (%s)\n",height,*notarizedheightp,kmdtxid.ToString().c_str(),btctxid.ToString().c_str(),(char *)&scriptbuf[len]); NOTARIZED_HEIGHT = *notarizedheightp; NOTARIZED_HASH = kmdtxid; NOTARIZED_BTCTXID = btctxid; - komodo_stateupdate(height,0,0,0,zero,0,0); - } + komodo_stateupdate(height,0,0,0,zero,0,0,0,0); + } else printf("reject ht.%d NOTARIZED.%d KMD.%s BTCTXID.%s (%s)\n",height,*notarizedheightp,kmdtxid.ToString().c_str(),btctxid.ToString().c_str(),(char *)&scriptbuf[len]); + } + else if ( i == 0 && scriptbuf[len] == 'P' ) + { + double KMDBTC,BTCUSD,CNYUSD; uint32_t numpvals,timestamp,pvals[128]; + numpvals = dpow_readprices(&scriptbuf[++len],×tamp,&KMDBTC,&BTCUSD,&CNYUSD,pvals); + komodo_stateupdate(height,0,0,0,zero,0,0,pvals,numpvals); + printf("vout OP_RETURN.%d prices numpvals.%d opretlen.%d\n",height,numpvals,opretlen); } } return(notaryid); @@ -655,7 +841,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) if ( numvalid > 13 ) { memset(&txhash,0,sizeof(txhash)); - komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0); + komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0,0,0); } printf("new notaries.%d newheight.%d from height.%d\n",numvouts-1,KOMODO_PUBKEYS_HEIGHT(height),height); } @@ -670,7 +856,7 @@ void komodo_disconnect(CBlockIndex *pindex,CBlock& block) //uint256 zero; //printf("disconnect ht.%d\n",pindex->nHeight); //memset(&zero,0,sizeof(zero)); - //komodo_stateupdate(-pindex->nHeight,0,0,0,zero,0,0); + //komodo_stateupdate(-pindex->nHeight,0,0,0,zero,0,0,0,0); } int32_t komodo_block2height(CBlock *block) @@ -719,4 +905,77 @@ void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height) } } +int32_t komodo_opreturnscript(uint8_t *script,uint8_t type,uint8_t *opret,int32_t opretlen) +{ + int32_t offset = 0; + script[offset++] = 0x6a; + opretlen++; + if ( opretlen >= 0x4c ) + { + if ( opretlen > 0xff ) + { + script[offset++] = 0x4d; + script[offset++] = opretlen & 0xff; + script[offset++] = (opretlen >> 8) & 0xff; + } + else + { + script[offset++] = 0x4c; + script[offset++] = opretlen; + } + } else script[offset++] = opretlen; + script[offset++] = type; + memcpy(&script[offset],opret,opretlen); + return(opretlen + offset); +} + +int32_t komodo_opreturn(uint8_t *opret,int32_t maxsize) +{ + static uint32_t lastcrc; + FILE *fp; char fname[512]; uint32_t crc32,check,timestamp; int32_t i,n,retval,fsize,len=0; uint8_t data[8192]; +#ifdef WIN32 + sprintf(fname,"%s\\%s",GetDataDir(false).string().c_str(),(char *)"komodofeed"); +#else + sprintf(fname,"%s/%s",GetDataDir(false).string().c_str(),(char *)"komodofeed"); +#endif + if ( (fp= fopen(fname,"rb")) != 0 ) + { + fseek(fp,0,SEEK_END); + fsize = (int32_t)ftell(fp); + rewind(fp); + if ( fsize <= maxsize-4 && fsize <= sizeof(data) && fsize > sizeof(crc32) ) + { + if ( (retval= (int32_t)fread(data,1,fsize,fp)) == fsize ) + { + len = iguana_rwnum(0,data,sizeof(crc32),(void *)&crc32); + check = calc_crc32(0,data+sizeof(crc32),(int32_t)(fsize-sizeof(crc32))); + if ( check == crc32 ) + { + double KMDBTC,BTCUSD,CNYUSD; uint32_t pvals[128]; + dpow_readprices(&data[len],×tamp,&KMDBTC,&BTCUSD,&CNYUSD,pvals); + if ( 0 && lastcrc != crc32 ) + { + for (i=0; i<32; i++) + printf("%u ",pvals[i]); + printf("t%u n.%d KMD %f BTC %f CNY %f (%f)\n",timestamp,n,KMDBTC,BTCUSD,CNYUSD,CNYUSD!=0?1./CNYUSD:0); + } + if ( timestamp > time(NULL)-600 ) + { + n = komodo_opreturnscript(opret,'P',data+sizeof(crc32),(int32_t)(fsize-sizeof(crc32))); + if ( 0 && lastcrc != crc32 ) + { + for (i=0; i maxsize.%d or data[%d]\n",fsize,maxsize,(int32_t)sizeof(data)); + fclose(fp); + } + return(n); +} + #endif diff --git a/src/komodo_interest.h b/src/komodo_interest.h new file mode 100644 index 000000000..f86d29267 --- /dev/null +++ b/src/komodo_interest.h @@ -0,0 +1,62 @@ + +//#define KOMODO_ENABLE_INTEREST + +#define KOMODO_INTEREST ((uint64_t)(0.05 * COIN)) +#define dstr(x) ((double)(x)/COIN) + +uint64_t komodo_accrued_interest(int32_t height,int64_t paidinterest) +{ + static uint64_t *interests; static int32_t maxheight; + uint64_t total; int32_t ind,incr = 100000; + if ( height >= maxheight ) + { + interests = (uint64_t *)realloc(interests,(maxheight + incr) * sizeof(*interests) * 2); + memset(&interests[maxheight << 1],0,incr * sizeof(*interests) * 2); + maxheight += incr; + } + ind = (height << 1); + if ( paidinterest < 0 ) // request + return(interests[ind]); + else + { + if ( interests[ind + 1] != paidinterest ) + { + interests[ind + 1] = paidinterest; + if ( height == 0 ) + interests[ind] = interests[ind + 1]; + else interests[ind] = interests[ind - 2] + interests[ind + 1]; + total = interests[ind]; + for (++height; height= LOCKTIME_THRESHOLD && tiptime != 0 && nLockTime < tiptime && nValue >= COIN ) + { + if ( (minutes= (tiptime - nLockTime) / 60) > 60 ) + { + numerator = (nValue * KOMODO_INTEREST); + denominator = (((uint64_t)365 * 24 * 60) / minutes); + if ( denominator == 0 ) + denominator = 1; // max KOMODO_INTEREST per transfer, do it at least annually! + interest = (numerator / denominator) / COIN; + fprintf(stderr,"komodo_interest %lld %.8f nLockTime.%u tiptime.%u minutes.%d interest %lld %.8f (%llu / %llu)\n",(long long)nValue,dstr(nValue),nLockTime,tiptime,minutes,(long long)interest,dstr(interest),(long long)numerator,(long long)denominator); + } + } + return(interest); +} diff --git a/src/main.cpp b/src/main.cpp index 8331c9f1a..c10113b4f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -699,9 +699,23 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) return true; if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; + if ( (int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime < nBlockTime-3600 ) + { + fprintf(stderr,"IsFinalTx reject locktime %u vs nBlockTime %u\n",tx.nLockTime,(uint32_t)nBlockTime); + return(false); // need to prevent pastdating tx + } BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (!txin.IsFinal()) + { + if ( txin.nSequence == 0xfffffffe && (int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockTime ) + { + + } + else if (!txin.IsFinal()) + { + printf("non-final txin seq.%x\n",txin.nSequence); return false; + } + } return true; } @@ -1037,38 +1051,44 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectAbsurdFee) +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,bool* pfMissingInputs, bool fRejectAbsurdFee) { AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; - if (!CheckTransaction(tx, state)) + { + fprintf(stderr,"AcceptToMemoryPool CheckTransaction failed\n"); return error("AcceptToMemoryPool: CheckTransaction failed"); - + } // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), - REJECT_INVALID, "coinbase"); - + { + fprintf(stderr,"AcceptToMemoryPool coinbase as individual tx\n"); + return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),REJECT_INVALID, "coinbase"); + } // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().RequireStandard() && !IsStandardTx(tx, reason)) - return state.DoS(0, - error("AcceptToMemoryPool: nonstandard transaction: %s", reason), - REJECT_NONSTANDARD, reason); - + { + fprintf(stderr,"AcceptToMemoryPool nonstandard transaction: %s\n",reason.c_str()); + return state.DoS(0,error("AcceptToMemoryPool: nonstandard transaction: %s", reason),REJECT_NONSTANDARD, reason); + } // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + { + fprintf(stderr,"AcceptToMemoryPool non-final\n"); return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); - - // is it already in the memory pool? + } + // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) + { + fprintf(stderr,"already in mempool\n"); return false; + } // Check for conflicts with in-memory transactions { @@ -1079,6 +1099,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now + fprintf(stderr,"Disable replacement feature for now\n"); return false; } } @@ -1086,6 +1107,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { if (pool.mapNullifiers.count(nf)) { + fprintf(stderr,"pool.mapNullifiers.count\n"); return false; } } @@ -1095,7 +1117,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { CCoinsView dummy; CCoinsViewCache view(&dummy); - + int64_t interest; CAmount nValueIn = 0; { LOCK(pool.cs); @@ -1104,7 +1126,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // do we already have it? if (view.HaveCoins(hash)) + { + fprintf(stderr,"view.HaveCoins(hash) error\n"); return false; + } // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), @@ -1113,6 +1138,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (!view.HaveCoins(txin.prevout.hash)) { if (pfMissingInputs) *pfMissingInputs = true; + fprintf(stderr,"do all inputs exist?\n"); return false; } } @@ -1130,7 +1156,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Bring the best block into scope view.GetBestBlock(); - nValueIn = view.GetValueIn(tx); + nValueIn = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime); // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool view.SetBackend(dummy); @@ -1148,8 +1174,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.DoS(0, - error("AcceptToMemoryPool: too many sigops %s, %d > %d", + return state.DoS(0, error("AcceptToMemoryPool: too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); @@ -1190,16 +1215,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), REJECT_INSUFFICIENTFEE, "rate limited free transaction"); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000); + hash.ToString(), nFees, ::minRelayTxFee.GetFee(nSize) * 10000); // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. @@ -2098,6 +2121,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; + int64_t interest,sum = 0; unsigned int nSigOps = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; @@ -2131,7 +2155,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); - +//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) @@ -2151,14 +2175,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); - nFees += view.GetValueIn(tx)-tx.GetValueOut(); - + nFees += view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime) - tx.GetValueOut(); + sum += interest; std::vector vChecks; if (!ContextualCheckInputs(tx, state, view, fScriptChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } - + komodo_accrued_interest(pindex->nHeight,sum); CTxUndo undoDummy; if (i > 0) { blockundo.vtxundo.push_back(CTxUndo()); @@ -3076,19 +3100,26 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d) vs %d", __func__, nHeight,pcheckpoint->nHeight)); else { - int32_t notarized_height; uint256 notarized_hash,notarized_btctxid; + int32_t notarized_height; uint256 notarized_hash,notarized_btctxid; CBlockIndex *notary; notarized_height = komodo_notarizeddata(chainActive.Tip()->nHeight,¬arized_hash,¬arized_btctxid); - //printf("nHeight.%d -> (%d %s)\n",chainActive.Tip()->nHeight,notarized_height,notarized_hash.ToString().c_str()); - if ( nHeight < notarized_height ) + if ( (notary= mapBlockIndex[notarized_hash]) != 0 ) { - fprintf(stderr,"nHeight.%d < NOTARIZED_HEIGHT.%d\n",nHeight,notarized_height); - return state.DoS(100, error("%s: forked chain older than last notarized (height %d) vs %d", __func__,nHeight, notarized_height)); - } - else if ( nHeight == notarized_height && memcmp(&hash,¬arized_hash,sizeof(hash)) != 0 ) - { - fprintf(stderr,"nHeight.%d == NOTARIZED_HEIGHT.%d, diff hash\n",nHeight,notarized_height); - return state.DoS(100, error("%s: forked chain at notarized (height %d) with different hash", __func__, notarized_height)); - } + //printf("nHeight.%d -> (%d %s)\n",chainActive.Tip()->nHeight,notarized_height,notarized_hash.ToString().c_str()); + if ( notary->nHeight == notarized_height ) // if notarized_hash not in chain, reorg + { + if ( nHeight < notarized_height ) + { + fprintf(stderr,"nHeight.%d < NOTARIZED_HEIGHT.%d\n",nHeight,notarized_height); + return state.DoS(100, error("%s: forked chain older than last notarized (height %d) vs %d", __func__,nHeight, notarized_height)); + } + else if ( nHeight == notarized_height && memcmp(&hash,¬arized_hash,sizeof(hash)) != 0 ) + { + fprintf(stderr,"nHeight.%d == NOTARIZED_HEIGHT.%d, diff hash\n",nHeight,notarized_height); + return state.DoS(100, error("%s: forked chain at notarized (height %d) with different hash", __func__, notarized_height)); + } + } else fprintf(stderr,"notary_hash %s ht.%d at ht.%d\n",notarized_hash.ToString().c_str(),notarized_height,notary->nHeight); + } else if ( notarized_height > 0 ) + fprintf(stderr,"couldnt find notary_hash %s ht.%d\n",notarized_hash.ToString().c_str(),notarized_height); } } diff --git a/src/miner.cpp b/src/miner.cpp index 56a6aa515..c14a76d53 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -96,6 +96,8 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); } +int32_t komodo_opreturn(uint8_t *opret,int32_t maxsize); + CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) { const CChainParams& chainparams = Params(); @@ -228,6 +230,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Collect transactions into block uint64_t nBlockSize = 1000; uint64_t nBlockTx = 0; + int64_t interest; int nBlockSigOps = 100; bool fSortedByFee = (nBlockPrioritySize <= 0); @@ -275,7 +278,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) if (!view.HaveInputs(tx)) continue; - CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut(); + CAmount nTxFees = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime)-tx.GetValueOut(); nTxSigOps += GetP2SHSigOpCount(tx, view); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) @@ -329,13 +332,25 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) // Create coinbase tx CMutableTransaction txNew; + //txNew.nLockTime = (uint32_t)time(NULL) - 60; txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); - txNew.vout.resize(1); + int32_t i,opretlen; uint8_t opret[8192],*ptr; + if ( (opretlen= komodo_opreturn(opret,sizeof(opret))) > 0 ) + { + txNew.vout.resize(2); + txNew.vout[1].scriptPubKey.resize(opretlen); + ptr = (uint8_t *)txNew.vout[1].scriptPubKey.data(); + for (i=0; ivtx[0] = txNew; @@ -488,6 +503,7 @@ void static BitcoinMiner(CWallet *pwallet) // // Create new block // + uint32_t starttime = (uint32_t)time(NULL); unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); CBlockIndex* pindexPrev = chainActive.Tip(); @@ -499,17 +515,17 @@ void static BitcoinMiner(CWallet *pwallet) } CBlock *pblock = &pblocktemplate->block; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); - LogPrintf("Running ZcashMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); // // Search // - int32_t notaryid; - int64_t nStart = GetTime(); + int32_t notaryid; uint32_t savebits; int64_t nStart = GetTime(); + savebits = pblock->nBits; if ( komodo_chosennotary(¬aryid,pindexPrev->nHeight+1,NOTARY_PUBKEY33) > 0 ) { + pblock->nBits = KOMODO_MINDIFF_NBITS; //fprintf(stderr,"I am the chosen one for ht.%d\n",pindexPrev->nHeight+1); } arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); @@ -549,11 +565,14 @@ void static BitcoinMiner(CWallet *pwallet) if (UintToArith256(pblock->GetHash()) > hashTarget) { return false; } + /* if ( pblock->nBits == KOMODO_MINDIFF_NBITS && time(NULL) < starttime+50 ) + sleep(starttime+50-time(NULL));*/ // Found a solution SetThreadPriority(THREAD_PRIORITY_NORMAL); LogPrintf("ZcashMiner:\n"); LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex()); + if (ProcessBlockFound(pblock, *pwallet, reservekey)) { // Ignore chain updates caused by us std::lock_guard lock{m_cs}; @@ -595,6 +614,7 @@ void static BitcoinMiner(CWallet *pwallet) // Update nNonce and nTime pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); + pblock->nBits = savebits; UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks) { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 2e83773c4..61e756b2f 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -267,8 +267,7 @@ public: friend bool operator==(const CTxOut& a, const CTxOut& b) { - return (a.nValue == b.nValue && - a.scriptPubKey == b.scriptPubKey); + return (a.nValue == b.nValue && a.scriptPubKey == b.scriptPubKey); } friend bool operator!=(const CTxOut& a, const CTxOut& b) diff --git a/src/rest.cpp b/src/rest.cpp index 7c238d506..d278198b4 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -506,6 +506,7 @@ static bool rest_getutxos(AcceptedConnection* conn, utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer)); utxo.push_back(Pair("height", (int32_t)coin.nHeight)); utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); + //utxo.push_back(Pair("interest", ValueFromAmount(komodo_interest(coin.out.nValue,coin.nLockTime,chainActive.Tip()->nTime)))); // include the script in a json output Object o; diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 4c92979b2..eed707d58 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -380,6 +380,35 @@ Value gettxoutsetinfo(const Array& params, bool fHelp) return ret; } +uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); +uint32_t komodo_txtime(uint256 hash); +uint64_t komodo_paxprice(int32_t height,char *base,char *rel,uint64_t basevolume); + +Value paxprice(const Array& params, bool fHelp) +{ + if ( fHelp || params.size() < 3 || params.size() > 4 ) + throw runtime_error("paxprice \"base\" \"rel\" height\n"); + LOCK(cs_main); + Object ret; uint64_t pricetoshis,basevolume=0,relvolume; + std::string base = params[0].get_str(); + std::string rel = params[1].get_str(); + int32_t height = atoi(params[2].get_str().c_str()); + if ( params.size() == 4 ) + basevolume = AmountFromValue(params[3]); + if ( basevolume == 0 ) + basevolume = COIN; + relvolume = komodo_paxprice(height,(char *)base.c_str(),(char *)rel.c_str(),basevolume); + ret.push_back(Pair("base", base)); + ret.push_back(Pair("rel", rel)); + ret.push_back(Pair("height", height)); + if ( basevolume != 0 ) + { + ret.push_back(Pair("price",((double)relvolume / (double)basevolume))); + ret.push_back(Pair("relvolume", ValueFromAmount(relvolume))); + } + return ret; +} + Value gettxout(const Array& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) @@ -448,9 +477,17 @@ Value gettxout(const Array& params, bool fHelp) ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) ret.push_back(Pair("confirmations", 0)); - else - ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); + else ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); + + CBlockIndex *pblockindex = chainActive[coins.nHeight]; + uint64_t interest; uint32_t timestamp=0; + if ( pblockindex != 0 ) + timestamp = pblockindex->nTime; // this is approx, but cant figure out how to get tx here + interest = komodo_interest(coins.nHeight,coins.vout[n].nValue,timestamp,pindex->nTime); + //fprintf(stderr,"nValue %llu lock.%u:%u nTime.%u -> %llu\n",(long long)coins.vout[n].nValue,coins.nLockTime,timestamp,pindex->nTime,(long long)interest); + ret.push_back(Pair("interest", ValueFromAmount(interest))); + Object o; ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); ret.push_back(Pair("scriptPubKey", o)); diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 2841a3793..04fc84c9d 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -105,7 +105,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "z_sendmany", 2}, { "z_getoperationstatus", 0}, { "z_getoperationresult", 0}, - { "z_importkey", 1 } + { "z_importkey", 1 }, + { "paxprice", 3 }, }; class CRPCConvertTable diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 5cca9df9d..46340b5a4 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -96,6 +96,8 @@ Array TxJoinSplitToJSON(const CTransaction& tx) { return vjoinsplit; } +uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); + void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) { entry.push_back(Pair("txid", tx.GetHash().GetHex())); @@ -119,10 +121,19 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry) } entry.push_back(Pair("vin", vin)); Array vout; + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex *pindex = it->second; + uint64_t interest; for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; Object out; out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + if ( pindex != 0 && tx.nLockTime != 0 ) + { + interest = komodo_interest(pindex->nHeight,txout.nValue,tx.nLockTime,pindex->nTime); + fprintf(stderr,"TxtoJSON interest %llu %.8f\n",(long long)interest,(double)interest/COIN); + out.push_back(Pair("interest", ValueFromAmount(interest))); + } out.push_back(Pair("n", (int64_t)i)); Object o; ScriptPubKeyToJSON(txout.scriptPubKey, o, true); diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index a18b9ec72..6becec534 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -300,6 +300,7 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "verifytxoutproof", &verifytxoutproof, true }, { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, { "blockchain", "verifychain", &verifychain, true }, + { "blockchain", "paxprice", &paxprice, true }, /* Mining */ { "mining", "getblocktemplate", &getblocktemplate, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 6402a7a16..a721c6381 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -243,6 +243,7 @@ extern json_spirit::Value getblockhash(const json_spirit::Array& params, bool fH extern json_spirit::Value getblock(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gettxoutsetinfo(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value paxprice(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getchaintips(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value invalidateblock(const json_spirit::Array& params, bool fHelp); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c2b0a7017..55e7983e5 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -520,9 +520,9 @@ BOOST_AUTO_TEST_CASE(test_Get) t1.vout.resize(2); t1.vout[0].nValue = 90*CENT; t1.vout[0].scriptPubKey << OP_1; - + int64_t interest; BOOST_CHECK(AreInputsStandard(t1, coins)); - BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT); + BOOST_CHECK_EQUAL(coins.GetValueIn(0,&interest,t1,0), (50+21+22)*CENT); // Adding extra junk to the scriptSig should make it non-standard: t1.vin[0].scriptSig << OP_11; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 4d6608570..39b98f0f7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -610,7 +610,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (txout.scriptPubKey == scriptPubKey) if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; + nAmount += txout.nValue; // komodo_interest? } return ValueFromAmount(nAmount); @@ -666,7 +666,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; + nAmount += txout.nValue; // komodo_interest? } } @@ -1140,7 +1140,7 @@ Value ListReceived(const Array& params, bool fByAccounts) continue; tallyitem& item = mapTally[address]; - item.nAmount += txout.nValue; + item.nAmount += txout.nValue; // komodo_interest? item.nConf = min(item.nConf, nDepth); item.txids.push_back(wtx.GetHash()); if (mine & ISMINE_WATCH_ONLY) @@ -2259,6 +2259,8 @@ Value resendwallettransactions(const Array& params, bool fHelp) return result; } +uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); + Value listunspent(const Array& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -2363,6 +2365,17 @@ Value listunspent(const Array& params, bool fHelp) } } entry.push_back(Pair("amount",ValueFromAmount(nValue))); + if ( out.tx->nLockTime != 0 ) + { + BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); + CBlockIndex *pindex = it->second; + uint64_t interest; + if ( pindex != 0 ) + { + interest = komodo_interest(pindex->nHeight,nValue,out.tx->nLockTime,pindex->nTime); + entry.push_back(Pair("interest",ValueFromAmount(interest))); + } + } entry.push_back(Pair("confirmations",out.nDepth)); entry.push_back(Pair("spendable", out.fSpendable)); results.push_back(entry); @@ -2880,7 +2893,7 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) { } } - CAmount nValue = out.tx->vout[out.i].nValue; + CAmount nValue = out.tx->vout[out.i].nValue; // komodo_interest balance += nValue; } return balance; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1a865a966..3a25d8363 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1312,7 +1312,7 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.vout.size()) if (IsMine(prev.vout[txin.prevout.n]) & filter) - return prev.vout[txin.prevout.n].nValue; + return prev.vout[txin.prevout.n].nValue; // komodo_interest? } } return 0; @@ -2333,6 +2333,8 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int return true; } +uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); + bool CWallet::SelectCoins(const CAmount& nTargetValue, set >& setCoinsRet, CAmount& nValueRet, bool& fOnlyCoinbaseCoinsRet, bool& fNeedCoinbaseCoinsRet, const CCoinControl* coinControl) const { // Output parameter fOnlyCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos. @@ -2369,11 +2371,17 @@ bool CWallet::SelectCoins(const CAmount& nTargetValue, set return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected()) { + uint64_t interest; BOOST_FOREACH(const COutput& out, vCoins) { if(!out.fSpendable) continue; nValueRet += out.tx->vout[out.i].nValue; + interest = komodo_interest(chainActive.Tip()->nHeight,out.tx->vout[out.i].nValue,out.tx->nLockTime,chainActive.Tip()->nTime); +#ifdef KOMODO_ENABLE_INTEREST + nValueRet += interest; +#endif + fprintf(stderr,"interest %llu from %llu lock.%u tip.%u\n",(long long)interest,(long long)out.tx->vout[out.i].nValue,out.tx->nLockTime,chainActive.Tip()->nTime); setCoinsRet.insert(make_pair(out.tx, out.i)); } return (nValueRet >= nTargetValue); @@ -2411,27 +2419,34 @@ bool CWallet::CreateTransaction(const vector& vecSend, wtxNew.BindWallet(this); CMutableTransaction txNew; - // Discourage fee sniping. - // - // However because of a off-by-one-error in previous versions we need to - // neuter it by setting nLockTime to at least one less than nBestHeight. - // Secondly currently propagation of transactions created for block heights - // corresponding to blocks that were just mined may be iffy - transactions - // aren't re-accepted into the mempool - we additionally neuter the code by - // going ten blocks back. Doesn't yet do anything for sniping, but does act - // to shake out wallet bugs like not showing nLockTime'd transactions at - // all. - txNew.nLockTime = std::max(0, chainActive.Height() - 10); - - // Secondly occasionally randomly pick a nLockTime even further back, so - // that transactions that are delayed after signing for whatever reason, - // e.g. high-latency mix networks and some CoinJoin implementations, have - // better privacy. - if (GetRandInt(10) == 0) - txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); - - assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); - assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + if ( 0 ) + { + // Discourage fee sniping. + // + // However because of a off-by-one-error in previous versions we need to + // neuter it by setting nLockTime to at least one less than nBestHeight. + // Secondly currently propagation of transactions created for block heights + // corresponding to blocks that were just mined may be iffy - transactions + // aren't re-accepted into the mempool - we additionally neuter the code by + // going ten blocks back. Doesn't yet do anything for sniping, but does act + // to shake out wallet bugs like not showing nLockTime'd transactions at + // all. + txNew.nLockTime = std::max(0, chainActive.Height() - 10); + + // Secondly occasionally randomly pick a nLockTime even further back, so + // that transactions that are delayed after signing for whatever reason, + // e.g. high-latency mix networks and some CoinJoin implementations, have + // better privacy. + if (GetRandInt(10) == 0) + txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); + + assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); + assert(txNew.nLockTime < LOCKTIME_THRESHOLD); + } + else + { + txNew.nLockTime = (uint32_t)chainActive.Tip()->nTime + 1; // set to a time close to now + } { LOCK2(cs_main, cs_wallet);