diff --git a/src/Makefile.am b/src/Makefile.am index 7e1647a6c..e6c1fbb14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -524,6 +524,11 @@ if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif +if ENABLE_TESTS +libbitcoin_server_a_SOURCES += rpc/testtransactions.cpp +endif + + # cli: zcash-cli libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) diff --git a/src/assetchains.old b/src/assetchains.old index a0cbd3b9c..fd472eff9 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -51,3 +51,4 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=MORTY -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -addnode=138.201.136.145 & ./komodod -pubkey=$pubkey -ac_name=VOTE2019 -ac_supply=123651638 -ac_public=1 -addnode=95.213.238.98 & ./komodod -pubkey=$pubkey -ac_name=KOIN -ac_supply=125000000 -addnode=3.0.32.10 & +~/hush3/src/komodod -pubkey=$pubkey -ac_name=HUSH3 -ac_sapling=1 -ac_reward=0,1125000000,562500000 -ac_halving=129,340000,840000 -ac_end=128,340000,5422111 -ac_eras=3 -ac_blocktime=150 -ac_cc=2 -ac_ccenable=228,234,235,236,241 -ac_founders=1 -ac_supply=6178674 -ac_perc=11111111 -clientname=GoldenSandtrout -addnode=188.165.212.101 -ac_cclib=hush3 -ac_script=76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac & diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 247a0f2ec..13c66d358 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -18,17 +18,24 @@ #define CC_PAYMENTS_H #include "CCinclude.h" +#include #define PAYMENTS_TXFEE 10000 +#define PAYMENTS_MERGEOFSET 10 // 100? +extern std::vector > vAddressSnapshot; +extern int32_t lastSnapShotHeight; bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr); #endif + diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 3dbe6a87c..238131d59 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -17,34 +17,41 @@ #ifndef CC_PRICES_H #define CC_PRICES_H +#include "komodo_defs.h" #include "CCinclude.h" int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); +extern void GetKomodoEarlytxidScriptPub(); +extern CScript KOMODO_EARLYTXID_SCRIPTPUB; -#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +// #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) // defined in komodo_defs.h #define PRICES_TXFEE 10000 #define PRICES_MAXLEVERAGE 777 #define PRICES_SMOOTHWIDTH 1 #define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 -#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) -#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) -#define PRICES_MULT (KOMODO_MAXPRICES * 2) -#define PRICES_DIV (KOMODO_MAXPRICES * 3) -#define PRICES_INV (KOMODO_MAXPRICES * 4) -#define PRICES_MDD (KOMODO_MAXPRICES * 5) -#define PRICES_MMD (KOMODO_MAXPRICES * 6) -#define PRICES_MMM (KOMODO_MAXPRICES * 7) -#define PRICES_DDD (KOMODO_MAXPRICES * 8) +#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) // actually 1111 1000 0000 0000 +#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) // 0000 1000 0000 0000 +#define PRICES_MULT (KOMODO_MAXPRICES * 2) // 0001 0000 0000 0000 +#define PRICES_DIV (KOMODO_MAXPRICES * 3) // 0001 1000 0000 0000 +#define PRICES_INV (KOMODO_MAXPRICES * 4) // 0010 0000 0000 0000 +#define PRICES_MDD (KOMODO_MAXPRICES * 5) // 0010 1000 0000 0000 +#define PRICES_MMD (KOMODO_MAXPRICES * 6) // 0011 0000 0000 0000 +#define PRICES_MMM (KOMODO_MAXPRICES * 7) // 0011 1000 0000 0000 +#define PRICES_DDD (KOMODO_MAXPRICES * 8) // 0100 0000 0000 0000 + +#define PRICES_NORMFACTOR (int64_t)(SATOSHIDEN) +#define PRICES_POINTFACTOR (int64_t)10000 bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); -UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount); -UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid); -UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight); -UniValue PricesCashout(uint64_t txfee,uint256 bettxid); +UniValue PricesBet(int64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); +UniValue PricesAddFunding(int64_t txfee,uint256 bettxid,int64_t amount); +UniValue PricesSetcostbasis(int64_t txfee,uint256 bettxid); +UniValue PricesRekt(int64_t txfee,uint256 bettxid,int32_t rektheight); +UniValue PricesCashout(int64_t txfee,uint256 bettxid); UniValue PricesInfo(uint256 bettxid,int32_t refheight); -UniValue PricesList(); +UniValue PricesList(uint32_t filter, CPubKey mypk); +UniValue PricesGetOrderbook(); #endif diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index db1ee7475..75d804df5 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -229,7 +229,7 @@ uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t entropyvout,i CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk, std::vector>* vData = NULL); CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2, std::vector>* vData = NULL); int32_t has_opret(const CTransaction &tx, uint8_t evalcode); -CScript getCCopret(const CScript &scriptPubKey); +bool getCCopret(const CScript &scriptPubKey, CScript &opret); bool makeCCopret(CScript &opret, std::vector> &vData); CC *MakeCCcond1(uint8_t evalcode,CPubKey pk); CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2); @@ -298,6 +298,7 @@ UniValue ValueFromAmount(const CAmount& amount); int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey); int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey); +inline std::string STR_TOLOWER(const std::string &str) { std::string out; for (std::string::const_iterator i = str.begin(); i != str.end(); i++) out += std::tolower(*i); return out; } // bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream: #define CCLOG_INFO 0 diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index f47bc4212..023858dc2 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -63,25 +63,28 @@ int32_t has_opret(const CTransaction &tx, uint8_t evalcode) int i = 0; for ( auto vout : tx.vout ) { - if ( vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[1] == evalcode ) + //fprintf(stderr, "[txid.%s] 1.%i 2.%i 3.%i 4.%i\n",tx.GetHash().GetHex().c_str(), vout.scriptPubKey[0], vout.scriptPubKey[1], vout.scriptPubKey[2], vout.scriptPubKey[3]); + if ( vout.scriptPubKey.size() > 3 && vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[2] == evalcode ) return i; i++; } return 0; } -CScript getCCopret(const CScript &scriptPubKey) +bool getCCopret(const CScript &scriptPubKey, CScript &opret) { std::vector> vParams = std::vector>(); - CScript dummy; CScript opret; - if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) ) + CScript dummy; bool ret = false; + if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) != 0 ) { - //opret << E_MARSHAL(ss << vParams[0]); - opret = CScript(vParams[0].begin()+6, vParams[0].end()); + ret = true; + if ( vParams.size() == 1) + { + opret = CScript(vParams[0].begin()+6, vParams[0].end()); + //fprintf(stderr, "vparams.%s\n", HexStr(vParams[0].begin(), vParams[0].end()).c_str()); + } } - //fprintf(stderr, "params_size.%li parmas_hexstr.%s\n", vParams.size(), HexStr(vParams[0].begin(),vParams[0].end()).c_str()); - //opret = CScript(vParams[0].begin(), vParams[0].end()); - return opret; + return ret; } bool makeCCopret(CScript &opret, std::vector> &vData) diff --git a/src/cc/COptCCParams.cpp b/src/cc/COptCCParams.cpp new file mode 100644 index 000000000..41c9ba874 --- /dev/null +++ b/src/cc/COptCCParams.cpp @@ -0,0 +1,116 @@ +/*Descriptson and examples of COptCCParams class found in: + script/standard.h/cpp + class COptCCParams + +structure of data in vData payload attached to end of CCvout: + param + OP_1 + param + OP_2 ... etc until OP_16 + OP_PUSHDATA4 is the last OP code to tell things its at the end. + + taken from standard.cpp line 22: COptCCParams::COptCCParams(std::vector &vch) + +EXAMPLE taken from Verus how to create scriptPubKey from COptCCParams class: +EXAMPLE taken from Verus how to decode scriptPubKey from COptCCParams class: +*/ + +bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout) +{ + CCcontract_info *cp, C; + cp = CCinit(&C,EVAL_STAKEGUARD); + + CPubKey ccAddress = CPubKey(ParseHex(cp->CChexstr)); + + // return an output that is bound to the stake transaction and can be spent by presenting either a signed condition by the original + // destination address or a properly signed stake transaction of the same utxo on a fork + vout = MakeCC1of2vout(EVAL_STAKEGUARD, value, dest, ccAddress); + + std::vector vPubKeys = std::vector(); + vPubKeys.push_back(dest); + vPubKeys.push_back(ccAddress); + + std::vector> vData = std::vector>(); + + CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); + + hw << stakeTx.vin[0].prevout.hash; + hw << stakeTx.vin[0].prevout.n; + + uint256 utxo = hw.GetHash(); + vData.push_back(std::vector(utxo.begin(), utxo.end())); // Can we use any data here to construct vector? + + CStakeParams p; + if (GetStakeParams(stakeTx, p)) + { + // prev block hash and height is here to make validation easy + vData.push_back(std::vector(p.prevHash.begin(), p.prevHash.end())); + std::vector height = std::vector(4); + for (int i = 0; i < 4; i++) + { + height[i] = (p.blkHeight >> (8 * i)) & 0xff; + } + vData.push_back(height); + + COptCCParams ccp = COptCCParams(COptCCParams::VERSION, EVAL_STAKEGUARD, 1, 2, vPubKeys, vData); + + vout.scriptPubKey << ccp.AsVector() << OP_DROP; + return true; + } + return false; +} + +bool ValidateMatchingStake(const CTransaction &ccTx, uint32_t voutNum, const CTransaction &stakeTx, bool &cheating) +{ + // an invalid or non-matching stake transaction cannot cheat + cheating = false; + + //printf("ValidateMatchingStake: ccTx.vin[0].prevout.hash: %s, ccTx.vin[0].prevout.n: %d\n", ccTx.vin[0].prevout.hash.GetHex().c_str(), ccTx.vin[0].prevout.n); + + if (ccTx.IsCoinBase()) + { + CStakeParams p; + if (ValidateStakeTransaction(stakeTx, p)) + { + std::vector> vParams = std::vector>(); + CScript dummy; + + if (ccTx.vout[voutNum].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() > 0) + { + COptCCParams ccp = COptCCParams(vParams[0]); + if (ccp.IsValid() & ccp.vData.size() >= 3 && ccp.vData[2].size() <= 4) + { + CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); + + hw << stakeTx.vin[0].prevout.hash; + hw << stakeTx.vin[0].prevout.n; + uint256 utxo = hw.GetHash(); + + uint32_t height = 0; + int i, dataLen = ccp.vData[2].size(); + for (i = dataLen - 1; i >= 0; i--) + { + height = (height << 8) + ccp.vData[2][i]; + } + // for debugging strange issue + // printf("iterator: %d, height: %d, datalen: %d\n", i, height, dataLen); + + if (utxo == uint256(ccp.vData[0])) + { + if (p.prevHash != uint256(ccp.vData[1]) && p.blkHeight >= height) + { + cheating = true; + return true; + } + // if block height is equal and we are at the else, prevHash must have been equal + else if (p.blkHeight == height) + { + return true; + } + } + } + } + } + } + return false; +} diff --git a/src/cc/customcc.cpp b/src/cc/customcc.cpp index a8b0bf871..f7e8e6407 100644 --- a/src/cc/customcc.cpp +++ b/src/cc/customcc.cpp @@ -82,12 +82,12 @@ UniValue custom_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) bool custom_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { char expectedaddress[64]; CPubKey pk; - CScript opret; int32_t numvout; + CScript opret; int32_t numvout = 0; if ( has_opret(tx, EVAL_CUSTOM) == 0 ) { std::vector> vParams = std::vector>(); - opret = getCCopret(tx.vout[0].scriptPubKey); - numvout = 1; + if ( getCCopret(tx.vout[0].scriptPubKey,opret) ) + numvout = 1; } else { diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 62f020063..a23a7b16c 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -53,6 +53,15 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn) eval->state.GetRejectReason().data(), tx.vin[nIn].prevout.hash.GetHex().data()); if (eval->state.IsError()) fprintf(stderr, "Culprit: %s\n", EncodeHexTx(tx).data()); + CTransaction tmp; + if (mempool.lookup(tx.GetHash(), tmp)) + { + // This is to remove a payments airdrop if it gets stuck in the mempool. + // Miner will mine 1 invalid block, but doesnt stop them mining until a restart. + // This would almost never happen in normal use. + std::list dummy; + mempool.remove(tx,dummy,true); + } return false; } diff --git a/src/cc/hempcoin_notes.txt b/src/cc/hempcoin_notes.txt new file mode 100644 index 000000000..2fd72c897 --- /dev/null +++ b/src/cc/hempcoin_notes.txt @@ -0,0 +1,65 @@ +How this works: + - earlytxid must be a transaction included in the chain before block 100. The chain MUST not have any other of these type of tx before block 100, or someone may be able to change it and mess things up. + - When it gets to block 100, it takes the txid specified by the -earlytxid param (does not affect magic) + - Looks up the transaction searches for the opreturn, then permenantly appends it to the end of ac_script in RAM. + - After every daemon restart, the first time the daemon mines a block, or receives a block that pays ac_script it will look up the op_return and save it again. + - this enables it to always reach consensus but doesnt need to constantly keep looking up the tx in the chain. + - The trick is to use ac_founders=101 or higher so that nothing is ever paid to the unspendable CC address. Although it should still work without this it burns coins. + +-ac_script can be any Global CC address you can spend to with an OP_RETURN. Here we use example of paymentsCC being used to fund a rewards plan, and a set of founders address's. + you can get the ac_script from another chain, but the op_return payload must generated on the chain itself. this command gives you the needed info to get the scripPubKey Hex: + ./komodo-cli -ac_name=TEST paymentsfund '["5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682",1000]' + append: b8, to the end of ac_script, this changes magic value for -earlytxid chains vs normal ac_script and allows bypass of ac_supply paid to the scritpt as it would be unspendable and you would be unable to create the needed plans with no coins. + -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 + +-testnode=1 is not affecting magic and allows mining on a single node, we can use this to bootstrap the chain before syncing a second node to save time. + +start chain and make sure to do the following steps before block 100 (set generate false/true is a good idea between steps) + ./komodod -ac_name=TESTHC -ac_supply=1000000 -ac_reward=100000000000 -ac_cc=2 -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 -ac_founders=150 -ac_blocktime=20 -ac_nk=96,5 -testnode=1 + +create rewards plan and fund it with all or a % of the premine. Must be some amount. eg. + ./komodo-cli -ac_name=TESTHC rewardscreatefunding test 50000 25 0 2 500 + +do rewards add funding: + ./komodo-cli -ac_name=TESTHC rewardsaddfunding test 47a3150150bd196bd2086cae5e0c6b01a23785a04139fa660d169121a534b38e 1000 + +and get the script pubkey and op_return from this tx (no need to send it) +./komodo-cli -ac_name=TESTHC decoderawtransaction 010000000204ca4c7aaae62bb8fc9412ac010e047fa8d33c3f87d2adeb3e02170642ddfe370000000049483045022100d7b9a +4f28ca3a35f34dcdb6075e905cde1eaa962bd0619d0a8ed8e17e952bc99022077308e12325fc2a02c752ec3df9aeee1fc219ea54a4d3884834582b75c89815e01ffffffff08800132da3233d80c65e87b6db6a76dcf +188e4fdfa23198d69f647e67754cfb0000000049483045022100d6a8f7a1c4f6013f5897768ae0117fe61dfb72352d3e6652e64a6588db3ffcb102202aa1d041b24f9cbbf7028295b7c5e7f18b4f95ae39c13031dab +7f06634438e6801ffffffff0300e8764817000000302ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401ccf0c0764817000000232103bbec93af84 +0933ae2d35fc56eff24f34dbe26871402552f84c44f690945ccd79ac00000000000000002c6a2ae54174657374000000008eb334a52191160d66fa3941a08537a2016b0c5eae6c08d26b19bd500115a34700000000 + +From the return of this you need the scriptpubkey hex of vout 0: + scriptPubKey: 2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc +and the scriptpubkey hex of the OP_RETURN in vout 2. + OP_RETURN: 6a2ae54174657374000000008eb334a52191160d66fa3941a08537a2016b0c5eae6c08d26b19bd500115a347 + +create txidopreturn for this payment: + ./komodo-cli -ac_name=TESTHC paymentstxidopret '[50,"2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc","6a2ae54174657374000000008eb334a52191160d66fa3941a08537a2016b0c5eae6c08d26b19bd500115a347"]' + +create the txidopret for the founders reward(s) pubkeys: should be able to be a few here, not sure of max number yet. These can pay anything that does not need an opreturn. allocation and scriptpubkey hex. + ./komodo-cli -ac_name=TESTHC paymentstxidopret '[50,"76a9146bf5dd9f679c87a3f83ea176f82148d26653c04388ac"]' + +create payments plan: + ./komodo-cli -ac_name=TESTHC paymentscreate '[0,0,"61f55f2f87dad3a37d42731a8cb73b3ebea1817abfa176218162c360a8bd7145","0550014823ffa0aa99d7dd7ca5292f4dd0a1b9156eddec03412c953f095181bc"]' +gives plan txid: ee7765be874fb084c00538b1b0488e8ecb857de253f09a9ba6ea8d3579b77d33 + +paymentsfund: + To do this you first need to change the type of tx generated by paymentsfund RPC. in payments.cpp go to line: 639 and comment it out, then uncomment the block of code under this. + change the line 646 to line 647 with comments, and line 650/651 aswell. This enables the RPC to generate the ccvout opreturn payload you need without sending the payment on the chain. Just decode the raw hex. + ./komodo-cli -ac_name=TESTHC paymentsfund '["ee7765be874fb084c00538b1b0488e8ecb857de253f09a9ba6ea8d3579b77d33",1000,1]' + +get the payment fund scriptpubkey hex from vout 0: (the split it at OP_CHECKCRYPTOCONDITION or 'cc' ) + 2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401cc 2a0401f00101246a22f046337db779358deaa69b9af053e27d85cb8e8e48b0b13805c084b04f87be6577ee75 + + put the second half into an OP_RETURN: (the remaining part of the the above scriptpubkey) eg. + ./komodo-cli -ac_name=TESTHC opreturn_burn 1 2a0401f00101246a22f046337db779358deaa69b9af053e27d85cb8e8e48b0b13805c084b04f87be6577ee75 + opret_burn takes any burn amount and arbitrary hex string. (RPC works, but may have bugs, likely use this for LABS too with some fixes) + this gives a raw hex. Decode it and check the OP_RETURN is right before sending. + -earlytxid=810bd62fb8353fad20267ff2050684b8829affa3edf6b366633931530791dfce + restart the chain with earlytxid param before height 100 on all nodes (if not using -testnode=1) + ./komodod -ac_name=TESTHC -ac_supply=1000000 -ac_reward=100000000000 -ac_cc=2 -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 -ac_founders=150 -ac_blocktime=20 -ac_nk=96,5 -earlytxid=810bd62fb8353fad20267ff2050684b8829affa3edf6b366633931530791dfce + +once the payments plan has been funded with the mined coinbase you can issue payments release when conditions of the plan are met to fund founders reward/rewards plan. eg. + ./komodo-cli -ac_name=TESTHC paymentsrelease '["ee7765be874fb084c00538b1b0488e8ecb857de253f09a9ba6ea8d3579b77d33",500]' diff --git a/src/cc/import.cpp b/src/cc/import.cpp index ded10abd5..35b4f5405 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -535,6 +535,8 @@ bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction uint256 tokenid = zeroid; if (vimportOpret.begin()[0] == EVAL_TOKENS) { // for tokens (new opret with tokens) + if ( is_STAKED(ASSETCHAINS_SYMBOL) == 1 ) + return eval->Invalid("no-tokens-migrate-on-LABS"); struct CCcontract_info *cpTokens, CCtokens_info; std::vector> oprets; uint8_t evalCodeInOpret; diff --git a/src/cc/makecclib b/src/cc/makecclib index b2f8e2ee1..e4816c55c 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -4,6 +4,8 @@ rm *.so rogue/rogue games/tetris games/prices echo rogue make -f Makefile_rogue ./makerogue +rm ../libcc.so +cp librogue.so ../libcc.so echo sudoku/musig/dilithium gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o sudokucc.so cclib.cpp @@ -16,4 +18,3 @@ echo games prices echo customcc stub gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp - diff --git a/src/cc/makecustom b/src/cc/makecustom index 61b251e6e..154be4f31 100755 --- a/src/cc/makecustom +++ b/src/cc/makecustom @@ -4,4 +4,3 @@ cp customcc.so ../libcc.so cd .. make cd cc - diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index ef457fe82..a5d61d404 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -935,13 +935,13 @@ UniValue OracleFormat(uint8_t *data,int32_t datalen,char *format,int32_t formatl if ( j >= datalen ) break; } - return(obj); + return(str); } UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; - CPubKey pk; std::string name,description,format; int32_t numvouts,n=0; std::vector data; char *formatstr = 0; + UniValue result(UniValue::VOBJ),b(UniValue::VARR); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; + CPubKey pk; std::string name,description,format; int32_t numvouts,n=0; std::vector data; char str[67], *formatstr = 0; result.push_back(Pair("result","success")); if ( GetTransaction(reforacletxid,oracletx,hashBlock,false) != 0 && (numvouts=oracletx.vout.size()) > 0 ) @@ -954,15 +954,18 @@ UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num) { if ( (formatstr= (char *)format.c_str()) == 0 ) formatstr = (char *)""; + UniValue a(UniValue::VARR); a.push_back(OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size())); + a.push_back(uint256_str(str,batontxid)); + b.push_back(a); batontxid = btxid; - if ( ++n >= num ) + if ( ++n >= num && num != 0) break; } else break; } } } - result.push_back(Pair("samples",a)); + result.push_back(Pair("samples",b)); return(result); } diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 8bd41b333..6311e393d 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,12 +16,6 @@ #include "CCPayments.h" /* - --earlytxid is not an -ac_param, so it doesnt affect the chain magics -extra data after the normal CCvout is whatever data we want and can represent whatever we want -so -ac_script= -in the validation if you see the useearlytxid in the opreturn data or extra data, you use the earlytxid as the txid that specifies the payment - 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret @@ -123,6 +117,26 @@ uint8_t DecodePaymentsFundOpRet(CScript scriptPubKey,uint256 &checktxid) return(0); } +CScript EncodePaymentsMergeOpRet(uint256 checktxid) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'M' << checktxid); + return(opret); +} + +uint8_t DecodePaymentsMergeOpRet(CScript scriptPubKey,uint256 &checktxid) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> checktxid) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'M' ) + return(f); + } + return(0); +} + CScript EncodePaymentsOpRet(int32_t lockedblocks,int32_t minrelease,int64_t totalallocations,std::vector txidoprets) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; @@ -137,16 +151,76 @@ uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t & script = (uint8_t *)vopret.data(); if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> totalallocations; ss >> txidoprets) != 0 ) { - if ( e == EVAL_PAYMENTS && f == 'C' ) + if ( e == EVAL_PAYMENTS && f == 'C' && txidoprets.size() > 1 ) return(f); } return(0); } -int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr) +CScript EncodePaymentsSnapsShotOpRet(int32_t lockedblocks,int32_t minrelease,int32_t minimum,int32_t top,int32_t bottom,int8_t fixedAmount,std::vector> excludeScriptPubKeys) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + if ( (strcmp(ASSETCHAINS_SYMBOL, "CFEKPAY") == 0) ) // exempt for now, remove this after game completed. + { + minimum = 10000; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << top << bottom << fixedAmount << excludeScriptPubKeys); + } + else + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << minimum << top << bottom << fixedAmount << excludeScriptPubKeys); + return(opret); +} + +uint8_t DecodePaymentsSnapsShotOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &minimum,int32_t &top,int32_t &bottom,int8_t &fixedAmount,std::vector> &excludeScriptPubKeys) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( (strcmp(ASSETCHAINS_SYMBOL, "CFEKPAY") == 0) ) // exempt for now, remove this after game completed. + { + minimum = 10000; + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> top; ; ss >> bottom; ss >> fixedAmount; ss >> excludeScriptPubKeys) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'S' ) + return(f); + } + } + else + { + + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> minimum; ss >> top; ; ss >> bottom; ss >> fixedAmount; ss >> excludeScriptPubKeys) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'S' ) + return(f); + } + } + return(0); +} + +CScript EncodePaymentsTokensOpRet(int32_t lockedblocks,int32_t minrelease,int32_t top,std::vector> excludeScriptPubKeys, uint256 tokenid) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'O' << lockedblocks << minrelease << top << excludeScriptPubKeys << tokenid); + return(opret); +} + +uint8_t DecodePaymentsTokensOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &top,std::vector> &excludeScriptPubKeys, uint256 &tokenid) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> top; ss >> excludeScriptPubKeys; ss >> tokenid) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'O' ) + return(f); + } + return(0); +} + +int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr, CScript &ccopret) { char destaddr[64]; - if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) + //if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) + if ( getCCopret(tx.vout[v].scriptPubKey, ccopret) ) { if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (cmpaddr[0] == 0 || strcmp(destaddr,cmpaddr) == 0) ) return(tx.vout[v].nValue); @@ -170,6 +244,34 @@ void pub2createtxid(char *str) free(rev); } +bool payments_game(int32_t &top, int32_t &bottom) +{ + uint64_t x; + uint256 tmphash = chainActive[lastSnapShotHeight]->GetBlockHash(); + memcpy(&x,&tmphash,sizeof(x)); + bottom = ((x & 0xff) % 50); + if ( bottom == 0 ) bottom = 1; + top = (((x>>8) & 0xff) % 100); + if ( top < 50 ) top += 50; + bottom = (vAddressSnapshot.size()*bottom)/100; + top = (vAddressSnapshot.size()*top)/100; + fprintf(stderr, "bottom.%i top.%i\n",bottom,top); + return true; +} + +bool payments_lockedblocks(uint256 blockhash,int32_t lockedblocks,int32_t &blocksleft) +{ + int32_t ht = chainActive.Height(); + CBlockIndex* pblockindex = komodo_blockindex(blockhash); + if ( pblockindex == 0 || pblockindex->GetHeight()+lockedblocks > ht) + { + blocksleft = pblockindex->GetHeight()+lockedblocks - ht; + fprintf(stderr, "not elegible to be spent yet height.%i vs elegible_ht.%i blocksleft.%i\n",ht,(pblockindex!=0?pblockindex->GetHeight():0)+lockedblocks,blocksleft); + return false; + } + return true; +} + bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { // one of two addresses @@ -177,151 +279,241 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // change is/must be in vout[0] // only 'F' or 1of2 txidaddr can be spent // all vouts must match exactly - char temp[128], coinaddr[64], txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash; CTransaction tmptx; - int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; + char temp[128], coinaddr[64]={0}, txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash, tokenid; CTransaction plantx; int8_t funcid=0, fixedAmount=0; + int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector txidoprets; bool fHasOpret = false,fIsMerge = false; CPubKey txidpk,Paymentspk; + int32_t top,bottom=0,minimum=10000; std::vector> excludeScriptPubKeys; bool fFixedAmount = false; CScript ccopret; + mpz_t mpzTotalAllocations, mpzAllocation;; mpz_init(mpzTotalAllocations); // user marker vout to get the createtxid - if ( tx.vout.size() < 2 ) - return(eval->Invalid("not enough vouts")); - if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) + if ( tx.vout.size() == 1 ) + { + if ( IsPaymentsvout(cp,tx,0,coinaddr,ccopret) != 0 && ccopret.size() > 2 && DecodePaymentsMergeOpRet(ccopret,createtxid) ) + { + fIsMerge = true; + } else return(eval->Invalid("not enough vouts")); + } + else if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) { scriptpubkey = HexStr(tx.vout[tx.vout.size()-2].scriptPubKey.begin()+2, tx.vout[tx.vout.size()-2].scriptPubKey.end()-1); fHasOpret = true; - } else scriptpubkey = HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin()+2,tx.vout[tx.vout.size()-1].scriptPubKey.end()-1); - strcpy(temp, scriptpubkey.c_str()); - pub2createtxid(temp); - createtxid = Parseuint256(temp); + } + else scriptpubkey = HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin()+2,tx.vout[tx.vout.size()-1].scriptPubKey.end()-1); + if ( !fIsMerge ) + { + strcpy(temp, scriptpubkey.c_str()); + pub2createtxid(temp); + createtxid = Parseuint256(temp); + } //printf("createtxid.%s\n",createtxid.ToString().c_str()); // use the createtxid to fetch the tx and all of the plans info. - if ( myGetTransaction(createtxid,tmptx,blockhash) != 0 ) - { - if ( tmptx.vout.size() > 0 && DecodePaymentsOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( myGetTransaction(createtxid,plantx,blockhash) != 0 && plantx.vout.size() > 0 ) + { + if ( ((funcid= DecodePaymentsOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) - return(eval->Invalid("negative values")); + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) + return(eval->Invalid("negative values")); + if ( minimum < 10000 ) + return(eval->Invalid("minimum must be over 10000")); Paymentspk = GetUnspendable(cp,0); + txidpk = CCtxidaddr(txidaddr,createtxid); + GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); if ( !CheckTxFee(tx, PAYMENTS_TXFEE+1, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) return eval->Invalid("txfee is too high"); - // Get all the script pubkeys and allocations - std::vector allocations; - std::vector scriptPubKeys; - int64_t checkallocations = 0; - i = 0; - BOOST_FOREACH(const uint256& txidopret, txidoprets) - { - CTransaction tx0; std::vector scriptPubKey,opret; int64_t allocation; - if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) - { - scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); - allocations.push_back(allocation); - //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); - checkallocations += allocation; - // if we have an op_return to pay to need to check it exists and is paying the correct opret. - if ( !opret.empty() ) - { - if ( !fHasOpret ) - { - fprintf(stderr, "missing opret.%s in payments release.\n",HexStr(opret.begin(), opret.end()).c_str()); - return(eval->Invalid("missing opret in payments release")); - } - else if ( CScript(opret.begin(),opret.end()) != tx.vout[tx.vout.size()-1].scriptPubKey ) - { - fprintf(stderr, "opret.%s vs opret.%s\n",HexStr(opret.begin(), opret.end()).c_str(), HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin(), tx.vout[tx.vout.size()-1].scriptPubKey.end()).c_str()); - return(eval->Invalid("pays incorrect opret")); - } - } - } - i++; - } - - // sanity check to make sure we got all the required info - if ( allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size() ) - return(eval->Invalid("missing data cannot validate")); - - //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); - if ( totalallocations != checkallocations ) - return(eval->Invalid("allocation missmatch")); - - txidpk = CCtxidaddr(txidaddr,createtxid); - GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); - //fprintf(stderr, "coinaddr.%s\n", coinaddr); - // make sure change is in vout 0 and is paying to the contract address. - if ( (change= IsPaymentsvout(cp,tx,0,coinaddr)) == 0 ) + if ( (change= IsPaymentsvout(cp,tx,0,coinaddr,ccopret)) == 0 ) return(eval->Invalid("change is in wrong vout or is wrong tx type")); - // Check vouts go to the right place and pay the right amounts. - int64_t amount = 0, checkamount; int32_t n = 0; - checkamount = tx.GetValueOut() - change - PAYMENTS_TXFEE; - for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) + if ( !fIsMerge ) { - std::string destscriptPubKey = HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()); - std::string voutscriptPubKey = HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()); - if ( destscriptPubKey != voutscriptPubKey ) + // Get all the script pubkeys and allocations + std::vector allocations; + std::vector scriptPubKeys; + int64_t checkallocations = 0; + i = 0; + if ( funcid == 'C' ) { - fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", destscriptPubKey.c_str(), voutscriptPubKey.c_str()); - return(eval->Invalid("pays wrong address")); + // normal payment + for (const uint256& txidopret : txidoprets) + { + CTransaction tx0; std::vector scriptPubKey,opret; int64_t allocation; + if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); + allocations.push_back(allocation); + //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); + checkallocations += allocation; + // if we have an op_return to pay to need to check it exists and is paying the correct opret. + if ( !opret.empty() ) + { + if ( !fHasOpret ) + { + fprintf(stderr, "missing opret.%s in payments release.\n",HexStr(opret.begin(), opret.end()).c_str()); + return(eval->Invalid("missing opret in payments release")); + } + else if ( CScript(opret.begin(),opret.end()) != tx.vout[tx.vout.size()-1].scriptPubKey ) + { + fprintf(stderr, "opret.%s vs opret.%s\n",HexStr(opret.begin(), opret.end()).c_str(), HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin(), tx.vout[tx.vout.size()-1].scriptPubKey.end()).c_str()); + return(eval->Invalid("pays incorrect opret")); + } + } + } + i++; + } + mpz_set_si(mpzTotalAllocations,totalallocations); } - int64_t test = allocations[n]; - test *= checkamount; - test /= totalallocations; - if ( test != tx.vout[i].nValue && test != tx.vout[i].nValue-1 ) + else if ( funcid == 'S' ) { - fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); + if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) + return(eval->Invalid("snapshots not activated on this chain")); + if ( vAddressSnapshot.size() == 0 ) + return(eval->Invalid("need first snapshot")); + if ( top > 3999 ) + return(eval->Invalid("transaction too big")); + if ( fixedAmount == 7 ) + { + // game setting, randomise bottom and top values + fFixedAmount = payments_game(top,bottom); + } + else if ( fixedAmount != 0 ) + { + fFixedAmount = true; + } + for (int32_t j = bottom; j < vAddressSnapshot.size(); j++) + { + auto &address = vAddressSnapshot[j]; + CScript scriptPubKey = GetScriptForDestination(address.second); bool skip = false; + for ( auto skipkey : excludeScriptPubKeys ) + { + if ( scriptPubKey == CScript(skipkey.begin(), skipkey.end()) ) + { + skip = true; + //fprintf(stderr, "SKIPPED::: %s\n", CBitcoinAddress(address.second).ToString().c_str()); + } + } + if ( !skip ) + { + mpz_init(mpzAllocation); + i++; + scriptPubKeys.push_back(scriptPubKey); + allocations.push_back(address.first); + mpz_set_si(mpzAllocation,address.first); + mpz_add(mpzTotalAllocations,mpzTotalAllocations,mpzAllocation); + mpz_clear(mpzAllocation); + } + if ( i+bottom == top ) // we reached top amount to pay, it can be less than this! + break; + } + if ( i != tx.vout.size()-2 ) + return(eval->Invalid("pays wrong amount of recipients")); + } + else if ( funcid == 'O' ) + { + // tokens snapshot. + } + // sanity check to make sure we got all the required info, skip for merge type tx + //fprintf(stderr, " allocations.size().%li scriptPubKeys.size.%li\n",allocations.size(), scriptPubKeys.size()); + if ( (allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size()) ) + return(eval->Invalid("missing data cannot validate")); + + //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); + if ( funcid == 'C' && totalallocations != checkallocations ) // only check for normal payments release. + return(eval->Invalid("allocation missmatch")); + + // Check vouts go to the right place and pay the right amounts. + int64_t amount = 0, checkamount; int32_t n = 0; + checkamount = tx.GetValueOut() - change - PAYMENTS_TXFEE; + mpz_t mpzCheckamount; mpz_init(mpzCheckamount); mpz_set_si(mpzCheckamount,checkamount); + for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) + { + if ( scriptPubKeys[n] != tx.vout[i].scriptPubKey ) + { + fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()).c_str(), HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()).c_str()); + return(eval->Invalid("pays wrong address")); + } + int64_t test; + if ( fFixedAmount ) + { + test = checkamount / (top-bottom); + } + else + { + mpz_init(mpzAllocation); + mpz_set_si(mpzAllocation,allocations[n]); + mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); + mpz_cdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); + test = mpz_get_si(mpzAllocation); + mpz_clear(mpzAllocation); + } + // Vairance of 1 sat is allowed, for rounding errors. + if ( test >= tx.vout[i].nValue+1 && test <= tx.vout[i].nValue-1 ) + { + fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); + return(eval->Invalid("amounts do not match")); + } + if ( test < minimum ) + { + fprintf(stderr, "vout.%i test.%li vs minimum.%i\n",i, test, minimum); + return(eval->Invalid("under minimum size")); + } + amount += tx.vout[i].nValue; + n++; + } + mpz_clear(mpzTotalAllocations); + // This is a backup check to make sure there are no extra vouts paying something else! + if ( checkamount != amount ) return(eval->Invalid("amounts do not match")); + + if ( amount < minrelease*COIN ) + { + fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); + return(eval->Invalid("amount is too small")); } - amount += tx.vout[i].nValue; - n++; } - // This is a backup check to make sure there are no extra vouts paying something else! - if ( checkamount != amount ) - return(eval->Invalid("amounts do not match")); - - if ( amount < minrelease*COIN ) - { - fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); - return(eval->Invalid("amount is too small")); - } - - i = 0; - int32_t ht = chainActive.LastTip()->GetHeight(); + // Check vins + i = 0; int32_t dust = 0; + int32_t blocksleft; BOOST_FOREACH(const CTxIn& vin, tx.vin) { - CTransaction txin; + CTransaction txin; if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) { // check the vin comes from the CC address's - char destaddr[64]; + char destaddr[64]; int32_t mergeoffset = 0; CScript opret; uint256 checktxid; Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); + if ( fIsMerge && txin.vout[vin.prevout.n].nValue < COIN ) + dust++; if ( strcmp(destaddr,coinaddr) != 0 ) { - CScript opret; uint256 checktxid; int32_t opret_ind; + // if does not come from address its in the global payments adddress and we need to check the opreturn. + uint256 checktxid; int32_t opret_ind; if ( (opret_ind= has_opret(txin, EVAL_PAYMENTS)) == 0 ) - { - // get op_return from CCvout - opret = getCCopret(txin.vout[0].scriptPubKey); - } + getCCopret(txin.vout[vin.prevout.n].scriptPubKey,opret); // get op_return from CCvout, else - { - // get op_return from the op_return - opret = txin.vout[opret_ind].scriptPubKey; - } // else return(eval->Invalid("vin has wrong amount of vouts")); // dont think this is needed? + opret = txin.vout[opret_ind].scriptPubKey; if ( DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid ) { fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); return(eval->Invalid("vin is not paymentsCC type")); } } - // check the chain depth vs locked blocks requirement. - CBlockIndex* pblockindex = komodo_blockindex(blockhash); - if ( pblockindex == 0 || pblockindex->GetHeight() > ht-lockedblocks ) + else if ( fIsMerge && getCCopret(txin.vout[vin.prevout.n].scriptPubKey,opret) && opret.size() > 2 && DecodePaymentsMergeOpRet(opret,checktxid) == 'M' ) { - fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex!=0?pblockindex->GetHeight():0, ht-lockedblocks); - return(eval->Invalid("vin not elegible")); + mergeoffset = PAYMENTS_MERGEOFSET; } + //fprintf(stderr, "mergeoffset.%i\n", mergeoffset); + // check the chain depth vs locked blocks requirement. + if ( !payments_lockedblocks(blockhash, lockedblocks+mergeoffset, blocksleft) ) + return(eval->Invalid("vin not elegible")); + i++; } else return(eval->Invalid("cant get vin transaction")); - i++; + } + if ( fIsMerge ) + { + if ( i < 2 ) + return(eval->Invalid("must have at least 2 vins to carry out merge")); + else if ( i == dust+1 ) + return(eval->Invalid("cannot merge only dust")); } } else return(eval->Invalid("create transaction cannot decode")); } else return(eval->Invalid("Could not get contract transaction")); @@ -330,19 +522,27 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // end of consensus code // helper functions for rpc calls in rpcwallet.cpp - -int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t latestheight) +int64_t AddPaymentsInputs(bool fLockedBlocks,int8_t GetBalance,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t lockedblocks,int64_t minrelease,int32_t &blocksleft) { - char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,ht,n = 0; - std::vector > unspentOutputs; - if ( maxinputs > CC_MAXVINS ) - maxinputs = CC_MAXVINS; - if ( maxinputs > 0 ) - threshold = total/maxinputs; - else threshold = total; + char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t iter,vout,ht,n = 0; + std::vector > unspentOutputs; CScript ccopret; + std::vector > blocksleft_balance; + if ( GetBalance == 0 ) + { + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + } + else threshold = 0; Paymentspk = GetUnspendable(cp,0); for (iter=0; iter<2; iter++) { + if ( GetBalance == 1 && iter == 1 ) + continue; // getbalance of global paymentsCC address. + if ( GetBalance == 2 && iter == 0 ) + continue; // get balance of txidpk address. if ( iter == 0 ) GetCCaddress(cp,coinaddr,Paymentspk); else GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); @@ -352,54 +552,68 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP txid = it->first.txhash; vout = (int32_t)it->first.index; //fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); - if ( vout == 0 && GetTransaction(txid,vintx,hashBlock,false) != 0 ) + if ( (vout == 0 || vout == 1) && GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( latestheight != 0 ) - { - if ( (ht= komodo_blockheight(hashBlock)) == 0 ) - { - fprintf(stderr,"null ht\n"); - continue; - } - else if ( ht > latestheight ) - { - fprintf(stderr,"ht.%d > lastheight.%d\n",ht,latestheight); - continue; - } - } if ( iter == 0 ) { CScript opret; uint256 checktxid; int32_t opret_ind; if ( (opret_ind= has_opret(vintx, EVAL_PAYMENTS)) == 0 ) { // get op_return from CCvout - opret = getCCopret(vintx.vout[0].scriptPubKey); + getCCopret(vintx.vout[vout].scriptPubKey,opret); } else { // get op_return from the op_return opret = vintx.vout[opret_ind].scriptPubKey; } - if ( myGetTransaction(txid,tx,hashBlock) == 0 || DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid ) + if ( DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid ) { fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str()); continue; } } - if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr,ccopret)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { - if ( total != 0 && maxinputs != 0 ) + int32_t offset = 0; + if ( ccopret.size() > 2 && DecodePaymentsMergeOpRet(ccopret,checktxid) == 'M' ) + offset = PAYMENTS_MERGEOFSET; + int32_t tmpblocksleft = 0; + if ( fLockedBlocks && !payments_lockedblocks(hashBlock, lockedblocks+offset, tmpblocksleft) ) + { + blocksleft_balance.push_back(std::make_pair(tmpblocksleft,nValue)); + continue; + } + if ( (GetBalance == 0 && total != 0 && maxinputs != 0) || GetBalance == 4 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); nValue = it->second.satoshis; + if ( nValue < COIN ) + blocksleft++; // count dust with unused variable. totalinputs += nValue; n++; //fprintf(stderr,"iter.%d %s/v%d %s %.8f\n",iter,txid.GetHex().c_str(),vout,coinaddr,(double)nValue/COIN); - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; + if ( GetBalance == 0 && ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) ) + break; // create tx. We have ebnough inputs to make it. } //else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); } } } + if ( GetBalance == 3 && totalinputs < minrelease ) // return elegible balance to be spent, and blocks left until min release can be released. + { + int64_t lockedblocks_balance = totalinputs; // inputs that can be spent already. + // sort utxos by blocks until able to be spent, smallest at top. + std::sort(blocksleft_balance.begin(), blocksleft_balance.end()); + // iterate the utxos blocks left vector, to get block height min release is able to be released. + for ( auto utxo : blocksleft_balance ) + { + lockedblocks_balance += utxo.second; + if ( lockedblocks_balance >= minrelease ) + { + blocksleft = utxo.first; + break; + } + } + } return(totalinputs); } @@ -474,9 +688,12 @@ int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { - int32_t latestheight,nextheight = komodo_nextheight(); - CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations,checkallocations=0,allocation; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + int32_t nextheight = komodo_nextheight(); + //int32_t latestheight,nextheight = komodo_nextheight(); + CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock,tokenid; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations=0,checkallocations=0,allocation; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + int32_t top,bottom=0,blocksleft=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t funcid,fixedAmount=0; bool fFixedAmount = false; + mpz_t mpzTotalAllocations; mpz_init(mpzTotalAllocations); cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -484,111 +701,220 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { createtxid = payments_juint256(jitem(params,0)); amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; - if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 && tx.vout.size() > 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( ((funcid= DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); return(result); } - latestheight = (nextheight - lockedblocks - 1); + // set minimum size to 10k sat otherwise the tx will be invalid. + if ( minimum < 10000 ) + minimum = 10000; + //latestheight = (nextheight - lockedblocks - 1); if ( amount < minrelease*COIN ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","amount too smal")); result.push_back(Pair("amount",ValueFromAmount(amount))); result.push_back(Pair("minrelease",ValueFromAmount(minrelease*COIN))); + if ( params != 0 ) + free_json(params); return(result); } txidpk = CCtxidaddr(txidaddr,createtxid); mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk)); - m = txidoprets.size(); - for (i=0; i scriptPubKey,opret; - vout.nValue = 0; - if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + // normal payments + for (i=0; i 0 ) + std::vector scriptPubKey,opret; + vout.nValue = 0; + if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { - onlyopret.resize(opret.size()); - memcpy(&onlyopret[0],&opret[0],opret.size()); - numoprets++; + vout.nValue = allocation; + vout.scriptPubKey.resize(scriptPubKey.size()); + memcpy(&vout.scriptPubKey[0],&scriptPubKey[0],scriptPubKey.size()); + checkallocations += allocation; + if ( opret.size() > 0 ) + { + onlyopret.resize(opret.size()); + memcpy(&onlyopret[0],&opret[0],opret.size()); + numoprets++; + } + } else break; + mtx.vout.push_back(vout); + } + result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( i != m ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidoprets[i]")); + result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); + return(result); + } + else if ( checkallocations != totalallocations ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","totalallocations mismatch")); + result.push_back(Pair("checkallocations",(int64_t)checkallocations)); + result.push_back(Pair("totalallocations",(int64_t)totalallocations)); + if ( params != 0 ) + free_json(params); + return(result); + } + else if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many oprets")); + if ( params != 0 ) + free_json(params); + return(result); + } + // set totalallocations to a mpz_t bignum, for amounts calculation later. + mpz_set_si(mpzTotalAllocations,totalallocations); + } + else if ( funcid == 'S' ) + { + // normal snapshot + if ( vAddressSnapshot.size() == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","first snapshot has not happened yet")); + if ( params != 0 ) + free_json(params); + return(result); + } + if ( top > 3999 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot pay more than 3999 addresses")); + if ( params != 0 ) + free_json(params); + return(result); + } + i = 0; + if ( fixedAmount == 7 ) + { + // game setting, randomise bottom and top values + fFixedAmount = payments_game(top,bottom); + } + else if ( fixedAmount != 0 ) + { + fFixedAmount = true; + } + for (int32_t j = bottom; j < vAddressSnapshot.size(); j++) + { + auto &address = vAddressSnapshot[j]; + CScript scriptPubKey = GetScriptForDestination(address.second); bool skip = false; + for ( auto skipkey : excludeScriptPubKeys ) + { + if ( scriptPubKey == CScript(skipkey.begin(), skipkey.end()) ) + { + skip = true; + //fprintf(stderr, "SKIPPED::: %s\n", CBitcoinAddress(address.second).ToString().c_str()); + } } - } else break; - mtx.vout.push_back(vout); + if ( !skip ) + { + mpz_t mpzAllocation; mpz_init(mpzAllocation); + i++; + //fprintf(stderr, "address: %s nValue.%li \n", CBitcoinAddress(address.second).ToString().c_str(), address.first); + vout.nValue = address.first; + vout.scriptPubKey = scriptPubKey; + mpz_set_si(mpzAllocation,address.first); + mpz_add(mpzTotalAllocations,mpzTotalAllocations,mpzAllocation); + mtx.vout.push_back(vout); + mpz_clear(mpzAllocation); + } + if ( i+bottom == top ) // we reached top amount to pay, it can be less than this! + break; + } + m = i; // this is the amount we got, either top, or all of the address on the chain. } - result.push_back(Pair("numoprets",(int64_t)numoprets)); - if ( i != m ) + else if ( funcid == 'O' ) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","invalid txidoprets[i]")); - result.push_back(Pair("txi",(int64_t)i)); - if ( params != 0 ) - free_json(params); - return(result); - } - else if ( checkallocations != totalallocations ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","totalallocations mismatch")); - result.push_back(Pair("checkallocations",(int64_t)checkallocations)); - result.push_back(Pair("totalallocations",(int64_t)totalallocations)); - if ( params != 0 ) - free_json(params); - return(result); - } - else if ( numoprets > 1 ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","too many oprets")); - if ( params != 0 ) - free_json(params); - return(result); + // token snapshot } newamount = amount; + int64_t totalamountsent = 0; + mpz_t mpzAmount; mpz_init(mpzAmount); mpz_set_si(mpzAmount,amount); for (i=0; i= newamount+2*PAYMENTS_TXFEE ) - { - std::string rawtx; - if ( (CCchange= (inputsum - newamount - 2*PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) - mtx.vout[0].nValue = CCchange; - mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); - GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); - CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); - if ( params != 0 ) - free_json(params); - result.push_back(Pair("amount",ValueFromAmount(amount))); - result.push_back(Pair("newamount",ValueFromAmount(newamount))); - return(payments_rawtxresult(result,rawtx,1)); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt find enough locked funds")); - } + if ( totalamountsent < amount ) newamount = totalamountsent; + fprintf(stderr, "newamount.%li totalamountsent.%li\n", newamount, totalamountsent); + mpz_clear(mpzAmount); mpz_clear(mpzTotalAllocations); } else { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt decode paymentscreate txid opret")); + if ( params != 0 ) + free_json(params); + return(result); + } + if ( (inputsum= AddPaymentsInputs(true,0,cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,CC_MAXVINS/2,createtxid,lockedblocks,minrelease,blocksleft)) >= newamount+2*PAYMENTS_TXFEE ) + { + std::string rawtx; + if ( (CCchange= (inputsum - newamount - 2*PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) + mtx.vout[0].nValue = CCchange; + mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); + GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); + if ( params != 0 ) + free_json(params); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("newamount",ValueFromAmount(newamount))); + return(payments_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough locked funds")); } } else @@ -611,6 +937,8 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount,totalallocations; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,lockedblocks,minrelease; std::vector txidoprets; + int32_t top,bottom,minimum=10000; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -620,14 +948,14 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( n == 3 ) useopret = jint(jitem(params,2),0) != 0; - if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 ) + if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 && DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 0 && DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 0) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid createtxid")); } else if ( AddNormalinputs(mtx,mypk,amount+PAYMENTS_TXFEE,60) > 0 ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -642,18 +970,18 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) } else { + mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk)); opret = EncodePaymentsFundOpRet(txid); - fprintf(stderr, "opret.%s\n", HexStr(opret.begin(), opret.end()).c_str()); - std::vector> vData = std::vector>(); + // Use the below one along with other FinalizeCCTx/return, to get the ccvout scriptpubkey + /*std::vector> vData = std::vector>(); if ( makeCCopret(opret, vData) ) - { - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData)); - fprintf(stderr, "params_size.%li parmas_hexstr.%s\n", vData.size(), HexStr(vData[0].begin(),vData[0].end()).c_str()); - } + mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData)); */ } - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret); + //rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); // use this one to get ccvout scriptpubkey. if ( params != 0 ) free_json(params); + //return(payments_rawtxresult(result,rawtx,0)); // disable sending for CCvout, as we only need to decode the tx. return(payments_rawtxresult(result,rawtx,1)); } else @@ -672,6 +1000,65 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) return(result); } +UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); + CPubKey Paymentspk,mypk,txidpk; uint256 createtxid,hashBlock; int64_t inputsum,totalallocations=0; CScript opret; CTransaction tx; char txidaddr[64],destaddr[64]; std::string rawtx; + int32_t n,lockedblocks,minrelease,top,bottom,minimum=10000,blocksleft; std::vector txidoprets; + std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( params != 0 && n == 1 ) + { + createtxid = payments_juint256(jitem(params,0)); + txidpk = CCtxidaddr(txidaddr,createtxid); + if ( myGetTransaction(createtxid,tx,hashBlock) == 0 || tx.vout.size() == 1 || (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 && DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 0 && DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 0) ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid createtxid")); + } + else if ( (inputsum= AddPaymentsInputs(true,4,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft)) > 0 && mtx.vin.size() > 1 ) + { + int32_t dust = blocksleft; + if ( mtx.vin.size() == dust+1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot merge only dust")); + } + else + { + // encode the checktxid into the end of the ccvout, along with 'M' to flag merge type tx. + opret = EncodePaymentsMergeOpRet(createtxid); + std::vector> vData = std::vector>(); + // try to pay to diffrent pubkey here... change txidpk. + if ( makeCCopret(opret, vData) ) + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,inputsum-PAYMENTS_TXFEE,Paymentspk,txidpk,&vData)); + GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey mypk; std::string rawtx; @@ -789,63 +1176,69 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) { - // need to code: exclude list of tokenid, dust threshold, maxpayees, excluded pubkeys[] CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease; std::string rawtx; int64_t totalallocations = 0; + UniValue result(UniValue::VOBJ); + uint256 hashBlock; CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::string rawtx; + int32_t lockedblocks,minrelease,top,bottom,n,i,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount; + if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot use airdrop wihtout -ac_snapshot set.")); + return(result); + } cJSON *params = payments_reparse(&n,jsonstr); - if ( params != 0 && n >= 4 ) + if ( params != 0 && n >= 5 ) { lockedblocks = juint(jitem(params,0),0); minrelease = juint(jitem(params,1),0); - if ( lockedblocks < 0 || minrelease < 0 ) + minimum = juint(jitem(params,2),0); + if ( minimum < 10000 ) minimum = 10000; + top = juint(jitem(params,3),0); + bottom = juint(jitem(params,4),0); + fixedAmount = juint(jitem(params,5),0); // fixed amount is a flag, set to 7 does game mode, 0 normal snapshot, anything else fixed allocations. + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || minimum < 0 || fixedAmount < 0 || top > 3999 ) { result.push_back(Pair("result","error")); - result.push_back(Pair("error","negative parameter")); + result.push_back(Pair("error","negative parameter, or top over 3999")); if ( params != 0 ) free_json(params); return(result); } - for (i=0; i 6 ) { - std::vector scriptPubKey,opret; int64_t allocation; - if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + for (i=0; i 0 ) - numoprets++; + /* TODO: Change this RPC to take an address. Because a tokens airdrop needs its own RPC anyway. + CTxDestination destination = DecodeDestination(name_); + CScript scriptPubKey = GetScriptForDestination(destination); + */ + char *inputhex = jstri(params,6+i); + std::vector scriptPubKey; + int32_t len = strlen(inputhex)/2; + scriptPubKey.resize(len); + decode_hex((uint8_t *)scriptPubKey.data(),len,(char *)inputhex); + excludeScriptPubKeys.push_back(scriptPubKey); } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","invalid txidopret")); - result.push_back(Pair("txid",txidoprets[i].GetHex())); - result.push_back(Pair("txi",(int64_t)i)); - if ( params != 0 ) - free_json(params); - return(result); - } - } - if ( numoprets > 1 ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","too many opreturns")); - result.push_back(Pair("numoprets",(int64_t)numoprets)); - if ( params != 0 ) - free_json(params); - return(result); } mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) { mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); + CScript tempopret = EncodePaymentsSnapsShotOpRet(lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys); + if ( tempopret.size() > 10000 ) // TODO: Check this! + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","op_return is too big, try with less exclude addresses.")); + if ( params != 0 ) + free_json(params); + return(result); + } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,tempopret); if ( params != 0 ) free_json(params); return(payments_rawtxresult(result,rawtx,1)); - } + } result.push_back(Pair("result","error")); result.push_back(Pair("error","not enough normal funds")); } @@ -861,15 +1254,17 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease; std::vector txidoprets; int64_t funds,fundsopret,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease,blocksleft=0; std::vector txidoprets; int64_t funds,fundsopret,elegiblefunds,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; + int32_t top,bottom,minimum=10000; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),komodo_nextheight()); cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) { Paymentspk = GetUnspendable(cp,0); createtxid = payments_juint256(jitem(params,0)); - if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 && tx.vout.size() > 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) { @@ -879,6 +1274,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } + result.push_back(Pair("plan_type","payments")); result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); result.push_back(Pair("totalallocations",(int64_t)totalallocations)); result.push_back(Pair("minrelease",(int64_t)minrelease)); @@ -907,29 +1303,82 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) } else fprintf(stderr,"error decoding voutsize.%d\n",(int32_t)txO.vout.size()); a.push_back(obj); } - flag++; result.push_back(Pair("numoprets",(int64_t)numoprets)); if ( numoprets > 1 ) { + flag++; result.push_back(Pair("result","error")); result.push_back(Pair("error","too many opreturns")); - } - else + } else result.push_back(Pair("txidoprets",a)); + } + else if ( DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || fixedAmount < 0 || top > 3999 || minimum < 10000 ) { - result.push_back(Pair("txidoprets",a)); - txidpk = CCtxidaddr(txidaddr,createtxid); - GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); - funds = CCaddress_balance(fundsaddr,1); - result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); - GetCCaddress(cp,fundsopretaddr,Paymentspk); - fundsopret = CCaddress_balance(fundsopretaddr,1); // Shows balance for ALL payments plans, not just the one asked for! - result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); - result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); - result.push_back(Pair("result","success")); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); } + if ( fixedAmount == 7 && payments_game(top,bottom)) + result.push_back(Pair("plan_type","payments_game")); + else + result.push_back(Pair("plan_type","snapshot")); + result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); + result.push_back(Pair("minrelease",(int64_t)minrelease)); + result.push_back(Pair("minimum",(int64_t)minimum)); + result.push_back(Pair("bottom",(int64_t)bottom)); + result.push_back(Pair("top",(int64_t)top)); + result.push_back(Pair("fixedFlag",(int64_t)fixedAmount)); + // TODO: convert to show addresses instead of scriptpubkey. + for ( auto scriptPubKey : excludeScriptPubKeys ) + a.push_back(HexStr(scriptPubKey.begin(),scriptPubKey.end())); + result.push_back(Pair("excludeScriptPubkeys",a)); + } + else if ( DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + result.push_back(Pair("plan_type","token snapshot")); + result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); + result.push_back(Pair("minrelease",(int64_t)minrelease)); + result.push_back(Pair("top",(int64_t)top)); + result.push_back(Pair("tokenid",tokenid.ToString())); + // TODO: show pubkeys instead of scriptpubkeys + for ( auto scriptPubKey : excludeScriptPubKeys ) + a.push_back(HexStr(scriptPubKey.begin(),scriptPubKey.end())); + result.push_back(Pair("excludeScriptPubkeys",a)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt decode valid payments create txid opreturn")); + } + if ( flag == 0 ) + { + txidpk = CCtxidaddr(txidaddr,createtxid); + GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); + funds = AddPaymentsInputs(false,2,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft); + result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); + GetCCaddress(cp,fundsopretaddr,Paymentspk); + fundsopret = AddPaymentsInputs(false,1,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft); + result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); + result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); + // Blocks until minrelease can be released. + elegiblefunds = AddPaymentsInputs(true,3,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft); + result.push_back(Pair("elegiblefunds",ValueFromAmount(elegiblefunds))); + result.push_back(Pair("min_release_height",chainActive.Height()+blocksleft)); + result.push_back(Pair("result","success")); } } - if ( flag == 0 ) + else { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt find valid payments create txid")); @@ -947,8 +1396,9 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { - std::vector > addressIndex; uint256 txid,hashBlock; - UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease; std::vector txidoprets; int64_t totalallocations; + std::vector > addressIndex; uint256 txid,hashBlock,tokenid; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease; std::vector txidoprets; int64_t totalallocations=0; + int32_t top=0,bottom=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount = 0; Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); SetCCtxids(addressIndex,markeraddr,true); @@ -957,9 +1407,9 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) txid = it->first.txhash; if ( it->first.index == 0 && myGetTransaction(txid,tx,hashBlock) != 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' ) + if ( tx.vout.size() > 0 && (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' || DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 'S' || DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 'O') ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) || bottom < 0 || fixedAmount < 0 || minimum < 10000 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -970,6 +1420,6 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) } } result.push_back(Pair("result","success")); - result.push_back(Pair("createtxids",a)); - return(result); + result.push_back(Pair("createtxids",a)); + return(result); } diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 407cd81fa..27529bca4 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -11,137 +11,113 @@ * * * Removal or modification of this copyright notice is prohibited. * * * - ******************************************************************************/ + ***************************************************************************** +To create payments plan start a chain with the following ac_params: + -ac_snapshot=1440 (or for test chain something smaller, if you like.) + - this enables the payments airdrop cc to work. + -ac_earlytxidcontract=237 (Eval code for prices cc.) + - this allows to know what contract this chain is paying with the scriptpubkey in the earlytxid op_return. + +./komodod -ac_name=TESTPRC -ac_supply=100000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -ac_snapshot=50 -ac_sapling=1 -ac_earlytxidcontract=237 -testnode=1 -gen -genproclimit=1 + +Then in very early block < 10 or so, do paymentsairdrop eg. + `./komodo-cli -ac_name=TESTPRC paymentsairdrop '[10,10,0,3999,0,0]' +Once this tx is confirmed, do `paymentsfund` and decode the raw hex. You can edit the source to not send the tx if requried. +Get the full `hex` of the vout[0] that pays to CryptoCondition. then place it on chain with the following command: with the hex you got in place of the hex below. + './komodo-cli -ac_name=TESTPRC opreturn_burn 1 2ea22c8020292ba5c8fd9cc89b12b35bf8f5d00196990ecbb06102b84d9748d11d883ef01e81031210008203000401cc' +copy the hex, and sendrawtransaction, copy the txid returned. +this places the scriptpubkey that pays the plan into an op_return before block 100, allowing us to retreive it, and nobody to change it. +Restart the daemon with -earlytxid= eg: + +./komodod -ac_name=TESTPRC -ac_supply=100000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -ac_snapshot=50 -ac_sapling=1 -ac_earlytxidcontract=237 -earlytxid=cf89d17fb11037f65c160d0749dddd74dc44d9893b0bb67fe1f96c1f59786496 -testnode=1 -gen -genproclimit=1 + +mine the chain past block 100, preventing anyone else, creating another payments plan on chain before block 100. + +We call the following in Validation and RPC where the address is needed. +if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + GetKomodoEarlytxidScriptPub(); + +This will fetch the op_return, calculate the scriptPubKey and save it to the global. +On daemon restart as soon as validation for BETTX happens the global will be filled, after this the transaction never needs to be looked up again. +GetKomodoEarlytxidScriptPub is on line #2080 of komodo_bitcoind.h + */ #include "CCassets.h" #include "CCPrices.h" +#include + +#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) + +#define NVOUT_CCMARKER 1 +#define NVOUT_NORMALMARKER 3 + +typedef struct OneBetData { + int64_t positionsize; + int32_t firstheight; + int64_t costbasis; + int64_t profits; + + OneBetData() { positionsize = 0; firstheight = 0; costbasis = 0; profits = 0; } // it is important to clear costbasis as it will be calculated as minmax from inital value 0 +} onebetdata; + +typedef struct BetInfo { + int64_t averageCostbasis, firstprice, lastprice, liquidationprice, equity; + int64_t rektfee; + int32_t lastheight; + int16_t leverage; + bool isOpen, isRekt; + uint256 tokenid; + + std::vector parsed; + std::vector bets; + CPubKey pk; + + BetInfo() { + averageCostbasis = firstprice = lastprice = liquidationprice = equity = 0; + lastheight = 0; + leverage = 0; + rektfee = 0; + isOpen = isRekt = false; + } +} BetInfo; + /* CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; 0.5% fee based on betamount, NOT leveraged betamount!! 0.1% collected by price basis determinant 0.2% collected by rekt tx + + PricesBet -> +/-leverage, amount, synthetic -> opreturn includes current price + funds are locked into 1of2 global CC address + for first day, long basis is MAX(correlated,smoothed), short is MIN() + reference price is the smoothed of the previous block + if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price + original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund + 0.5% of bet -> globalfund + + PricesStatus -> bettxid maxsamples returns initial params, cost basis, amount left, rekt:true/false, rektheight, initial synthetic price, current synthetic price, net gain + + PricesRekt -> bettxid height -> 0.1% to miner, rest to global CC + + PricesClose -> bettxid returns (synthetic value + amount) + + PricesList -> all bettxid -> list [bettxid, netgain] + + +*/ - At the simplest, prices CC allows to make leveraged cash settled bets on long and short BTCUSD: - BTCUSD, 1 with positive leverage parameter up to 777 - BTCUSD, 1 with negative leverage parameter up to -777 - - These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define - - Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection address (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) - - The definition of the synthetic instrument that is being tracked is actually defined with a forth type of syntax and is quite flexible to allow unlimited combinations and weights, but that is an independent aspect and will be covered later. - - Let us discuss the simplest synthetic case of a long or short of BTCUSD (or any other direct pricepoint from the trustless oracle). If you look at the charts, you will see the blue line that is direct from the miners (and therefore cant be directly used). The orange line is the 51% correlated price that is deterministically random selected from the prior 24 hours. And finally the green line which is simply the average value from the priot 24 hours. - - We will assume that the orange and green prices are able to be deterministically calculated from the raw coinbase data (blue). The prices rpc is currently working reasonably well and appears to return deterministic prices, however for use in the prices CC, it is often needed to find just a single data point. To that effect there is the temporary function prices_syntheticprice, which uses a bruteforce and totally inefficient way to calculate the correlated and smoothed prices. This inefficient process will need to be changed to a permanent storage of correlated and smoothed prices for each trustless oracle that is updated each block. This will then allow to directly lookup each value for any of the trustless prices. Such speed will indeed be needed to scale up usage of a REKT chain. - - Since the mined prices can be manipulated by any miner, combined with the +/-1% tolerance, it would be possible for an external miner to obtain a large hashrate and skew the price +1%, then to -1% to create a 2% price difference, which under high leverage might be able to generate a large profit. - - To avoid this precisely controllable biasing, the 51% correlation of past day is used (orange), which makes the price jump around a lot. The green line that sums the prior day is needed to remove this jitter back to the average value. However, this creates a 1.5 day delay to the price movement of the smoothed price compared to the current price. What this means is that if we want to use the green line to settle prices automatically, a trivial way to make money is to bet in the direct that the mined prices are relative to the smoothed prices. Given 1.5 day head start, it wont be any mystery which direction the smoothed line will move in the next 24 hours. - - So this makes the smoothed price unusable for settling the bets with. However, we cant use the correlated price either, as it would also allow to make bets when the correlated price picked a significantly lower/higher price than the raw price. The solution is to have a floating basis for the costbasis of the bet. In order to allow finding the right costbasis, for long bets the maximum price between the correlated and smoothed price is needed over the first day after the bet. For short bets, the minimum price is needed. - - What this means is that the actual starting price for a bet wont be known for sure when the bet is made, but if the price is relatively stable, there wont be much of a change. If there is a lot of recent volatility, then the starting price will have a high variability. In order to calculate the costbasis, it currently requires many calculations to find the MAX (or MIN) for the first day. Even when this is made to be a lookup, it still requires to issue a transaction to change the state of a bet from a "starting" state to a "in effect" state and this state change is indicated by whether the bettx vout that contains the costbasis utxo is spent or not. - - Once a bet goes into effect, then block by block, it is checked by the decentralized set of rekt scanners if it is rekt or not, in this case for double the reward for calculating the cost basis. This fully decentralized mechanism ensures that some node will decide to collect the free money and ensures that the bankroll is growing. To miss a rekt bet and not close it out when it can be is to allow it to survive for another block, which can change it profitability from negative to positive. - - Which comes to how profits are calculated. Once the costbasis is locked, then the profit calculation can be made based on the ratio between the costbasis and the current price, multiplied by the leverage. So, if a long position gains 10% at a 10x leverage, then approximately the entire bet amount will be made, ie. a double. Similarily with a short position, a 10% drop in price at 10x leverage will double the bet amount. Since it takes a full day to establish the costbasis, it is not possible to allow changing the costbasis for an existing bet, however it is possible to add funds so that it is less likely to be rekt. The sum of the initial bet and all added funds must be greater than the loss at every block to prevent a position from being rekt. To make it simple to calculate the amount of funds added, another bettx vout is used as a baton. Techniques similar to rogue CC to find where the bettx vout was spent and looking at each such transaction to find the total funds added can be used. - - The once that makes the bet is able to add funds or cashout at any block, as long as it isnt rekt. If it is rekt, the bettor is able to collect the rekt fee, so at least that is some consolation, but it is advised to increase the total funding to avoid being rekt. On a cashout all the funds that were bet adjusted by the profits can be collected. - - Hopefully the above description makes it clear what the following rpc calls should be doing: - - UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) - funds are locked into 1of2 global CC address - for first day, long basis is MAX(correlated,smoothed), short is MIN() - reference price is the smoothed of the previous block - if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price - original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund - 0.5% of bet -> globalfund +int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t height, int16_t leverage, std::vector vec, int64_t positionsize, int64_t &profits, int64_t &outprice); - UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) - add funding to an existing bet, doesnt change the profit calcs but does make the bet less likely to be rekt - - UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) - in the first day from the bet, the costbasis can (and usually does) change based on the MAX(correlated,smoothed) for long and MIN() for shorts - to calculate this requires a bit of work, so whatever node is first to get the proper calculation confirmed, gets a 0.1% costbasis fee - - UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) - similarily, any node can submit a rekt tx and cash in on 0.2% fee if their claim is confirmed - - UniValue PricesCashout(uint64_t txfee,uint256 bettxid) - only the actually creator of bet is able to cashout and only if it isnt rekt at that moment - - UniValue PricesInfo(uint256 bettxid,int32_t refheight) - all relevant info about a bet - - UniValue PricesList() - a list of all pending and completed bets in different lists - - - Appendix Synthetic position definition language: - - Let us start from the familiar BTCUSD nomenclature. this is similar (identical) to forex EURUSD and the equivalent. Notice the price needs two things as it is actually a ratio, ie. BTCUSD is how many USD does 1 BTC get converted to. It can be thought of as the value of BTC/USD, in other words a ratio. - - The value of BTC alone, or USD alone, it is actually quite an abstract issue and it can only be approximated. Specific ways of how to do this can be discussed later, for now it is only important to understand that all prices are actually ratios. - - And these ratios work as normal algebra does. So a/b * b/c == a/c! You can try this out with BTCUSD and USDJPY -> BTC/USD * USD/JPY -> BTC/JPY or the BTCJPY price - - division is the reciprocal, so BTCUSD reciprocated is USDBTC - - Without getting into all the possible combinations of things, let us first understand what the language allows. It uses a stack based language, where individual tokens update the state. The final state needs to have an empty stack and it will have a calculated price. - - The most important is pushing a specific price onto the stack. All the correlated and smoothed prices are normalized so it has each integer unit equal to 0.00000001, amounts above 100 million are above one, amounts less are below one. 100 million is the unit constant. - - In the prices CC synthetic definition language, the symbol that is returned pushes the adjusted price to the stack. The adjustment is selecting between the correlated and smoothed if within a day from the bet creation, or just the smoothed if after a day. You can have a maximum depth of 3, any more than that and it should return an error. - - This means there are operations that are possible on 1, 2 and 3 symbols. For 1 symbol, it is easy, the only direct operation is the inverse, which is represented by "!". All items in the language are separated by "," - - "BTCUSD, !" - - The above is the inverse or USD/BTC, which is another way to short BTCUSD. It is also possible to short the entire synthetic with a negative leverage. The exact results of a -1 leverage and inverse, might not be exact due to the math involved with calculating the profit, but it should generally be similar. - - For two symbols, there is a bit more we can do, namely multiply and divide, combined with inverting, however to simplify the language any inverting is required to be done by the ordering of the symbols and usage of multiply or divide. multiply is "*" and divide is "/" the top of the stack (last to be pushed) is the divisor, the one right before the divisor is the numerator. - - "BTCUSD, USDJPY, *" <- That will create a BTCJPY synthetic - - "BTCEUR, BTCUSD, /" <- That will create a USDEUR synthetic - - If you experiment around with this, you will see that given two symbols and the proper order and * or /, you can always create the synthetic that you want, assuming what you want is the cancelling out of the term in common with the two symbols and the resulting ratio is between the two unique terms. - */ +// helpers: -// Now we get to the three symbol operations, which there are 4 of *//, **/, *** and /// - -/* - these four operators work on the top of the stack in left to right order as the syntax of the definition lists it, though it is even possible to have the value from an earlier computation on the top of the stack. Ultimately all three will be multiplied together, so a * in a spot means it us used without changing. A / means its inverse will be used. - - "KMDBTC, BTCUSD, USDJPY, ***" <- this would create a KMDJPY synthetic. The various location of the / to make an inverse is to orient the raw symbol into the right orientation as the pricefeed is only getting one orientation of the ratio. - - So now we have covered all ways to map 1, 2 and 3 values on the top of the stack. A value can be on the top of the stack directly from a symbol, or as the result of some 1, 2 or 3 symbol operation. With a maximum stack depth of 3, it might require some experimentation to get a very complex synthetic to compile. Alternately, a stack deeper than 3 might be the acceptable solution if there are a family of synthetics that need it. - - At this point, it is time to describe the weights. It turns out that all above examples are incomplete and the synthetic descriptions are all insufficient and should generate an error. The reason is that the actual synthetic price is the value of the accumulator, which adds up all the weighted prices. In order to assign a weight to a price value on the stack, you simply use a number that is less than 2048. - - What such a weight number does, is consume the top of the stack, which also must be at depth of 1 and adds it to the accumulator. This allows combining multiple different synthetics into a meta synthetic, like for an aggregated index that is weighted by marketcap, or whatever other type of ratio trade that is desired. - - "BTCUSD, 1000, ETHBTC, BTCUSD, *, 300" -> that creates a dual index of BTCUSD and ETHUSD with a 30% ETH weight - - all weight operations consumes the one and only stack element and adds its weight to the accumulator, using this a very large number of terms can be all added together. Using inverses, allows to get the short direction into the equation mixed with longs, but all terms must be positive. If you want to create a spread between two synthetics, you need to create two different synthetics and go long with one and short with another. - - "BTCUSD, 1" with leverage -2 and "KMDBTC, BTCUSD, *, 1" with leverage 1 this will setup a +KMDUSD -2 BTCUSD spread when the two are combined, and would be at breakeven when KMDUSD gains 2x more percentage wise than BTC does. anytime KMD gains more, will be positive, if the gains are less, would be negative. - - Believe it or not, the string to binary compiler for synthetic description and interpretation of it is less than 200 lines of code. - - todo: complete all the above, bet tokens, cross chain prices within cluster These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define - - Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection scriptPubKey (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) this requires adding new vouts - - Modification: in the event there is one price in the accumulator and one price on the stack at the end, then it is a (A - B) spread - - Monetizations should be sent to: RGsWqwFviaNJSbmgWi6338NL2tKkY7ZqKL - - */ +// returns true if there are only digits and no alphas or slashes in 's' +inline bool is_weight_str(std::string s) { + return + std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); } ) > 0 && + std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isalpha(c) || c == '/'; } ) == 0; +} // start of consensus code @@ -156,8 +132,9 @@ CScript prices_betopret(CPubKey mypk,int32_t height,int64_t amount,int16_t lever uint8_t prices_betopretdecode(CScript scriptPubKey,CPubKey &pk,int32_t &height,int64_t &amount,int16_t &leverage,int64_t &firstprice,std::vector &vec,uint256 &tokenid) { std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B' ) + if (vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B') { return(f); } @@ -218,35 +195,364 @@ uint8_t prices_finalopretdecode(CScript scriptPubKey,uint256 &bettxid,int64_t &p return(0); } +// price opret basic validation and retrieval +static uint8_t PricesCheckOpret(const CTransaction & tx, vscript_t &opret) +{ + if (tx.vout.size() > 0 && GetOpReturnData(tx.vout.back().scriptPubKey, opret) && opret.size() > 2 && opret.begin()[0] == EVAL_PRICES && IS_CHARINSTR(opret.begin()[1], "BACF")) + return opret.begin()[1]; + else + return (uint8_t)0; +} + +// validate bet tx helper +static bool ValidateBetTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & bettx) +{ + uint256 tokenid; + int64_t positionsize, firstprice; + int32_t firstheight; + int16_t leverage; + CPubKey pk, pricespk; + std::vector vec; + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + GetKomodoEarlytxidScriptPub(); + if (bettx.vout.size() < 5 || bettx.vout.size() > 6) + return eval->Invalid("incorrect vout number for bet tx"); + + vscript_t opret; + if( prices_betopretdecode(bettx.vout.back().scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) != 'B') + return eval->Invalid("cannot decode opreturn for bet tx"); + + pricespk = GetUnspendable(cp, 0); + + if (MakeCC1vout(cp->evalcode, bettx.vout[0].nValue, pk) != bettx.vout[0]) + return eval->Invalid("cannot validate vout0 in bet tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, bettx.vout[1].nValue, pricespk) != bettx.vout[1]) + return eval->Invalid("cannot validate vout1 in bet tx with global pk"); + if (MakeCC1vout(cp->evalcode, bettx.vout[2].nValue, pricespk) != bettx.vout[2] ) + return eval->Invalid("cannot validate vout2 in bet tx with pk from opreturn"); + // This should be all you need to verify it, maybe also check amount? + if ( bettx.vout[4].scriptPubKey != KOMODO_EARLYTXID_SCRIPTPUB ) + return eval->Invalid("the fee was paid to wrong address."); + + int64_t betamount = bettx.vout[2].nValue; + if (betamount != (positionsize * 199) / 200) { + return eval->Invalid("invalid position size in the opreturn"); + } + + // validate if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey) + CAmount ccOutputs = 0; + for (auto vout : bettx.vout) + if (vout.scriptPubKey.IsPayToCryptoCondition()) + ccOutputs += vout.nValue; + CAmount normalInputs = TotalPubkeyNormalInputs(bettx, pk); + if (normalInputs < ccOutputs) { + return eval->Invalid("bettx normal inputs not signed with pubkey in opret"); + } + + if (leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE) { + return eval->Invalid("invalid leverage"); + } + + return true; +} + +// validate add funding tx helper +static bool ValidateAddFundingTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & addfundingtx, const CTransaction & vintx) +{ + uint256 bettxid; + int64_t amount; + CPubKey pk, pricespk; + vscript_t vintxOpret; + + if (addfundingtx.vout.size() < 3 || addfundingtx.vout.size() > 4) + return eval->Invalid("incorrect vout number for add funding tx"); + + vscript_t opret; + if (prices_addopretdecode(addfundingtx.vout.back().scriptPubKey, bettxid, pk, amount) != 'A') + return eval->Invalid("cannot decode opreturn for add funding tx"); + + pricespk = GetUnspendable(cp, 0); + uint8_t vintxFuncId = PricesCheckOpret(vintx, vintxOpret); + if (vintxFuncId != 'A' && vintxFuncId != 'B') { // if vintx is bettx + return eval->Invalid("incorrect vintx funcid"); + } + + if (vintxFuncId == 'B' && vintx.GetHash() != bettxid) {// if vintx is bettx + return eval->Invalid("incorrect bet txid in opreturn"); + } + + if (MakeCC1vout(cp->evalcode, addfundingtx.vout[0].nValue, pk) != addfundingtx.vout[0]) + return eval->Invalid("cannot validate vout0 in add funding tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, addfundingtx.vout[1].nValue, pricespk) != addfundingtx.vout[1]) + return eval->Invalid("cannot validate vout1 in add funding tx with global pk"); + + return true; +} + +// validate costbasis tx helper +static bool ValidateCostbasisTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & costbasistx, const CTransaction & bettx) +{ + uint256 bettxid; + int64_t costbasisInOpret; + CPubKey pk, pricespk; + int32_t height; + + return true; //deprecated + + // check basic structure: + if (costbasistx.vout.size() < 3 || costbasistx.vout.size() > 4) + return eval->Invalid("incorrect vout count for costbasis tx"); + + vscript_t opret; + if (prices_costbasisopretdecode(costbasistx.vout.back().scriptPubKey, bettxid, pk, height, costbasisInOpret) != 'C') + return eval->Invalid("cannot decode opreturn for costbasis tx"); + + pricespk = GetUnspendable(cp, 0); + if (CTxOut(costbasistx.vout[0].nValue, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG) != costbasistx.vout[0]) //might go to any pk who calculated costbasis + return eval->Invalid("cannot validate vout0 in costbasis tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, costbasistx.vout[1].nValue, pricespk) != costbasistx.vout[1]) + return eval->Invalid("cannot validate vout1 in costbasis tx with global pk"); + + if (bettx.GetHash() != bettxid) + return eval->Invalid("incorrect bettx id"); + + if (bettx.vout.size() < 1) // for safety and for check encapsulation + return eval->Invalid("incorrect bettx no vouts"); + + // check costbasis rules: + if (costbasistx.vout[0].nValue > bettx.vout[1].nValue / 10) { + return eval->Invalid("costbasis myfee too big"); + } + + uint256 tokenid; + int64_t positionsize, firstprice; + int32_t firstheight; + int16_t leverage; + CPubKey betpk; + std::vector vec; + if (prices_betopretdecode(bettx.vout.back().scriptPubKey, betpk, firstheight, positionsize, leverage, firstprice, vec, tokenid) != 'B') + return eval->Invalid("cannot decode opreturn for bet tx"); + + if (firstheight + PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH > chainActive.Height()) { + return eval->Invalid("cannot calculate costbasis yet"); + } + + int64_t costbasis = 0, profits, lastprice; + int32_t retcode = prices_syntheticprofits(costbasis, firstheight, firstheight + PRICES_DAYWINDOW, leverage, vec, positionsize, profits, lastprice); + if (retcode < 0) + return eval->Invalid("cannot calculate costbasis yet"); + std::cerr << "ValidateCostbasisTx() costbasis=" << costbasis << " costbasisInOpret=" << costbasisInOpret << std::endl; + if (costbasis != costbasisInOpret) { + //std::cerr << "ValidateBetTx() " << "incorrect costbasis value" << std::endl; + return eval->Invalid("incorrect costbasis value"); + } + + return true; +} + +// validate final tx helper +static bool ValidateFinalTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & finaltx, const CTransaction & bettx) +{ + uint256 bettxid; + int64_t amount; + CPubKey pk, pricespk; + int64_t profits; + int32_t height; + int64_t firstprice, costbasis, addedbets, positionsize; + int16_t leverage; + + if (finaltx.vout.size() < 3 || finaltx.vout.size() > 4) { + //std::cerr << "ValidateFinalTx()" << " incorrect vout number for final tx =" << finaltx.vout.size() << std::endl; + return eval->Invalid("incorrect vout number for final tx"); + } + + vscript_t opret; + if (prices_finalopretdecode(finaltx.vout.back().scriptPubKey, bettxid, profits, height, pk, firstprice, costbasis, addedbets, positionsize, leverage) != 'F') + return eval->Invalid("cannot decode opreturn for final tx"); + + if (bettx.GetHash() != bettxid) + return eval->Invalid("incorrect bettx id"); + + pricespk = GetUnspendable(cp, 0); + + if (CTxOut(finaltx.vout[0].nValue, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG) != finaltx.vout[0]) + return eval->Invalid("cannot validate vout0 in final tx with pk from opreturn"); + + if( finaltx.vout.size() == 3 && MakeCC1vout(cp->evalcode, finaltx.vout[1].nValue, pricespk) != finaltx.vout[1] ) + return eval->Invalid("cannot validate vout1 in final tx with global pk"); + + return true; +} + +// validate prices tx function +// performed checks: +// basic tx structure (vout num) +// basic tx opret structure +// reference to the bet tx vout +// referenced bet txid in tx opret +// referenced bet tx structure +// non-final tx has only 1 cc vin +// cc vouts to self with mypubkey from opret +// cc vouts to global pk with global pk +// for bet tx that normal inputs digned with my pubkey from the opret >= cc outputs - disable betting for other pubkeys (Do we need this rule?) +// TODO: +// opret params (firstprice,positionsize...) +// costbasis calculation +// cashout balance (PricesExactAmounts) +// use the special address for 50% fees bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { + vscript_t vopret; + + if (strcmp(ASSETCHAINS_SYMBOL, "REKT0") == 0 && chainActive.Height() < 2965) + return true; + // check basic opret rules: + if (PricesCheckOpret(tx, vopret) == 0) + return eval->Invalid("tx has no prices opreturn"); + + uint8_t funcId = vopret.begin()[1]; + + CTransaction firstVinTx; + vscript_t firstVinTxOpret; + bool foundFirst = false; + int32_t ccVinCount = 0; + uint32_t prevoutN = 0; + + // check basic rules: + + // find first cc vin and load vintx (might be either bet or add funding tx): + for (auto vin : tx.vin) { + if (cp->ismyvin(vin.scriptSig)) { + CTransaction vintx; + uint256 hashBlock; + vscript_t vintxOpret; + + if (!myGetTransaction(vin.prevout.hash, vintx, hashBlock)) + return eval->Invalid("cannot load vintx"); + + if (PricesCheckOpret(vintx, vintxOpret) == 0) { + //return eval->Invalid("cannot find prices opret in vintx"); + std::cerr << "PricesValidate() " << "cannot find prices opret in vintx" << std::endl; + } + + if (funcId != 'F' && vintxOpret.begin()[1] == 'B' && prevoutN == 1) { + //return eval->Invalid("cannot spend bet marker"); + std::cerr << "PricesValidate() " << " non-final tx cannot spend cc marker vout=" << prevoutN << std::endl; + } + + if (!foundFirst) { + prevoutN = vin.prevout.n; + firstVinTx = vintx; + firstVinTxOpret = vintxOpret; + foundFirst = true; + } + ccVinCount++; + } + } + if (!foundFirst) + return eval->Invalid("prices cc vin not found"); + + if (funcId != 'F' && ccVinCount > 1) {// for all prices tx except final tx only one cc vin is allowed + //return eval->Invalid("only one prices cc vin allowed for this tx"); + std::cerr << "PricesValidate() " << "only one prices cc vin allowed for this tx" << std::endl; + } + + switch (funcId) { + case 'B': // bet + return eval->Invalid("unexpected validate for bet funcid"); + + case 'A': // add funding + // check tx structure: + if (!ValidateAddFundingTx(cp, eval, tx, firstVinTx)) { + //return false; // invalid state is already set in the func + std::cerr << "PricesValidate() " << "ValidateAddFundingTx = false " << eval->state.GetRejectReason() << std::endl; + } + + if (firstVinTxOpret.begin()[1] == 'B') { + if (!ValidateBetTx(cp, eval, firstVinTx)) {// check tx structure + // return false; + std::cerr << "PricesValidate() " << "funcId=A ValidatebetTx = false " << eval->state.GetRejectReason() << std::endl; + } + } + else if (firstVinTxOpret.begin()[1] == 'A') { + // no need to validate the previous addfunding tx (it was validated when added) + } + + if (prevoutN != 0) { // check spending rules + // return eval->Invalid("incorrect vintx vout to spend"); + std::cerr << "PricesValidate() " << "add fund tx incorrect vout to spend=" << prevoutN << std::endl; + } + break; + + case 'C': // set costbasis + if (!ValidateCostbasisTx(cp, eval, tx, firstVinTx)) { + //return false; + std::cerr << "PricesValidate() " << "ValidateCostbasisTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (!ValidateBetTx(cp, eval, firstVinTx)) { + //return false; + std::cerr << "PricesValidate() " << "funcId=C ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (prevoutN != 1) { // check spending rules + // return eval->Invalid("incorrect vout to spend"); + std::cerr << "PricesValidate() " << "costbasis tx incorrect vout to spend=" << prevoutN << std::endl; + } + //return eval->Invalid("test: costbasis is good"); + break; + + case 'F': // final tx + if (!ValidateFinalTx(cp, eval, tx, firstVinTx)) { + ///return false; + std::cerr << "PricesValidate() " << "ValidateFinalTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (!ValidateBetTx(cp, eval, firstVinTx)) { + // return false; + std::cerr << "PricesValidate() " << "ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (prevoutN != 1) { // check spending rules + // return eval->Invalid("incorrect vout to spend"); + std::cerr << "PricesValidate() "<< "final tx incorrect vout to spend=" << prevoutN << std::endl; + } + break; + + default: + return eval->Invalid("invalid funcid"); + } + + eval->state = CValidationState(); return true; } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,int64_t total,int32_t maxinputs,uint256 vintxid,int32_t vinvout) +int64_t AddPricesInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, char *destaddr, int64_t total, int32_t maxinputs) { - int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + int64_t nValue, price, totalinputs = 0; uint256 txid, hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout, n = 0; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,destaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + + SetCCunspents(unspentOutputs, destaddr); + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( vout == vinvout && txid == vintxid ) - continue; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) + //if (vout == exclvout && txid == excltxid) // exclude vout which is added directly to vins outside this function + // continue; + if (GetTransaction(txid, vintx, hashBlock, false) != 0 && vout < vintx.vout.size()) { - if ( (nValue= vintx.vout[vout].nValue) >= total/maxinputs && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + vscript_t vopret; + uint8_t funcId = PricesCheckOpret(vintx, vopret); + if (funcId == 'B' && vout == 1) // skip cc marker + continue; + + if ((nValue = vintx.vout[vout].nValue) >= total / maxinputs && myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, vout) == 0) { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); + if (total != 0 && maxinputs != 0) + mtx.vin.push_back(CTxIn(txid, vout, CScript())); nValue = it->second.satoshis; totalinputs += nValue; n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; } } @@ -254,474 +560,1482 @@ int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char return(totalinputs); } -UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +UniValue prices_rawtxresult(UniValue &result, std::string rawtx, int32_t broadcastflag) { CTransaction tx; - if ( rawtx.size() > 0 ) + if (rawtx.size() > 0) { - result.push_back(Pair("hex",rawtx)); - if ( DecodeHexTx(tx,rawtx) != 0 ) + result.push_back(Pair("hex", rawtx)); + if (DecodeHexTx(tx, rawtx) != 0) { - if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + if (broadcastflag != 0 && myAddtomempool(tx) != 0) RelayTransaction(tx); - result.push_back(Pair("txid",tx.GetHash().ToString())); - result.push_back(Pair("result","success")); - } else result.push_back(Pair("error","decode hex")); - } else result.push_back(Pair("error","couldnt finalize CCtx")); + result.push_back(Pair("txid", tx.GetHash().ToString())); + result.push_back(Pair("result", "success")); + } + else + result.push_back(Pair("error", "decode hex")); + } + else + result.push_back(Pair("error", "couldnt finalize CCtx")); return(result); } -int32_t prices_syntheticvec(std::vector &vec,std::vector synthetic) +static std::string prices_getsourceexpression(const std::vector &vec) { + + std::string expr; + + for (int32_t i = 0; i < vec.size(); i++) + { + char name[65]; + std::string operand; + uint16_t opcode = vec[i]; + int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // index or weight + + switch (opcode & KOMODO_PRICEMASK) + { + case 0: // indices + komodo_pricename(name, value); + operand = std::string(name); + break; + + case PRICES_WEIGHT: // multiply by weight and consume top of stack by updating price + operand = std::to_string(value); + break; + + case PRICES_MULT: // "*" + operand = std::string("*"); + break; + + case PRICES_DIV: // "/" + operand = std::string("/"); + break; + + case PRICES_INV: // "!" + operand = std::string("!"); + break; + + case PRICES_MDD: // "*//" + operand = std::string("*//"); + break; + + case PRICES_MMD: // "**/" + operand = std::string("**/"); + break; + + case PRICES_MMM: // "***" + operand = std::string("***"); + break; + + case PRICES_DDD: // "///" + operand = std::string("///"); + break; + + default: + return "invalid opcode"; + break; + } + + if (expr.size() > 0) + expr += std::string(", "); + expr += operand; + } + return expr; +} + +// helper functions to get synthetic expression reduced: + +// return s true and needed operand count if string is opcode +static bool prices_isopcode(const std::string &s, int &need) { - int32_t i,need,ind,depth = 0; std::string opstr; uint16_t opcode,weight; - if ( synthetic.size() == 0 ) + if (s == "!") { + need = 1; + return true; + } + else if (s == "*" || s == "/") { + need = 2; + return true; + } + else if (s == "***" || s == "///" || s == "*//" || s == "**/") { + need = 3; + return true; + } + else + return false; +} + +// split pair onto two quotes divided by "_" +static void prices_splitpair(const std::string &pair, std::string &upperquote, std::string &bottomquote) +{ + size_t pos = pair.find('_'); // like BTC_USD + if (pos != std::string::npos) { + upperquote = pair.substr(0, pos); + bottomquote = pair.substr(pos + 1); + } + else { + upperquote = pair; + bottomquote = ""; + } + //std::cerr << "prices_splitpair: upperquote=" << upperquote << " bottomquote=" << bottomquote << std::endl; +} + +// invert pair like BTS_USD -> USD_BTC +static std::string prices_invertpair(const std::string &pair) +{ + std::string upperquote, bottomquote; + prices_splitpair(pair, upperquote, bottomquote); + return bottomquote + std::string("_") + upperquote; +} + +// invert pairs in operation accordingly to "/" operator, convert operator to * or *** +static void prices_invertoperation(const std::vector &vexpr, int p, std::vector &voperation) +{ + int need; + + voperation.clear(); + if (prices_isopcode(vexpr[p], need)) { + if (need > 1) { + if (need == 2) { + voperation.push_back(vexpr[p - 2]); + if (vexpr[p] == "/") + voperation.push_back(prices_invertpair(vexpr[p - 1])); + else + voperation.push_back(vexpr[p - 1]); + voperation.push_back("*"); + } + + if (need == 3) { + int i; + std::string::const_iterator c; + for (c = vexpr[p].begin(), i = -3; c != vexpr[p].end(); c++, i++) { + if (*c == '/') + voperation.push_back(prices_invertpair(vexpr[p + i])); + else + voperation.push_back(vexpr[p + i]); + } + voperation.push_back("***"); + } + } + else if (vexpr[p] == "!") { + voperation.push_back(prices_invertpair(vexpr[p - 1])); + // do not add operator + } + } + + //std::cerr << "prices_invert inverted="; + //for (auto v : voperation) std::cerr << v << " "; + //std::cerr << std::endl; +} + +// reduce pairs in the operation, change or remove opcode if reduced +static int prices_reduceoperands(std::vector &voperation) +{ + int opcount = voperation.size() - 1; + int need = opcount; + //std::cerr << "prices_reduceoperands begin need=" << need << std::endl; + + while (true) { + int i; + //std::cerr << "prices_reduceoperands opcount=" << opcount << std::endl; + for (i = 0; i < opcount; i++) { + std::string upperquote, bottomquote; + bool breaktostart = false; + + //std::cerr << "prices_reduceoperands voperation[i]=" << voperation[i] << " i=" << i << std::endl; + prices_splitpair(voperation[i], upperquote, bottomquote); + if (upperquote == bottomquote) { + std::cerr << "prices_reduceoperands erasing i=" << i << std::endl; + voperation.erase(voperation.begin() + i); + opcount--; + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; + + if (voperation.size() > 0 && voperation.back() == "*") + voperation.pop_back(); + breaktostart = true; + break; + } + + + int j; + for (j = i + 1; j < opcount; j++) { + + //std::cerr << "prices_reduceoperands voperation[j]=" << voperation[j] << " j=" << j << std::endl; + + std::string upperquotej, bottomquotej; + prices_splitpair(voperation[j], upperquotej, bottomquotej); + if (upperquote == bottomquotej || bottomquote == upperquotej) { + if (upperquote == bottomquotej) + voperation[i] = upperquotej + "_" + bottomquote; + else + voperation[i] = upperquote + "_" + bottomquotej; + //std::cerr << "prices_reduceoperands erasing j=" << j << std::endl; + voperation.erase(voperation.begin() + j); + opcount--; + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; + + need--; + if (voperation.back() == "***") { + voperation.pop_back(); + voperation.push_back("*"); // convert *** to * + } + else if (voperation.back() == "*") { + voperation.pop_back(); // convert * to nothing + } + breaktostart = true; + break; + } + } + if (breaktostart) + break; + } + if (i >= opcount) // all seen + break; + } + + //std::cerr << "prices_reduceoperands end need=" << need << std::endl; + return need; +} + +// substitute reduced operation in vectored expr +static void prices_substitutereduced(std::vector &vexpr, int p, std::vector voperation) +{ + int need; + if (prices_isopcode(vexpr[p], need)) { + vexpr.erase(vexpr.begin() + p - need, vexpr.begin() + p + 1); + vexpr.insert(vexpr.begin() + p - need, voperation.begin(), voperation.end()); + } +} + +// try to reduce synthetic expression by substituting "BTC_USD, BTC_EUR, 30, /" with "EUR_USD, 30" etc +static std::string prices_getreducedexpr(const std::string &expr) +{ + std::string reduced; + + std::vector vexpr; + SplitStr(expr, vexpr); + + for (size_t i = 0; i < vexpr.size(); i++) { + int need; + + if (prices_isopcode(vexpr[i], need)) { + std::vector voperation; + prices_invertoperation(vexpr, i, voperation); + if (voperation.size() > 0) { + int reducedneed = prices_reduceoperands(voperation); + if (reducedneed < need) { + prices_substitutereduced(vexpr, i, voperation); + } + } + } + } + + for (size_t i = 0; i < vexpr.size(); i++) { + if (reduced.size() > 0) + reduced += std::string(", "); + reduced += vexpr[i]; + } + + //std::cerr << "reduced=" << reduced << std::endl; + return reduced; +} + +// parse synthetic expression into vector of codes +int32_t prices_syntheticvec(std::vector &vec, std::vector synthetic) +{ + int32_t i, need, ind, depth = 0; std::string opstr; uint16_t opcode, weight; + if (synthetic.size() == 0) { + std::cerr << "prices_syntheticvec() expression is empty" << std::endl; return(-1); - for (i=0; i= 0 ) + else if (!is_weight_str(opstr) && (ind = komodo_priceind(opstr.c_str())) >= 0) opcode = ind, need = 0; - else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) + else if ((weight = atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES) { opcode = PRICES_WEIGHT | weight; need = 1; - } else return(-2); - if ( depth < need ) + } + else { + std::cerr << "prices_syntheticvec() incorrect opcode=" << opstr << std::endl; + return(-2); + } + if (depth < need) { + std::cerr << "prices_syntheticvec() incorrect not enough operands for opcode=" << opstr << std::endl; return(-3); + } depth -= need; - if ( (opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT ) // weight - depth++; - if ( depth > 3 ) + std::cerr << "prices_syntheticvec() opcode=" << opcode << " opstr=" << opstr << " need=" << need << " depth=" << depth << std::endl; + if ((opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT) { // skip weight + depth++; // increase operands count + std::cerr << "depth++=" << depth << std::endl; + } + if (depth > 3) { + std::cerr << "prices_syntheticvec() too many operands, last=" << opstr << std::endl; return(-4); + } vec.push_back(opcode); } - if ( depth != 0 ) + if (depth != 0) { - fprintf(stderr,"depth.%d not empty\n",depth); + fprintf(stderr, "prices_syntheticvec() depth.%d not empty\n", depth); return(-5); } return(0); } -int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) +// calculates price for synthetic expression +int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t minmax, int16_t leverage) { - int32_t i,ind,errcode,depth,retval = -1; uint16_t opcode; int64_t pricedata[PRICES_MAXDATAPOINTS],pricestack[4],price,den,a,b,c; - price = den = depth = errcode = 0; - for (i=0; i= 2) { + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * b) / SATOSHIDEN; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); + + } + else + errcode = -3; + break; + + case PRICES_DIV: // "/" + if (depth >= 2) { + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * SATOSHIDEN) / b; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -4; + break; + + case PRICES_INV: // "!" + if (depth >= 1) { + a = pricestack[--depth]; + // pricestack[depth++] = (SATOSHIDEN * SATOSHIDEN) / a; + mpz_set_si(mpzA, a); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzA); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -5; + break; + + case PRICES_MDD: // "*//" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (((a * SATOSHIDEN) / b) * SATOSHIDEN) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -6; + break; + + case PRICES_MMD: // "**/" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * b) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -7; + break; + + case PRICES_MMM: // "***" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (((a * b) / SATOSHIDEN ) * c) / SATOSHIDEN; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_mul(mpzResult, mpzResult, mpzC); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -8; + break; + + case PRICES_DDD: // "///" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + //pricestack[depth++] = (((((SATOSHIDEN * SATOSHIDEN) / a) * SATOSHIDEN) / b) * SATOSHIDEN) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzA); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -9; + break; + + default: + errcode = -10; + break; + } + + std::cerr << "prices_syntheticprice test mpzResult=" << mpz_get_si(mpzResult) << std::endl; + // check overflow: + if (mpz_cmp_si(mpzResult, std::numeric_limits::max()) > 0) { + errcode = -13; + break; + } + + if (errcode != 0) + break; + + if( depth > 0 ) + std::cerr << "prices_syntheticprice top pricestack[depth-1=" << depth-1 << "]=" << pricestack[depth-1] << std::endl; + else + std::cerr << "prices_syntheticprice pricestack empty" << std::endl; + } - if ( den == 0 ) + free(pricedata); + mpz_clear(mpzResult); + mpz_clear(mpzA); + mpz_clear(mpzB); + mpz_clear(mpzC); + + if( mpz_get_si(mpzDen) != 0 ) + mpz_tdiv_q(mpzTotalPrice, mpzTotalPrice, mpzDen); // price / den + + int64_t den = mpz_get_si(mpzDen); + int64_t priceIndex = mpz_get_si(mpzTotalPrice); + + mpz_clear(mpzDen); + mpz_clear(mpzTotalPrice); + mpz_clear(mpzPriceValue); + + if (errcode != 0) + std::cerr << "prices_syntheticprice errcode in switch=" << errcode << std::endl; + + if (errcode == -13) { + std::cerr << "prices_syntheticprice overflow in price" << std::endl; + return errcode; + } + + if (den == 0) { + std::cerr << "prices_syntheticprice den==0 return err=-11" << std::endl; return(-11); - else if ( depth != 0 ) + } + else if (depth != 0) { + std::cerr << "prices_syntheticprice depth!=0 err=-12" << std::endl; return(-12); - else if ( errcode != 0 ) + } + else if (errcode != 0) { + std::cerr << "prices_syntheticprice err=" << errcode << std::endl; return(errcode); - return(price / den); -} - -int64_t prices_syntheticprofits(int64_t &costbasis,int32_t firstheight,int32_t height,int16_t leverage,std::vector vec,int64_t positionsize,int64_t addedbets) -{ - int64_t price,profits = 0; int32_t minmax; - minmax = (height > firstheight+PRICES_DAYWINDOW); - if ( (price= prices_syntheticprice(vec,height,minmax,leverage)) < 0 ) - { - fprintf(stderr,"unexpected zero synthetic price at height.%d\n",height); - return(0); } - if ( minmax != 0 ) - { - if ( leverage > 0 && price > costbasis ) - costbasis = price; - else if ( leverage < 0 && (costbasis == 0 || price < costbasis) ) - costbasis = price; - } - profits = ((price * SATOSHIDEN) / costbasis) - SATOSHIDEN; - profits *= leverage * positionsize; - return(positionsize + addedbets + profits); + std::cerr << "prices_syntheticprice priceIndex=" << priceIndex << " den=" << den << std::endl; + + return priceIndex; } -void prices_betjson(UniValue &result,int64_t profits,int64_t costbasis,int64_t positionsize,int64_t addedbets,int16_t leverage,int32_t firstheight,int64_t firstprice) +// calculates costbasis and profit/loss for the bet +int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t height, int16_t leverage, std::vector vec, int64_t positionsize, int64_t &profits, int64_t &outprice) { - result.push_back(Pair("profits",ValueFromAmount(profits))); - result.push_back(Pair("costbasis",ValueFromAmount(costbasis))); - result.push_back(Pair("positionsize",ValueFromAmount(positionsize))); - result.push_back(Pair("addedbets",ValueFromAmount(addedbets))); - result.push_back(Pair("leverage",(int64_t)leverage)); - result.push_back(Pair("firstheight",(int64_t)firstheight)); - result.push_back(Pair("firstprice",ValueFromAmount(firstprice))); + int64_t price; +#ifndef TESTMODE + const int32_t COSTBASIS_PERIOD = PRICES_DAYWINDOW; +#else + const int32_t COSTBASIS_PERIOD = 7; +#endif + + + if (height < firstheight) { + fprintf(stderr, "requested height is lower than bet firstheight.%d\n", height); + return -1; + } + + int32_t minmax = (height < firstheight + COSTBASIS_PERIOD); // if we are within 24h then use min or max value + + if ((price = prices_syntheticprice(vec, height, minmax, leverage)) < 0) + { + fprintf(stderr, "error getting synthetic price at height.%d\n", height); + return -1; + } + + // clear lowest positions: + //price /= PRICES_POINTFACTOR; + //price *= PRICES_POINTFACTOR; + outprice = price; + + if (minmax) { // if we are within day window, set temp costbasis to max (or min) price value + if (leverage > 0 && price > costbasis) { + costbasis = price; // set temp costbasis + std::cerr << "prices_syntheticprofits() minmax costbasis=" << costbasis << std::endl; + } + else if (leverage < 0 && (costbasis == 0 || price < costbasis)) { + costbasis = price; + std::cerr << "prices_syntheticprofits() minmax costbasis=" << costbasis << std::endl; + } + //else { //-> use the previous value + // std::cerr << "prices_syntheticprofits() unchanged costbasis=" << costbasis << " price=" << price << " leverage=" << leverage << std::endl; + //} + } + else { + if (height == firstheight + COSTBASIS_PERIOD) { + // if costbasis not set, just set it + //costbasis = price; + + // use calculated minmax costbasis + std::cerr << "prices_syntheticprofits() use permanent costbasis=" << costbasis << " at height=" << height << std::endl; + } + } + + // normalize to 10,000,000 to prevent underflow + //profits = costbasis > 0 ? (((price / PRICES_POINTFACTOR * PRICES_NORMFACTOR) / costbasis) - PRICES_NORMFACTOR / PRICES_POINTFACTOR) * PRICES_POINTFACTOR : 0; + //double dprofits = costbasis > 0 ? ((double)price / (double)costbasis - 1) : 0; + + //std::cerr << "prices_syntheticprofits() test value1 (price/PRICES_POINTFACTOR * PRICES_NORMFACTOR)=" << (price / PRICES_POINTFACTOR * PRICES_NORMFACTOR) << std::endl; + //std::cerr << "prices_syntheticprofits() test value2 (price/PRICES_POINTFACTOR * PRICES_NORMFACTOR)/costbasis=" << (costbasis != 0 ? (price / PRICES_POINTFACTOR * PRICES_NORMFACTOR)/costbasis : 0) << std::endl; + + //std::cerr << "prices_syntheticprofits() fractional profits=" << profits << std::endl; + //std::cerr << "prices_syntheticprofits() profits double=" << (double)price / (double)costbasis -1.0 << std::endl; + //double dprofits = (double)price / (double)costbasis - 1.0; + + //profits *= ((int64_t)leverage * (int64_t)positionsize); + //profits /= (int64_t)PRICES_NORMFACTOR; // de-normalize + + //dprofits *= ((double)leverage * (double)positionsize); + + //dprofits *= leverage * positionsize; + // profits = dprofits; + //std::cerr << "prices_syntheticprofits() dprofits=" << dprofits << std::endl; + + if (costbasis > 0) { + mpz_t mpzProfits; + mpz_t mpzCostbasis; + mpz_t mpzPrice; + mpz_t mpzLeverage; + + mpz_init(mpzProfits); + mpz_init(mpzCostbasis); + mpz_init(mpzPrice); + mpz_init(mpzLeverage); + + mpz_set_si(mpzCostbasis, costbasis); + mpz_set_si(mpzPrice, price); + mpz_mul_ui(mpzPrice, mpzPrice, SATOSHIDEN); // (price*SATOSHIDEN) + + mpz_tdiv_q(mpzProfits, mpzPrice, mpzCostbasis); // profits = (price*SATOSHIDEN)/costbasis // normalization + mpz_sub_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits -= SATOSHIDEN + + mpz_set_si(mpzLeverage, leverage); + mpz_mul(mpzProfits, mpzProfits, mpzLeverage); // profits *= leverage + mpz_mul_ui(mpzProfits, mpzProfits, positionsize); // profits *= positionsize + mpz_tdiv_q_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits /= SATOSHIDEN // de-normalization + + profits = mpz_get_si(mpzProfits); + + mpz_clear(mpzLeverage); + mpz_clear(mpzProfits); + mpz_clear(mpzCostbasis); + mpz_clear(mpzPrice); + + } + else + profits = 0; + + std::cerr << "prices_syntheticprofits() profits=" << profits << std::endl; + return 0; // (positionsize + addedbets + profits); } -int64_t prices_costbasis(CTransaction bettx) +// makes result json object +void prices_betjson(UniValue &result, std::vector bets, int16_t leverage, int32_t endheight, int64_t lastprice) +{ + + UniValue resultbets(UniValue::VARR); + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : bets) { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("positionsize", ValueFromAmount(b.positionsize))); + entry.push_back(Pair("profits", ValueFromAmount(b.profits))); + entry.push_back(Pair("costbasis", ValueFromAmount(b.costbasis))); + entry.push_back(Pair("firstheight", b.firstheight)); + resultbets.push_back(entry); + totalbets += b.positionsize; + totalprofits += b.profits; + } + int64_t equity = totalbets + totalprofits; + + result.push_back(Pair("bets", resultbets)); + result.push_back(Pair("leverage", (int64_t)leverage)); + result.push_back(Pair("TotalPositionSize", ValueFromAmount(totalbets))); + result.push_back(Pair("TotalProfits", ValueFromAmount(totalprofits))); + result.push_back(Pair("equity", ValueFromAmount(equity))); + result.push_back(Pair("LastPrice", ValueFromAmount(lastprice))); + result.push_back(Pair("LastHeight", endheight)); +} + +// retrieves costbasis from a tx spending bettx vout1 (deprecated) +int64_t prices_costbasis(CTransaction bettx, uint256 &txidCostbasis) { int64_t costbasis = 0; // if vout1 is spent, follow and extract costbasis from opreturn //uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) + //uint256 txidCostbasis; + int32_t vini; + int32_t height; + txidCostbasis = zeroid; +/* + if (CCgetspenttxid(txidCostbasis, vini, height, bettx.GetHash(), 1) < 0) { + std::cerr << "prices_costbasis() no costbasis txid found" << std::endl; + return 0; + } - return(costbasis); + CTransaction txCostbasis; + uint256 hashBlock; + uint256 bettxid; + CPubKey pk; + bool isLoaded = false; + uint8_t funcId = 0; + + if ((isLoaded = myGetTransaction(txidCostbasis, txCostbasis, hashBlock)) && + txCostbasis.vout.size() > 0 && + (funcId = prices_costbasisopretdecode(txCostbasis.vout.back().scriptPubKey, bettxid, pk, height, costbasis)) != 0) { + return costbasis; + } + + std::cerr << "prices_costbasis() cannot load costbasis tx or decode opret" << " isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl; */ + return 0; } -int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid) +// enumerates and retrieves added bets, returns the last baton txid +int64_t prices_enumaddedbets(uint256 &batontxid, std::vector &bets, uint256 bettxid) { - int64_t addedbets = 0; + int64_t addedBetsTotal = 0; + int32_t vini; + int32_t height; + int32_t retcode; + + batontxid = bettxid; // initially set to the source bet tx + uint256 sourcetxid = bettxid; + // iterate through batons, adding up vout1 -> addedbets - return(addedbets); + while ((retcode = CCgetspenttxid(batontxid, vini, height, sourcetxid, 0)) == 0) { + + CTransaction txBaton; + CBlockIndex blockIdx; + uint256 bettxidInOpret; + CPubKey pk; + bool isLoaded = false; + uint8_t funcId = 0; + int64_t amount; + EvalRef eval; + + if ((isLoaded = eval->GetTxConfirmed(batontxid, txBaton, blockIdx)) && + blockIdx.IsValid() && + txBaton.vout.size() > 0 && + (funcId = prices_addopretdecode(txBaton.vout.back().scriptPubKey, bettxidInOpret, pk, amount)) != 0) + { + OneBetData added; + + addedBetsTotal += amount; + added.positionsize = amount; + added.firstheight = blockIdx.GetHeight(); + bets.push_back(added); + std::cerr << "prices_batontxid() added amount=" << amount << std::endl; + } + else { + std::cerr << "prices_batontxid() cannot load or decode add bet tx, isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl; + return -1; + } + sourcetxid = batontxid; + } + + return(addedBetsTotal); } -UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) +// pricesbet rpc impl: make betting tx +UniValue PricesBet(int64_t txfee, int64_t amount, int16_t leverage, std::vector synthetic) { - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; char myaddr[64]; std::string rawtx; - if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) + fprintf(stderr, "assetchains_contract.%i vs eval_prices.%i\n",ASSETCHAINS_EARLYTXIDCONTRACT, EVAL_PRICES); + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","leverage too big")); + // Lock here, as in validation we cannot call lock in the function itself. + // may not be needed as the validation call to update the global, is called in a LOCK already, and it can only update there and here. + LOCK(cs_main); + GetKomodoEarlytxidScriptPub(); + } + /* + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CPubKey pricespk, mypk; + int64_t betamount, firstprice = 0; + std::vector vec; + //char myaddr[64]; + std::string rawtx; + + if (leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "leverage too big")); return(result); } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( prices_syntheticvec(vec,synthetic) < 0 || (firstprice= prices_syntheticprice(vec,nextheight-1,1,leverage)) < 0 || vec.size() == 0 || vec.size() > 4096 ) + pricespk = GetUnspendable(cp, 0); + //GetCCaddress(cp, myaddr, mypk); + if (prices_syntheticvec(vec, synthetic) < 0 || (firstprice = prices_syntheticprice(vec, nextheight - 1, 1, leverage)) < 0 || vec.size() == 0 || vec.size() > 4096) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","invalid synthetic")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid synthetic")); return(result); } - if ( AddNormalinputs(mtx,mypk,amount+4*txfee,64) >= amount+4*txfee ) + if (AddNormalinputs(mtx, mypk, amount + 4 * txfee, 64) >= amount + 4 * txfee) { betamount = (amount * 199) / 200; - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding - mtx.vout.push_back(MakeCC1vout(cp->evalcode,(amount-betamount)+2*txfee,pricespk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,betamount,pricespk,mypk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_betopret(mypk,nextheight-1,amount,leverage,firstprice,vec,zeroid)); - return(prices_rawtxresult(result,rawtx,0)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, mypk)); // vout0 baton for total funding + // mtx.vout.push_back(MakeCC1vout(cp->evalcode, (amount - betamount) + 2 * txfee, pricespk)); // vout1, when spent, costbasis is set + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, pricespk)); // vout1 cc marker (NVOUT_CCMARKER) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, betamount, pricespk)); // vout2 betamount + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); // vout3 normal marker NVOUT_NORMALMARKER - TODO: remove it as we have cc marker now, when move to the new chain + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + { + // Lock here, as in validation we cannot call lock in the function itself. + // may not be needed as the validation call to update the global, is called in a LOCK already, and it can only update there and here. + LOCK(cs_main); + GetKomodoEarlytxidScriptPub(); + } + mtx.vout.push_back(CTxOut(amount-betamount, KOMODO_EARLYTXID_SCRIPTPUB)); + + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_betopret(mypk, nextheight - 1, amount, leverage, firstprice, vec, zeroid)); + return(prices_rawtxresult(result, rawtx, 0)); } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); - return(result); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); + return(result); + */ } -UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) +// pricesaddfunding rpc impl: add yet another bet +UniValue PricesAddFunding(int64_t txfee, uint256 bettxid, int64_t amount) { int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; CTransaction bettx; + CPubKey pricespk, mypk; + //int64_t addedbets = 0, betamount, firstprice; + std::vector vec; + uint256 batontxid; + std::string rawtx; + //char myaddr[64]; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) + pricespk = GetUnspendable(cp, 0); + //GetCCaddress(cp, myaddr, mypk); + if (AddNormalinputs(mtx, mypk, amount + 2*txfee, 64) >= amount + 2*txfee) { - if ( prices_batontxid(batontxid,bettx,bettxid) >= 0 ) + std::vector bets; + if (prices_enumaddedbets(batontxid, bets, bettxid) >= 0) { - mtx.vin.push_back(CTxIn(batontxid,0,CScript())); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding - mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_addopret(bettxid,mypk,amount)); - return(prices_rawtxresult(result,rawtx,0)); + mtx.vin.push_back(CTxIn(batontxid, 0, CScript())); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, mypk)); // vout0 baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode, amount, pricespk)); // vout1 added amount + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_addopret(bettxid, mypk, amount)); + return(prices_rawtxresult(result, rawtx, 0)); } else { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt find batonttxid")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "couldnt find batonttxid")); return(result); } } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); return(result); } -UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) -{ - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = PRICES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) +// scan chain from the initial bet's first position upto the chain tip and calculate bet's costbasises and profits, breaks if rekt detected +int32_t prices_scanchain(std::vector &bets, int16_t leverage, std::vector vec, int64_t &lastprice, int32_t &endheight) { + + if (bets.size() == 0) + return -1; + + bool stop = false; + for (int32_t height = bets[0].firstheight; ; height++) // the last datum for 24h is the costbasis value { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) - { - addedbets = prices_batontxid(batontxid,bettx,bettxid); - mtx.vin.push_back(CTxIn(bettxid,1,CScript())); - for (i=0; i bets[i].firstheight) { + + int32_t retcode = prices_syntheticprofits(bets[i].costbasis, bets[i].firstheight, height, leverage, vec, bets[i].positionsize, bets[i].profits, lastprice); + if (retcode < 0) { + std::cerr << "prices_scanchain() prices_syntheticprofits returned -1, breaking" << std::endl; + stop = true; break; } + totalbets += bets[i].positionsize; + totalprofits += bets[i].profits; } - if ( i == PRICES_DAYWINDOW ) - result.push_back(Pair("rekt",0)); - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - myfee = bettx.vout[1].nValue / 10; - result.push_back(Pair("myfee",myfee)); - mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[1].nValue-myfee-txfee,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_costbasisopret(bettxid,mypk,firstheight+PRICES_DAYWINDOW-1,costbasis)); - return(prices_rawtxresult(result,rawtx,0)); + } + + if (stop) + break; + + endheight = height; + int64_t equity = totalbets + totalprofits; + if (equity < 0) + { // we are in loss + break; } } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); + + return 0; +} + +// pricescostbasis rpc impl: set cost basis (open price) for the bet (deprecated) +UniValue PricesSetcostbasis(int64_t txfee, uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); + UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; CTransaction bettx; uint256 hashBlock, batontxid, tokenid; + int64_t myfee, positionsize = 0, addedbets, firstprice = 0, lastprice, profits = 0, costbasis = 0, equity; + int32_t i, firstheight = 0, height, numvouts; int16_t leverage = 0; + std::vector vec; + CPubKey pk, mypk, pricespk; std::string rawtx; +/* + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) + txfee = PRICES_TXFEE; + + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp, 0); + if (myGetTransaction(bettxid, bettx, hashBlock) != 0 && (numvouts = bettx.vout.size()) > 3) + { + if (prices_betopretdecode(bettx.vout[numvouts - 1].scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) == 'B') + { + if (nextheight <= firstheight + PRICES_DAYWINDOW + 1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cannot calculate costbasis yet")); + return(result); + } + + addedbets = prices_enumaddedbets(batontxid, bettx, bettxid); + mtx.vin.push_back(CTxIn(bettxid, 1, CScript())); // spend vin1 with betamount + //for (i = 0; i < PRICES_DAYWINDOW + 1; i++) // the last datum for 24h is the actual costbasis value + //{ + int32_t retcode = prices_syntheticprofits(costbasis, firstheight, firstheight + PRICES_DAYWINDOW, leverage, vec, positionsize, addedbets, profits, lastprice); + if (retcode < 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cannot calculate costbasis error getting price")); + return(result); + } + equity = positionsize + addedbets + profits; + //if (equity < 0) + //{ // we are in loss + // result.push_back(Pair("rekt", (int64_t)1)); + // result.push_back(Pair("rektheight", (int64_t)firstheight + i)); + // break; + //} + //} + //if (i == PRICES_DAYWINDOW + 1) + // result.push_back(Pair("rekt", 0)); + + prices_betjson(result, profits, costbasis, positionsize, addedbets, leverage, firstheight, firstprice, lastprice, equity); + + if (AddNormalinputs(mtx, mypk, txfee, 4) >= txfee) + { + myfee = bettx.vout[1].nValue / 10; // fee for setting costbasis + result.push_back(Pair("myfee", myfee)); + + mtx.vout.push_back(CTxOut(myfee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, bettx.vout[1].nValue - myfee, pricespk)); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_costbasisopret(bettxid, mypk, firstheight + PRICES_DAYWINDOW , costbasis)); // -1 + return(prices_rawtxresult(result, rawtx, 0)); + } + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); + return(result); + } + } */ + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "deprecated")); return(result); } -UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) + +int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) { - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid,batontxid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = PRICES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + + if (myGetTransaction(bettxid, bettx, hashBlock) && bettx.vout.size() > 3) { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + if (hashBlock.IsNull()) + return -2; + + OneBetData bet1; + if (prices_betopretdecode(bettx.vout.back().scriptPubKey, betinfo.pk, bet1.firstheight, bet1.positionsize, betinfo.leverage, betinfo.firstprice, betinfo.parsed, betinfo.tokenid) == 'B') { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,rektheight,leverage,vec,positionsize,addedbets)) < 0 ) - { - myfee = (positionsize + addedbets) / 500; + uint256 finaltxid; + int32_t vini; + int32_t finaltxheight; //, endheight; + //std::vector bets; + + + if (CCgetspenttxid(finaltxid, vini, finaltxheight, bettxid, NVOUT_CCMARKER) == 0) + betinfo.isOpen = false; + else + betinfo.isOpen = true; + + //bet1.amount = betinfo.positionsize; + //bet1.firstheight = firstheight; + betinfo.bets.push_back(bet1); + + prices_enumaddedbets(batontxid, betinfo.bets, bettxid); + + if (prices_scanchain(betinfo.bets, betinfo.leverage, betinfo.parsed, betinfo.lastprice, betinfo.lastheight) < 0) { + return -4; } - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - if ( myfee != 0 ) - { - mtx.vin.push_back(CTxIn(bettxid,2,CScript())); - mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[2].nValue-myfee-txfee,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,rektheight,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); - return(prices_rawtxresult(result,rawtx,0)); + + mpz_t mpzTotalbets; + mpz_t mpzTotalprofits; + mpz_t mpzTotalcostbasis; + + mpz_init(mpzTotalbets); + mpz_init(mpzTotalprofits); + mpz_init(mpzTotalcostbasis); + + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + mpz_t mpzProduct; + mpz_t mpzProfits; + + mpz_init(mpzProduct); + mpz_init(mpzProfits); + + //totalprofits += b.profits; + //dcostbasis += b.amount * (double)b.costbasis; + // costbasis += b.amount * (b.costbasis / PRICES_POINTFACTOR); // prevent int64 overflow (but we have underflow for 1/BTC) + // std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + //std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + mpz_set_ui(mpzProduct, b.costbasis); + mpz_mul_ui(mpzProduct, mpzProduct, (uint64_t)b.positionsize); // b.costbasis * b.amount + mpz_add(mpzTotalcostbasis, mpzTotalcostbasis, mpzProduct); //averageCostbasis += b.costbasis * b.amount; + + mpz_add_ui(mpzTotalbets, mpzTotalbets, (uint64_t)b.positionsize); //totalbets += b.amount; + mpz_add(mpzTotalprofits, mpzTotalprofits, mpzProfits); //totalprofits += b.profits; + + totalbets += b.positionsize; + totalprofits += b.profits; + + mpz_clear(mpzProduct); + mpz_clear(mpzProfits); } + + betinfo.equity = totalbets + totalprofits; + //int64_t averageCostbasis = 0; + + if (mpz_get_ui(mpzTotalbets) != 0) { //prevent zero div + mpz_t mpzAverageCostbasis; + mpz_init(mpzAverageCostbasis); + + //averageCostbasis = totalcostbasis / totalbets; + mpz_mul_ui(mpzTotalcostbasis, mpzTotalcostbasis, SATOSHIDEN); // profits *= SATOSHIDEN normalization to prevent loss of significance while division + mpz_tdiv_q(mpzAverageCostbasis, mpzTotalcostbasis, mpzTotalbets); + + mpz_tdiv_q_ui(mpzAverageCostbasis, mpzAverageCostbasis, SATOSHIDEN); // profits /= SATOSHIDEN de-normalization + + betinfo.averageCostbasis = mpz_get_ui(mpzAverageCostbasis); + mpz_clear(mpzAverageCostbasis); + } + + betinfo.liquidationprice = 0; + if (betinfo.leverage != 0) {// prevent zero div + betinfo.liquidationprice = betinfo.averageCostbasis - betinfo.averageCostbasis / betinfo.leverage; + } + + if (betinfo.equity >= 0) + betinfo.isRekt = false; else { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","position not rekt")); - return(result); + betinfo.isRekt = true; + betinfo.rektfee = totalbets / 500; } + + mpz_clear(mpzTotalbets); + mpz_clear(mpzTotalprofits); + mpz_clear(mpzTotalcostbasis); + return 0; } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant decode opret")); - return(result); - } + return -3; } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); - return(result); + return (-1); } -UniValue PricesCashout(uint64_t txfee,uint256 bettxid) +// pricesrekt rpc: anyone can rekt a bet at some block where losses reached limit, collecting fee +UniValue PricesRekt(int64_t txfee, uint256 bettxid, int32_t rektheight) { int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,inputsum,ignore,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CTransaction bettx; +/* uint256 hashBlock, tokenid, batontxid; + int64_t firstprice, lastprice = 0, positionsize; + int32_t firstheight; + int16_t leverage; + std::vector vec; */ + int64_t myfee = 0; + CPubKey pk, mypk, pricespk; + std::string rawtx; + char destaddr[64]; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) // TODO: what did we want tot do with txfee in prices? txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,destaddr,pricespk); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) - { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) - { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,nextheight-1,leverage,vec,positionsize,addedbets)) < 0 ) - { - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - result.push_back(Pair("result","error")); - result.push_back(Pair("error","position rekt")); - return(result); - } - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - mtx.vin.push_back(CTxIn(bettxid,2,CScript())); - if ( (inputsum= AddPricesInputs(cp,mtx,destaddr,profits+txfee,64,bettxid,2)) > profits+txfee ) - CCchange = (inputsum - profits); - mtx.vout.push_back(CTxOut(bettx.vout[2].nValue + profits,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - if ( CCchange >= txfee ) - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,nextheight-1,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); - return(prices_rawtxresult(result,rawtx,0)); + pricespk = GetUnspendable(cp, 0); + GetCCaddress(cp, destaddr, pricespk); + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); } - else + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant decode opret")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); return(result); } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + return(result); + } + + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + totalbets += b.positionsize; + totalprofits += b.profits; + } + + + if (!betinfo.isOpen) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position closed")); + return result; + } + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); // fill output + if (betinfo.isRekt) + { + myfee = betinfo.rektfee; // consolation fee for loss + } + if (myfee != 0) + { + int64_t CCchange = 0, inputsum; + + mtx.vin.push_back(CTxIn(bettxid, NVOUT_CCMARKER, CScript())); // spend cc marker + if ((inputsum = AddPricesInputs(cp, mtx, destaddr, myfee + txfee, 64)) > myfee + txfee) // TODO: why do we take txfee from global addr and not from user's addr? + CCchange = (inputsum - myfee); + mtx.vout.push_back(CTxOut(myfee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if (CCchange >= txfee) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CCchange, pricespk)); + + /// mtx.vout.push_back(MakeCC1vout(cp->evalcode, bettx.vout[2].nValue - myfee - txfee, pricespk)); // change + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_finalopret(bettxid, totalprofits, rektheight, mypk, betinfo.firstprice, 0, totalbets /*- positionsize*/, 0/*positionsize*/, betinfo.leverage)); + return(prices_rawtxresult(result, rawtx, 0)); + } + else + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position not rekt")); + return(result); } - return(result); } -UniValue PricesInfo(uint256 bettxid,int32_t refheight) +// pricescashout rpc impl: bettor can cashout hit bet if it is not rekt +UniValue PricesCashout(int64_t txfee, uint256 bettxid) { - UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) - { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); + UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; char destaddr[64]; +/* CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + int64_t positionsize, firstprice, lastprice = 0; + int32_t firstheight; + int16_t leverage; + std::vector vec;*/ + int64_t CCchange = 0, inputsum; + CPubKey pk, mypk, pricespk; + std::string rawtx; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) + txfee = PRICES_TXFEE; + + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp, 0); + GetCCaddress(cp, destaddr, pricespk); + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,refheight,leverage,vec,positionsize,addedbets)) < 0 ) - { - result.push_back(Pair("rekt",1)); - result.push_back(Pair("rektfee",(positionsize + addedbets) / 500)); - } else result.push_back(Pair("rekt",0)); - result.push_back(Pair("batontxid",batontxid.GetHex())); - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - result.push_back(Pair("height",(int64_t)refheight)); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); return(result); } - } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); - return(result); -} - -UniValue PricesList() -{ - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - SetCCtxids(addressIndex,cp->normaladdr,false); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - txid = it->first.txhash; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) - { - if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) - { - result.push_back(uint256_str(str,txid)); - } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); } + return(result); } + + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + totalbets += b.positionsize; + totalprofits += b.profits; + } + + + if (!betinfo.isOpen) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position closed")); + return result; + } + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); // fill output json + + if (betinfo.isRekt) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position rekt")); + return(result); + } + + mtx.vin.push_back(CTxIn(bettxid, NVOUT_CCMARKER, CScript())); // spend cc marker + if ((inputsum = AddPricesInputs(cp, mtx, destaddr, betinfo.equity + txfee, 64)) > betinfo.equity + txfee) + CCchange = (inputsum - betinfo.equity); + mtx.vout.push_back(CTxOut(betinfo.equity, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if (CCchange >= txfee) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CCchange, pricespk)); + // TODO: what should the opret param be: + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_finalopret(bettxid, totalprofits, nextheight - 1, mypk, betinfo.firstprice, 0, totalbets/*- betinfo.positionsize*/, 0/*betinfo.positionsize*/, betinfo.leverage)); + return(prices_rawtxresult(result, rawtx, 0)); + +} + + + + +// pricesinfo rpc impl +UniValue PricesInfo(uint256 bettxid, int32_t refheight) +{ + UniValue result(UniValue::VOBJ); +/* CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + int64_t positionsize = 0, firstprice = 0, lastprice = 0; + int32_t firstheight = 0, endheight; + int16_t leverage = 0; + std::vector vec; + CPubKey pk, mypk, pricespk; + std::string rawtx; */ + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if( retcode == -1 ) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + return(result); + } + + if (!betinfo.isRekt) + result.push_back(Pair("rekt", 0)); + else + { + result.push_back(Pair("rekt", (int64_t)1)); + result.push_back(Pair("rektfee", betinfo.rektfee)); + result.push_back(Pair("rektheight", betinfo.lastheight)); + } + + std::string expr = prices_getsourceexpression(betinfo.parsed); + result.push_back(Pair("expression", expr)); + result.push_back(Pair("reduced", prices_getreducedexpr(expr))); +// result.push_back(Pair("batontxid", batontxid.GetHex())); + result.push_back(Pair("costbasis", ValueFromAmount(betinfo.averageCostbasis))); +#ifdef TESTMODE + result.push_back(Pair("costbasis_test_period", 7)); +#endif + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); + + result.push_back(Pair("LiquidationPrice", ValueFromAmount(betinfo.liquidationprice))); + + return(result); +} + +// priceslist rpc impl +UniValue PricesList(uint32_t filter, CPubKey mypk) +{ + UniValue result(UniValue::VARR); + std::vector > addressIndex, addressIndexCC; + struct CCcontract_info *cp, C; + + cp = CCinit(&C, EVAL_PRICES); + //pricespk = GetUnspendable(cp, 0); + + // filters and outputs prices bet txid + auto AddBetToList = [&](uint256 txid) + { + int64_t amount, firstprice; + int32_t height; + int16_t leverage; + uint256 hashBlock, tokenid; + CPubKey pk, pricespk; + std::vector vec; + CTransaction vintx; + + if (GetTransaction(txid, vintx, hashBlock, false) != 0) + { + bool bAppend = false; + if (vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout.back().scriptPubKey, pk, height, amount, leverage, firstprice, vec, tokenid) == 'B' && + (mypk == CPubKey() || mypk == pk)) // if only mypubkey to list + { + if (filter == 0) + bAppend = true; + else { + int32_t vini; + int32_t height; + uint256 finaltxid; + + int32_t spent = CCgetspenttxid(finaltxid, vini, height, txid, NVOUT_CCMARKER); + if (filter == 1 && spent < 0 || // open positions + filter == 2 && spent == 0) // closed positions + bAppend = true; + } + if (bAppend) + result.push_back(txid.GetHex()); + } + std::cerr << "PricesList() " << " bettxid=" << txid.GetHex() << " mypk=" << HexStr(mypk) << " opretpk=" << HexStr(pk) << " filter=" << filter << " bAppend=" << bAppend << std::endl; + } + }; + + + SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) + { + if( it->first.txindex == NVOUT_NORMALMARKER ) + AddBetToList(it->first.txhash); + } + + /* for future when switch to cc marker only + SetCCtxids(addressIndexCC, cp->unspendableCCaddr, true); // cc marker + for (std::vector >::const_iterator it = addressIndexCC.begin(); it != addressIndexCC.end(); it++) + { + priceslist(it, 1); + } + */ return(result); } +void prices_addbookentry(uint256 txid) +{ + BetInfo betinfo; + //if( prices_getbetinfo(txid, betinfo) == 0 ) +} + +// walk through uxtos on the global address +// calculate the balance: +// + rekt positions +// = opposite positions +// - unbalanced positions +UniValue PricesGetOrderbook() +{ + UniValue result(UniValue::VARR); + std::vector > addressIndex, addressIndexCC; + struct CCcontract_info *cp, C; + + cp = CCinit(&C, EVAL_PRICES); + + SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) + { + if (it->first.txindex == NVOUT_NORMALMARKER) + prices_addbookentry(it->first.txhash); + } + return result; +} diff --git a/src/chain.h b/src/chain.h index d810ed4cb..b893e3a06 100644 --- a/src/chain.h +++ b/src/chain.h @@ -28,6 +28,7 @@ class CChainPower; #include "pow.h" #include "tinyformat.h" #include "uint256.h" +extern int8_t is_STAKED(const char *chain_name); #include @@ -36,6 +37,8 @@ class CChainPower; static const int SPROUT_VALUE_VERSION = 1001400; static const int SAPLING_VALUE_VERSION = 1010100; extern int32_t ASSETCHAINS_LWMAPOS; +extern char ASSETCHAINS_SYMBOL[65]; +extern uint64_t ASSETCHAINS_NOTARY_PAY[]; struct CDiskBlockPos { @@ -118,7 +121,7 @@ enum BlockStatus: uint32_t { BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade - BLOCK_IN_TMPFILE = 256 + BLOCK_IN_TMPFILE = 256 }; //! Short-hand for the highest consensus validity we implement. @@ -238,7 +241,7 @@ public: CBlockIndex* pskip; //! height of the entry in the chain. The genesis block has height 0 - int64_t newcoins,zfunds,sproutfunds; int8_t segid; // jl777 fields + int64_t newcoins,zfunds,sproutfunds,nNotaryPay; int8_t segid; // jl777 fields //! Which # file this block is stored in (blk?????.dat) int nFile; @@ -309,6 +312,7 @@ public: phashBlock = NULL; newcoins = zfunds = 0; segid = -2; + nNotaryPay = 0; pprev = NULL; pskip = NULL; nFile = 0; @@ -543,6 +547,11 @@ public: if ((s.GetType() & SER_DISK) && (nVersion >= SAPLING_VALUE_VERSION)) { READWRITE(nSaplingValue); } + if ( (s.GetType() & SER_DISK) && (is_STAKED(ASSETCHAINS_SYMBOL) != 0) && ASSETCHAINS_NOTARY_PAY[0] != 0 ) + { + READWRITE(nNotaryPay); + READWRITE(segid); + } } uint256 GetBlockHash() const diff --git a/src/init.cpp b/src/init.cpp index 4003f9ced..107e3e476 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -35,6 +35,7 @@ #include "httprpc.h" #include "key.h" #include "notarisationdb.h" + #ifdef ENABLE_MINING #include "key_io.h" #endif @@ -55,6 +56,7 @@ #ifdef ENABLE_WALLET #include "wallet/wallet.h" #include "wallet/walletdb.h" + #endif #include #include @@ -89,9 +91,11 @@ using namespace std; extern void ThreadSendAlert(); +extern bool komodo_dailysnapshot(int32_t height); extern int32_t KOMODO_LOADINGBLOCKS; extern bool VERUS_MINTBLOCKS; extern char ASSETCHAINS_SYMBOL[]; +extern int32_t KOMODO_SNAPSHOT_INTERVAL; ZCJoinSplit* pzcashParams = NULL; @@ -1622,6 +1626,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain"); break; } + + if ( ASSETCHAINS_CC != 0 && KOMODO_SNAPSHOT_INTERVAL != 0 && chainActive.Height() >= KOMODO_SNAPSHOT_INTERVAL ) + { + if ( !komodo_dailysnapshot(chainActive.Height()) ) + { + strLoadError = _("daily snapshot failed, please reindex your chain."); + break; + } + } if (!fReindex) { uiInterface.InitMessage(_("Rewinding blocks if needed...")); diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 80460b706..e616fb179 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -29,6 +29,7 @@ int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notar unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); bool EnsureWalletIsAvailable(bool avoidException); extern bool fRequestShutdown; +extern CScript KOMODO_EARLYTXID_SCRIPTPUB; int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); @@ -2045,9 +2046,77 @@ uint64_t komodo_checknotarypay(CBlock *pblock,int32_t height) return(0); } +bool komodo_appendACscriptpub() +{ + static bool didinit = false; + if ( didinit ) + return didinit; + if ( ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()] == 49 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()-1] == 51 ) + { + CTransaction tx; uint256 blockhash; + // get transaction and check that it occured before height 100. + if ( myGetTransaction(KOMODO_EARLYTXID,tx,blockhash) && mapBlockIndex[blockhash]->GetHeight() < 100 ) + { + for (int i = 0; i < tx.vout.size(); i++) + { + if ( tx.vout[i].scriptPubKey[0] == OP_RETURN ) + { + ASSETCHAINS_SCRIPTPUB.pop_back(); ASSETCHAINS_SCRIPTPUB.pop_back(); // remove last 2 chars. + // get OP_RETURN from txid and append the HexStr of it to scriptpub + ASSETCHAINS_SCRIPTPUB.append(HexStr(tx.vout[i].scriptPubKey.begin()+3, tx.vout[i].scriptPubKey.end())); + //fprintf(stderr, "ac_script.%s\n",ASSETCHAINS_SCRIPTPUB.c_str()); + didinit = true; + return true; + } + } + } + fprintf(stderr, "could not get KOMODO_EARLYTXID.%s OP_RETURN data. Restart with correct txid!\n", KOMODO_EARLYTXID.GetHex().c_str()); + StartShutdown(); + } + return false; +} + +void GetKomodoEarlytxidScriptPub() +{ + if ( KOMODO_EARLYTXID == zeroid ) + { + fprintf(stderr, "Restart deamon with -earlytxid.\n"); + StartShutdown(); + return; + } + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_SNAPSHOT_INTERVAL == 0 ) + { + fprintf(stderr, "Prices->paymentsCC contract must have -ac_snapshot enabled to pay out.\n"); + StartShutdown(); + return; + } + if ( chainActive.Height() < 100 ) + { + fprintf(stderr, "Cannot fetch -earlytxid before block 100.\n"); + StartShutdown(); + return; + } + CTransaction tx; uint256 blockhash; int32_t i; + // get transaction and check that it occured before height 100. + if ( myGetTransaction(KOMODO_EARLYTXID,tx,blockhash) && mapBlockIndex[blockhash]->GetHeight() < 100 ) + { + for (i = 0; i < tx.vout.size(); i++) + if ( tx.vout[i].scriptPubKey[0] == OP_RETURN ) + break; + if ( i < tx.vout.size() ) + { + KOMODO_EARLYTXID_SCRIPTPUB = CScript(tx.vout[i].scriptPubKey.begin()+3, tx.vout[i].scriptPubKey.end()); + fprintf(stderr, "KOMODO_EARLYTXID_SCRIPTPUB.%s\n", HexStr(KOMODO_EARLYTXID_SCRIPTPUB.begin(),KOMODO_EARLYTXID_SCRIPTPUB.end()).c_str()); + return; + } + } + fprintf(stderr, "INVALID -earlytxid, restart daemon with correct txid.\n"); + StartShutdown(); +} + int64_t komodo_checkcommission(CBlock *pblock,int32_t height) { - int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0; + int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0; static bool didinit = false; if ( ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 ) { checktoshis = komodo_commission(pblock,height); @@ -2069,6 +2138,12 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height) } if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { + static bool didinit = false; + if ( !didinit && height > 100 && KOMODO_EARLYTXID != zeroid && komodo_appendACscriptpub() ) + { + fprintf(stderr, "appended CC_op_return to ASSETCHAINS_SCRIPTPUB.%s\n", ASSETCHAINS_SCRIPTPUB.c_str()); + didinit = true; + } if ( ASSETCHAINS_SCRIPTPUB.size()/2 == scriptlen && scriptlen < sizeof(scripthex) ) { decode_hex(scripthex,scriptlen,(char *)ASSETCHAINS_SCRIPTPUB.c_str()); @@ -2205,11 +2280,11 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) else if ( ASSETCHAINS_STAKED != 0 ) failed = 0; } - if ( failed == 0 && ASSETCHAINS_COMMISSION != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 ) + if ( failed == 0 && ASSETCHAINS_COMMISSION != 0 ) { if ( height == 1 ) { - if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) + if ( ASSETCHAINS_SCRIPTPUB.size() > 1 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()] != 49 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()-1] != 51 ) { int32_t scriptlen; uint8_t scripthex[10000]; script = (uint8_t *)&pblock->vtx[0].vout[0].scriptPubKey[0]; @@ -2221,7 +2296,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) return(-1); } else return(-1); } - else + else if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 ) { script = (uint8_t *)&pblock->vtx[0].vout[0].scriptPubKey[0]; scriptlen = (int32_t)pblock->vtx[0].vout[0].scriptPubKey.size(); diff --git a/src/komodo_defs.h b/src/komodo_defs.h index ee44132bc..5efbe58fb 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -38,7 +38,12 @@ #define KOMODO_MAXNVALUE (((uint64_t)1 << 63) - 1) #define KOMODO_BIT63SET(x) ((x) & ((uint64_t)1 << 63)) #define KOMODO_VALUETOOBIG(x) ((x) > (uint64_t)10000000001*COIN) + +//#ifndef TESTMODE #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +//#else +//#define PRICES_DAYWINDOW (7) +//#endif extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); @@ -63,7 +68,7 @@ extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS extern std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; - +extern uint256 KOMODO_EARLYTXID; extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE; extern uint32_t ASSETCHAINS_CC; @@ -78,7 +83,8 @@ extern std::string DONATION_PUBKEY; extern uint8_t ASSETCHAINS_PRIVATE; extern int32_t USE_EXTERNAL_PUBKEY; extern char NOTARYADDRS[64][64]; -extern int32_t KOMODO_TESTNODE; +extern int32_t KOMODO_TESTNODE, KOMODO_SNAPSHOT_INTERVAL; +extern int32_t ASSETCHAINS_EARLYTXIDCONTRACT; int tx_height( const uint256 &hash ); extern std::vector vWhiteListAddress; void komodo_netevent(std::vector payload); @@ -101,7 +107,7 @@ int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); int8_t komodo_segid(int32_t nocache,int32_t height); int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); -int32_t komodo_priceind(char *symbol); +int32_t komodo_priceind(const char *symbol); int32_t komodo_pricesinit(); int64_t komodo_priceave(int64_t *tmpbuf,int64_t *correlated,int32_t cskip); int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t rawskip,uint32_t *nonzprices,int32_t smoothwidth); diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0ff1c0b66..1fafcc856 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2400,8 +2400,8 @@ char *komodo_pricename(char *name,int32_t ind) } return(0); } - -int32_t komodo_priceind(char *symbol) +// finds index for its symbol name +int32_t komodo_priceind(const char *symbol) { char name[65]; int32_t i,n = (int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET) / sizeof(uint32_t)); for (i=1; i 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 || (ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0) ) + if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 || (ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0) || KOMODO_SNAPSHOT_INTERVAL != 0 || ASSETCHAINS_EARLYTXIDCONTRACT != 0 ) { fprintf(stderr,"perc %.4f%% ac_pub=[%02x%02x%02x...] acsize.%d\n",dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0],ASSETCHAINS_OVERRIDE_PUBKEY33[1],ASSETCHAINS_OVERRIDE_PUBKEY33[2],(int32_t)ASSETCHAINS_SCRIPTPUB.size()); extraptr = extrabuf; @@ -2072,6 +2075,7 @@ void komodo_args(char *argv0) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS),(void *)&ASSETCHAINS_FOUNDERS); if ( ASSETCHAINS_FOUNDERS_REWARD != 0 ) { + fprintf(stderr, "set founders reward.%li\n",ASSETCHAINS_FOUNDERS_REWARD); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS_REWARD),(void *)&ASSETCHAINS_FOUNDERS_REWARD); } } @@ -2147,6 +2151,14 @@ void komodo_args(char *argv0) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_NK[0]),(void *)&ASSETCHAINS_NK[0]); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_NK[1]),(void *)&ASSETCHAINS_NK[1]); } + if ( KOMODO_SNAPSHOT_INTERVAL != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(KOMODO_SNAPSHOT_INTERVAL),(void *)&KOMODO_SNAPSHOT_INTERVAL); + } + if ( ASSETCHAINS_EARLYTXIDCONTRACT != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_EARLYTXIDCONTRACT),(void *)&ASSETCHAINS_EARLYTXIDCONTRACT); + } } addn = GetArg("-seednode",""); @@ -2214,7 +2226,10 @@ void komodo_args(char *argv0) sprintf(fname,"%s_7776",ASSETCHAINS_SYMBOL); if ( (fp= fopen(fname,"wb")) != 0 ) { - fprintf(fp,iguanafmtstr,name.c_str(),name.c_str(),name.c_str(),name.c_str(),magicstr,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,"78.47.196.146"); + int8_t notarypay = 0; + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) + notarypay = 1; + fprintf(fp,iguanafmtstr,name.c_str(),name.c_str(),name.c_str(),name.c_str(),magicstr,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,"78.47.196.146",notarypay); fclose(fp); //printf("created (%s)\n",fname); } else printf("error creating (%s)\n",fname); diff --git a/src/main.cpp b/src/main.cpp index d617f308a..1ec1d1560 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -642,6 +642,102 @@ UniValue komodo_snapshot(int top) return(result); } +bool komodo_snapshot2(std::map &addressAmounts) +{ + if ( fAddressIndex && pblocktree != 0 ) + { + return pblocktree->Snapshot2(addressAmounts, 0); + } + else return false; +} + +int32_t lastSnapShotHeight = 0; +std::vector > vAddressSnapshot; + +bool komodo_dailysnapshot(int32_t height) +{ + int reorglimit = 10; // CHANGE BACK TO 100 AFTER TESTING! + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height,undo_height,extraoffset; + if ( (extraoffset= height % KOMODO_SNAPSHOT_INTERVAL) != 0 ) + { + // we are on chain init, and need to scan all the way back to the correct height, other wise our node will have a diffrent snapshot to online nodes. + // use the notarizationsDB to scan back from the consesnus height to get the offset we need. + std::string symbol; Notarisation nota; + symbol.assign(ASSETCHAINS_SYMBOL); + if ( ScanNotarisationsDB(height-extraoffset, symbol, 100, nota) == 0 ) + undo_height = height-extraoffset-reorglimit; + else undo_height = nota.second.height; + //fprintf(stderr, "height.%i-extraoffset.%i = startscanfrom.%i to get undo_height.%i\n", height, extraoffset, height-extraoffset, undo_height); + } + else + { + // we are at the right height in connect block to scan back to last notarized height. + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + notarized_height > height-reorglimit ? undo_height = notarized_height : undo_height = height-reorglimit; + } + fprintf(stderr, "doing snapshot for height.%i undo_height.%i\n", height, undo_height); + // if we already did this height dont bother doing it again, this is just a reorg. The actual snapshot height cannot be reorged. + if ( undo_height == lastSnapShotHeight ) + return true; + std::map addressAmounts; + if ( !komodo_snapshot2(addressAmounts) ) + return false; + + // undo blocks in reverse order + for (int32_t n = height; n > undo_height; n--) + { + //fprintf(stderr, "undoing block.%i\n",n); + CBlockIndex *pindex; CBlock block; + if ( (pindex= komodo_chainactive(n)) == 0 || komodo_blockload(block, pindex) != 0 ) + return false; + // undo transactions in reverse order + for (int32_t i = block.vtx.size() - 1; i >= 0; i--) + { + const CTransaction &tx = block.vtx[i]; + CTxDestination vDest; + // loop vouts reverse order, remove value recieved. + for (unsigned int k = tx.vout.size(); k-- > 0;) + { + const CTxOut &out = tx.vout[k]; + if ( ExtractDestination(out.scriptPubKey, vDest) ) + { + addressAmounts[CBitcoinAddress(vDest).ToString()] -= out.nValue; + if ( addressAmounts[CBitcoinAddress(vDest).ToString()] < 1 ) + addressAmounts.erase(CBitcoinAddress(vDest).ToString()); + //fprintf(stderr, "VOUT: address.%s remove_coins.%li\n",CBitcoinAddress(vDest).ToString().c_str(), out.nValue); + } + } + // loop vins in reverse order, get prevout and return the sent balance. + for (unsigned int j = tx.vin.size(); j-- > 0;) + { + uint256 blockhash; CTransaction txin; + if ( !tx.IsCoinImport() && !tx.IsCoinBase() && myGetTransaction(tx.vin[j].prevout.hash,txin,blockhash) ) + { + int vout = tx.vin[j].prevout.n; + if ( ExtractDestination(txin.vout[vout].scriptPubKey, vDest) ) + { + //fprintf(stderr, "VIN: address.%s add_coins.%li\n",CBitcoinAddress(vDest).ToString().c_str(), txin.vout[vout].nValue); + addressAmounts[CBitcoinAddress(vDest).ToString()] += txin.vout[vout].nValue; + } + } + } + } + } + vAddressSnapshot.clear(); // clear existing snapshot + // convert address string to destination for easier conversion to what ever is required, eg, scriptPubKey. + for ( auto element : addressAmounts) + vAddressSnapshot.push_back(make_pair(element.second, DecodeDestination(element.first))); + // sort the vector by amount, highest at top. + std::sort(vAddressSnapshot.rbegin(), vAddressSnapshot.rend()); + //for (int j = 0; j < 50; j++) + // fprintf(stderr, "j.%i address.%s nValue.%li\n",j, CBitcoinAddress(vAddressSnapshot[j].second).ToString().c_str(), vAddressSnapshot[j].first ); + // include only top 5000 address. + if ( vAddressSnapshot.size() > 3999 ) vAddressSnapshot.resize(3999); + lastSnapShotHeight = undo_height; + fprintf(stderr, "vAddressSnapshot.size.%li\n", vAddressSnapshot.size()); + return true; +} + ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -3096,7 +3192,6 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); // undo spending activity addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, hash, j, true), prevout.nValue * -1)); - // restore unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); } @@ -3269,7 +3364,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); int32_t futureblock; - CAmount blockReward = 0; + CAmount blockReward = 0; uint64_t notarypaycheque = 0; // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in if (!CheckBlock(&futureblock,pindex->GetHeight(),pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck) || futureblock != 0 ) { @@ -3307,7 +3402,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): Notaries have not been paid!"), REJECT_INVALID, "bad-cb-amount"); // calculate the notaries compensation and validate the amounts and pubkeys are correct. - uint64_t notarypaycheque = komodo_checknotarypay((CBlock *)&block,(int32_t)pindex->GetHeight()); + notarypaycheque = komodo_checknotarypay((CBlock *)&block,(int32_t)pindex->GetHeight()); //fprintf(stderr, "notarypaycheque.%lu\n", notarypaycheque); if ( notarypaycheque > 0 ) blockReward += notarypaycheque; @@ -3497,8 +3592,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin fprintf(stderr,"valueout %.8f too big\n",(double)valueout/COIN); return state.DoS(100, error("ConnectBlock(): GetValueOut too big"),REJECT_INVALID,"tx valueout is too big"); } - prevsum = voutsum; - voutsum += valueout; + //prevsum = voutsum; + //voutsum += valueout; /*if ( KOMODO_VALUETOOBIG(voutsum) != 0 ) { fprintf(stderr,"voutsum %.8f too big\n",(double)voutsum/COIN); @@ -3714,6 +3809,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin //FlushStateToDisk(); komodo_connectblock(false,pindex,*(CBlock *)&block); // dPoW state update. + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) + { + // Update the notary pay with the latest payment. + pindex->nNotaryPay = pindex->pprev->nNotaryPay + notarypaycheque; + //fprintf(stderr, "total notary pay.%li\n", pindex->nNotaryPay); + } return true; } @@ -3923,6 +4024,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { DisconnectNotarisations(block); } pindexDelete->segid = -2; + pindexDelete->nNotaryPay = 0; pindexDelete->newcoins = 0; pindexDelete->zfunds = 0; @@ -4156,6 +4258,13 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * komodo_pricesupdate(pindexNew->GetHeight(),pblock); if ( ASSETCHAINS_SAPLING <= 0 && pindexNew->nTime > KOMODO_SAPLING_ACTIVATION - 24*3600 ) komodo_activate_sapling(pindexNew); + if ( ASSETCHAINS_CC != 0 && KOMODO_SNAPSHOT_INTERVAL != 0 && (pindexNew->GetHeight() % KOMODO_SNAPSHOT_INTERVAL) == 0 && pindexNew->GetHeight() >= KOMODO_SNAPSHOT_INTERVAL ) + { + uint64_t start = time(NULL); + if ( !komodo_dailysnapshot(pindexNew->GetHeight()) ) + fprintf(stderr, "daily snapshot failed, please reindex your chain\n"); // maybe force shutdown here? + fprintf(stderr, "snapshot completed in: %lu seconds\n", time(NULL)-start); + } return true; } @@ -6095,7 +6204,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->GetHeight(), nGoodTransactions); - + return true; } diff --git a/src/main.h b/src/main.h index ec36c976b..d507f9dd6 100644 --- a/src/main.h +++ b/src/main.h @@ -88,7 +88,7 @@ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default for -txexpirydelta, in number of blocks */ -static const unsigned int DEFAULT_TX_EXPIRY_DELTA = 20; +static const unsigned int DEFAULT_TX_EXPIRY_DELTA = 200; /** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int MAX_TEMPFILE_SIZE = 0x1000000; // 16 MiB 0x8000000 diff --git a/src/miner.cpp b/src/miner.cpp index e199acccd..8fc658fa9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -156,6 +156,7 @@ uint64_t komodo_notarypay(CMutableTransaction &txNew, std::vector &Notar int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len); CScript komodo_mineropret(int32_t nHeight); +bool komodo_appendACscriptpub(); CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake) { @@ -290,9 +291,9 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txvalue = tx.GetValueOut(); if ( KOMODO_VALUETOOBIG(txvalue) != 0 ) continue; - if ( KOMODO_VALUETOOBIG(txvalue + voutsum) != 0 ) - continue; - voutsum += txvalue; + //if ( KOMODO_VALUETOOBIG(txvalue + voutsum) != 0 ) // has been commented from main.cpp ? + // continue; + //voutsum += txvalue; if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 ) { //fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure nHeight.%d nTime.%u vs locktime.%u\n",nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime); @@ -652,6 +653,12 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txNew.vout[1].nValue = commission; if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { + static bool didinit = false; + if ( !didinit && nHeight > 100 && KOMODO_EARLYTXID != zeroid && komodo_appendACscriptpub() ) + { + fprintf(stderr, "appended ccopreturn to ASSETCHAINS_SCRIPTPUB.%s\n", ASSETCHAINS_SCRIPTPUB.c_str()); + didinit = true; + } //fprintf(stderr,"mine to -ac_script\n"); //txNew.vout[1].scriptPubKey = CScript() << ParseHex(); int32_t len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); @@ -721,7 +728,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } return(0); } - fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats); + //fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats); } else fprintf(stderr, "vout 2 of notarisation is not OP_RETURN scriptlen.%i\n", scriptlen); } if ( ASSETCHAINS_CBOPRET != 0 ) @@ -895,14 +902,14 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, int32_t gpucount, bool isStake) { CPubKey pubkey; CScript scriptPubKey; uint8_t *script,*ptr; int32_t i,len; - if ( nHeight == 1 && ASSETCHAINS_COMMISSION != 0 ) + if ( nHeight == 1 && ASSETCHAINS_COMMISSION != 0 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()] != 49 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()-1] != 51 ) { if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 ) { pubkey = ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY); scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG; } - else + else { len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); len >>= 1; diff --git a/src/notaries_staked.cpp b/src/notaries_staked.cpp index 0b7c97ceb..bb1518262 100644 --- a/src/notaries_staked.cpp +++ b/src/notaries_staked.cpp @@ -18,7 +18,7 @@ int8_t is_STAKED(const char *chain_name) if (doneinit == 1 && ASSETCHAINS_SYMBOL[0] != 0) return(STAKED); else STAKED = 0; - if ( (strcmp(chain_name, "LABS") == 0) || (strcmp(chain_name, "LABSTH") == 0) ) + if ( (strcmp(chain_name, "LABS") == 0) || (strcmp(chain_name, "LABSRCTEST") == 0) ) STAKED = 1; // These chains are allowed coin emissions. else if ( (strncmp(chain_name, "LABS", 4) == 0) ) STAKED = 2; // These chains have no coin emission, block subsidy is always 0, and comission is 0. Notary pay is allowed. diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 035d44662..cb2cfce92 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -43,6 +43,9 @@ #include +#include "cc/CCinclude.h" +#include "cc/CCPrices.h" + using namespace std; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); @@ -1155,17 +1158,17 @@ UniValue paxprice(const UniValue& params, bool fHelp) } return ret; } - +// fills pricedata with raw price, correlated and smoothed values for numblock /*int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) { int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,ignore,rngval; uint32_t rawprices[1440*6],*ptr; int64_t *tmpbuf; - width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; + width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; // need 2*PRICES_DAYWINDOW previous raw price points to calc PRICES_DAYWINDOW correlated points to calc, in turn, smoothed point komodo_heightpricebits(&seed,rawprices,firstheight + numblocks - 1); if ( firstheight < width ) return(-1); for (i=0; i vexpr; + SplitStr(sexpr, vexpr); + + // debug print parsed strings: + std::cerr << "parsed synthetic: "; + for (auto s : vexpr) + std::cerr << s << " "; + std::cerr << std::endl; + + return PricesBet(txfee, amount, leverage, vexpr); +} + +// pricesaddfunding rpc implementation +UniValue pricesaddfunding(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesaddfunding bettxid amount\n" + "where amount is in coins\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + CAmount txfee = 10000; + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + CAmount amount = atof(params[1].get_str().c_str()) * COIN; + if (amount <= 0) + throw runtime_error("invalid amount\n"); + + return PricesAddFunding(txfee, bettxid, amount); +} + +// rpc pricessetcostbasis implementation +UniValue pricessetcostbasis(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricessetcostbasis bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesSetcostbasis(txfee, bettxid); +} + +// pricescashout rpc implementation +UniValue pricescashout(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricescashout bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesCashout(txfee, bettxid); +} + +// pricesrekt rpc implementation +UniValue pricesrekt(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesrekt bettxid height\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int32_t height = atoi(params[0].get_str().c_str()); + + int64_t txfee = 10000; + + return PricesRekt(txfee, bettxid, height); +} + +// pricesrekt rpc implementation +UniValue pricesgetorderbook(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error("pricesgetorderbook\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + return PricesGetOrderbook(); +} + UniValue gettxout(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) @@ -1944,4 +2082,4 @@ void RegisterBlockchainRPCCommands(CRPCTable &tableRPC) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); -} +} \ No newline at end of file diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index 2ac53f788..35409323d 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -300,6 +300,9 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp) uint256 tokenid = zeroid; if( params.size() == 4 ) tokenid = Parseuint256(params[3].get_str().c_str()); + + if ( tokenid != zeroid && strcmp("LABS", targetSymbol.c_str())) + throw JSONRPCError(RPC_TYPE_ERROR, "There is no tokens support on LABS."); CPubKey myPubKey = Mypubkey(); struct CCcontract_info *cpTokens, C; @@ -312,8 +315,8 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp) if (tokenid.IsNull()) { // coins int64_t inputs; - if ((inputs = AddNormalinputs(mtx, myPubKey, burnAmount + txfee, 60)) == 0) { - throw runtime_error("Cannot find normal inputs\n"); + if ((inputs = AddNormalinputs(mtx, myPubKey, burnAmount + txfee, 10)) == 0) { + throw runtime_error("not enough funds, or need to merge utxos first\n"); } CTxDestination txdest = DecodeDestination(dest_addr_or_pubkey.c_str()); @@ -1145,36 +1148,43 @@ UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) //out.push_back(make_pair("blocktime",(int))); UniValue labs(UniValue::VARR); UniValue kmd(UniValue::VARR); - // Gets KMD notaries on KMD... but LABS notaries on labs chains needs to be fixed so LABS are identified on KMD. - int8_t numNN = 0; uint8_t notarypubkeys[64][33] = {0}; + int8_t numNN = 0, numSN = 0; uint8_t notarypubkeys[64][33] = {0}; uint8_t LABSpubkeys[64][33] = {0}; numNN = komodo_notaries(notarypubkeys, height, chainActive[height]->nTime); - + numSN = numStakedNotaries(LABSpubkeys,STAKED_era(chainActive[height]->nTime)); + BOOST_FOREACH(const Notarisation& n, nibs) { UniValue item(UniValue::VOBJ); UniValue notaryarr(UniValue::VARR); std::vector NotarisationNotaries; - if ( is_STAKED(n.second.symbol) != 0 ) - continue; // for now just skip this... need to fetch diff pubkeys for these chains. labs.push_back(item); uint256 hash; CTransaction tx; if ( GetTransaction(n.first,tx,hash,false) ) { - if ( !GetNotarisationNotaries(notarypubkeys, numNN, tx.vin, NotarisationNotaries) ) - continue; - if ( NotarisationNotaries.size() < numNN/5 ) - continue; + if ( is_STAKED(n.second.symbol) != 0 ) + { + if ( !GetNotarisationNotaries(LABSpubkeys, numSN, tx.vin, NotarisationNotaries) ) + continue; + } + else + { + if ( !GetNotarisationNotaries(notarypubkeys, numNN, tx.vin, NotarisationNotaries) ) + continue; + } } item.push_back(make_pair("txid", n.first.GetHex())); item.push_back(make_pair("chain", n.second.symbol)); item.push_back(make_pair("height", (int)n.second.height)); item.push_back(make_pair("blockhash", n.second.blockHash.GetHex())); - item.push_back(make_pair("KMD_height", height)); // for when timstamp input is used. + //item.push_back(make_pair("KMD_height", height)); // for when timstamp input is used. for ( auto notary : NotarisationNotaries ) notaryarr.push_back(notary); item.push_back(make_pair("notaries",notaryarr)); - kmd.push_back(item); + if ( is_STAKED(n.second.symbol) != 0 ) + labs.push_back(item); + else + kmd.push_back(item); } out.push_back(make_pair("KMD", kmd)); - //out.push_back(make_pair("LABS", labs)); + out.push_back(make_pair("LABS", labs)); return out; } @@ -1308,7 +1318,7 @@ UniValue getimports(const UniValue& params, bool fHelp) UniValue objTx(UniValue::VOBJ); objTx.push_back(Pair("txid",tx.GetHash().ToString())); ImportProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; - TotalImported += tx.vout[1].nValue; + TotalImported += tx.vout[0].nValue; // were vouts swapped? objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[1].nValue))); if (ExtractDestination(tx.vout[1].scriptPubKey, importaddress)) { @@ -1481,4 +1491,4 @@ UniValue getwalletburntransactions(const UniValue& params, bool fHelp) ret.push_backV(arrTmp); return ret; -} \ No newline at end of file +} diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 4e21ab9e9..4ea6e94e3 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -75,6 +75,8 @@ uint32_t komodo_segid32(char *coinaddr); 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); int8_t StakedNotaryID(std::string ¬aryname, char *Raddress); +uint64_t komodo_notarypayamount(int32_t nHeight, int64_t notarycount); +int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); #define KOMODO_VERSION "0.3.3b" #define VERUS_VERSION "0.4.0g" @@ -168,7 +170,7 @@ UniValue geterablockheights(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( - "getnotarysendmany\n" + "geterablockheights\n" "Returns a JSON object with the first block in each era.\n" ); @@ -1216,6 +1218,106 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) } } +CAmount checkburnaddress(CAmount &received, int64_t &nNotaryPay, int32_t &height, std::string sAddress) +{ + CBitcoinAddress address(sAddress); + uint160 hashBytes; int type = 0; CAmount balance = 0; + if (address.GetIndexKey(hashBytes, type, false)) + { + std::vector > addressIndex; + if (GetAddressIndex(hashBytes, type, addressIndex)) + { + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + if (it->second > 0) + received += it->second; + balance += it->second; + } + // Get notary pay from current chain tip + CBlockIndex* pindex = chainActive.LastTip(); + nNotaryPay = pindex->nNotaryPay; + height = pindex->GetHeight(); + } + } + return balance; +} + +UniValue checknotarization(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "checknotarization\n" + "\nReturns true if burn address balance is greater than total notary pay. (requires addressindex to be enabled).\n" + ); + + UniValue result(UniValue::VOBJ); CAmount balance = 0, received = 0; int64_t nNotaryPay = 0; int32_t height; + + // helper to test burn address's + /*uint8_t priv[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t pub[33] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + char coinaddr[64]; uint8_t buf33[33]; + //pubkey2addr(coinaddr, pub); + priv2addr(coinaddr,buf33,priv); + fprintf(stderr, "what.%s\n", coinaddr); + result.push_back(Pair("address", coinaddr)); + return result; + */ + + if ( ASSETCHAINS_NOTARY_PAY[0] == 0 ) + throw runtime_error("only works for ac_notarypay chains"); + // pubkey 020000000000000000000000000000000 + balance = checkburnaddress(received, nNotaryPay, height, "REDVp3ox1pbcWYCzySadfHhk8UU3HM4k5x"); + if ( nNotaryPay >= balance || received != balance ) + return false; + return true; +} + +UniValue getnotarypayinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getnotarypayinfo\n" + "\nReturns infomation about ac_notaypay status (requires addressindex to be enabled).\n" + "\nResult:\n" + "{\n" + " \"height\" (number) The current block height\n" + " \"balance\" (number) The current balance of the burn address\n" + " \"spent\" (bool) true if coins have been spent from the burn address\n" + " \"Total_NotaryPay\" (number) Total amount paid to notaries\n" + " \"Estimated_Notarizations_Left\" (number) the estimated amount of notarizations left before the balance is consumed\n" + " \"Estimated_Days_Left\" (number) the estimated amount of days the current balance will last\n" + " \"Estimated_Height\" (number) the estimated block height funds will run out\n" + "}\n" + ); + + if ( ASSETCHAINS_NOTARY_PAY[0] == 0 ) + throw runtime_error("only works for ac_notarypay chains"); + + UniValue result(UniValue::VOBJ); CAmount balance = 0, received = 0; int64_t TotalNotaryPay = 0, NotaryPay, notaleft = 0, daysleft = 0, notarycount; int32_t height, endheight = 0; uint8_t notarypubkeys[64][33] = {0}; + + // pubkey 020000000000000000000000000000000 + balance = checkburnaddress(received, TotalNotaryPay, height, "REDVp3ox1pbcWYCzySadfHhk8UU3HM4k5x"); + + notarycount = komodo_notaries(notarypubkeys, height, chainActive[height]->GetBlockTime()); + NotaryPay = komodo_notarypayamount(height, notarycount)*notarycount; + bool spent = (received != balance); + if ( !spent ) + { + notaleft = ((int64_t)balance - TotalNotaryPay) / NotaryPay; + daysleft = (((ASSETCHAINS_BLOCKTIME * 5) * notaleft) / 3600) / 24; + endheight = (notaleft * 5) + height; + } + + result.push_back(Pair("height", height)); + result.push_back(Pair("balance", ValueFromAmount(balance))); + result.push_back(Pair("spent", spent)); + result.push_back(Pair("Total_NotaryPay", ValueFromAmount(TotalNotaryPay))); + result.push_back(Pair("Estimated_Notarizations_Left", notaleft)); + result.push_back(Pair("Estimated_Days_Left", daysleft)); + result.push_back(Pair("Estimated_Height", endheight)); + return result; +} + UniValue getaddressbalance(const UniValue& params, bool fHelp) { if (fHelp ||params.size() > 2 || params.size() == 0) @@ -1281,8 +1383,9 @@ UniValue getsnapshot(const UniValue& params, bool fHelp) if (params.size() > 0 && !params[0].isNull()) { top = atoi(params[0].get_str().c_str()); - if (top <= 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, top must be a positive integer"); + if (top < 0) + //throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, top must be a positive integer"); + top = -1; } if ( fHelp || params.size() > 1) diff --git a/src/rpc/register.h b/src/rpc/register.h index 072517d88..245f76e22 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -35,6 +35,10 @@ void RegisterMiningRPCCommands(CRPCTable &tableRPC); /** Register raw transaction RPC commands */ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); +/** Register test transaction RPC commands */ +void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC); + + static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) { RegisterBlockchainRPCCommands(tableRPC); @@ -42,6 +46,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) RegisterMiscRPCCommands(tableRPC); RegisterMiningRPCCommands(tableRPC); RegisterRawTransactionRPCCommands(tableRPC); +#ifdef TESTMODE + RegisterTesttransactionsRPCCommands(tableRPC); +#endif } #endif diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 3ed082455..a41c7f413 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -463,7 +463,15 @@ static const CRPCCommand vRPCCommands[] = { "prices", "prices", &prices, true }, { "prices", "pricesaddress", &pricesaddress, true }, { "prices", "priceslist", &priceslist, true }, + { "prices", "mypriceslist", &mypriceslist, true }, { "prices", "pricesinfo", &pricesinfo, true }, + { "prices", "pricesbet", &pricesbet, true }, + { "prices", "pricessetcostbasis", &pricessetcostbasis, true }, + { "prices", "pricescashout", &pricescashout, true }, + { "prices", "pricesrekt", &pricesrekt, true }, + { "prices", "pricesaddfunding", &pricesaddfunding, true }, + { "prices", "pricesgetorderbook", &pricesgetorderbook, true }, + // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, @@ -480,12 +488,14 @@ static const CRPCCommand vRPCCommands[] = { "marmara", "marmaralock", &marmara_lock, true }, // Payments - { "payments", "paymentsaddress", &paymentsaddress, true }, - { "payments", "paymentstxidopret", &payments_txidopret, true }, - { "payments", "paymentscreate", &payments_create, true }, - { "payments", "paymentslist", &payments_list, true }, - { "payments", "paymentsinfo", &payments_info, true }, - { "payments", "paymentsfund", &payments_fund, true }, + { "payments", "paymentsaddress", &paymentsaddress, true }, + { "payments", "paymentstxidopret", &payments_txidopret, true }, + { "payments", "paymentscreate", &payments_create, true }, + { "payments", "paymentsairdrop", &payments_airdrop, true }, + { "payments", "paymentslist", &payments_list, true }, + { "payments", "paymentsinfo", &payments_info, true }, + { "payments", "paymentsfund", &payments_fund, true }, + { "payments", "paymentsmerge", &payments_merge, true }, { "payments", "paymentsrelease", &payments_release, true }, { "CClib", "cclibaddress", &cclibaddress, true }, @@ -542,6 +552,8 @@ static const CRPCCommand vRPCCommands[] = /* Address index */ { "addressindex", "getaddressmempool", &getaddressmempool, true }, { "addressindex", "getaddressutxos", &getaddressutxos, false }, + { "addressindex", "checknotarization", &checknotarization, false }, + { "addressindex", "getnotarypayinfo", &getnotarypayinfo, false }, { "addressindex", "getaddressdeltas", &getaddressdeltas, false }, { "addressindex", "getaddresstxids", &getaddresstxids, false }, { "addressindex", "getaddressbalance", &getaddressbalance, false }, @@ -565,10 +577,6 @@ static const CRPCCommand vRPCCommands[] = { "util", "reconsiderblock", &reconsiderblock, true }, /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, true }, - { "hidden", "test_ac", &test_ac, true }, - { "hidden", "test_heirmarker", &test_heirmarker, true }, - { "hidden", "test_proof", &test_proof, true }, - { "hidden", "test_burntx", &test_burntx, true }, #ifdef ENABLE_WALLET @@ -639,6 +647,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "z_importviewingkey", &z_importviewingkey, true }, { "wallet", "z_exportwallet", &z_exportwallet, true }, { "wallet", "z_importwallet", &z_importwallet, true }, + { "wallet", "opreturn_burn", &opreturn_burn, true }, // TODO: rearrange into another category { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 8e0054e6f..d0882d6cb 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -210,6 +210,8 @@ extern UniValue getaddresstxids(const UniValue& params, bool fHelp); extern UniValue getsnapshot(const UniValue& params, bool fHelp); extern UniValue getaddressbalance(const UniValue& params, bool fHelp); extern UniValue getpeerinfo(const UniValue& params, bool fHelp); +extern UniValue checknotarization(const UniValue& params, bool fHelp); +extern UniValue getnotarypayinfo(const UniValue& params, bool fHelp); extern UniValue ping(const UniValue& params, bool fHelp); extern UniValue addnode(const UniValue& params, bool fHelp); extern UniValue disconnectnode(const UniValue& params, bool fHelp); @@ -271,6 +273,7 @@ extern UniValue oraclesdata(const UniValue& params, bool fHelp); extern UniValue oraclessamples(const UniValue& params, bool fHelp); extern UniValue pricesaddress(const UniValue& params, bool fHelp); extern UniValue priceslist(const UniValue& params, bool fHelp); +extern UniValue mypriceslist(const UniValue& params, bool fHelp); extern UniValue pricesinfo(const UniValue& params, bool fHelp); extern UniValue pegsaddress(const UniValue& params, bool fHelp); extern UniValue marmaraaddress(const UniValue& params, bool fHelp); @@ -285,8 +288,10 @@ extern UniValue marmara_lock(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); extern UniValue payments_release(const UniValue& params, bool fHelp); extern UniValue payments_fund(const UniValue& params, bool fHelp); +extern UniValue payments_merge(const UniValue& params, bool fHelp); extern UniValue payments_txidopret(const UniValue& params, bool fHelp); extern UniValue payments_create(const UniValue& params, bool fHelp); +extern UniValue payments_airdrop(const UniValue& params, bool fHelp); extern UniValue payments_info(const UniValue& params, bool fHelp); extern UniValue payments_list(const UniValue& params, bool fHelp); @@ -466,6 +471,7 @@ extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp); // in rpcw extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_getoperationresult(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_listoperationids(const UniValue& params, bool fHelp); // in rpcwallet.cpp +extern UniValue opreturn_burn(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_validateaddress(const UniValue& params, bool fHelp); // in rpcmisc.cpp extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp); // in rpcdisclosure.cpp extern UniValue z_validatepaymentdisclosure(const UniValue ¶ms, bool fHelp); // in rpcdisclosure.cpp @@ -497,11 +503,13 @@ extern UniValue paxdeposit(const UniValue& params, bool fHelp); extern UniValue paxwithdraw(const UniValue& params, bool fHelp); extern UniValue prices(const UniValue& params, bool fHelp); +extern UniValue pricesbet(const UniValue& params, bool fHelp); +extern UniValue pricessetcostbasis(const UniValue& params, bool fHelp); +extern UniValue pricescashout(const UniValue& params, bool fHelp); +extern UniValue pricesrekt(const UniValue& params, bool fHelp); +extern UniValue pricesaddfunding(const UniValue& params, bool fHelp); +extern UniValue pricesgetorderbook(const UniValue& params, bool fHelp); + -// test rpc: -extern UniValue test_ac(const UniValue& params, bool fHelp); -extern UniValue test_heirmarker(const UniValue& params, bool fHelp); -extern UniValue test_burntx(const UniValue& params, bool fHelp); -extern UniValue test_proof(const UniValue& params, bool fHelp); #endif // BITCOIN_RPCSERVER_H diff --git a/src/rpc/testtransactions.cpp b/src/rpc/testtransactions.cpp new file mode 100644 index 000000000..ddf7c8895 --- /dev/null +++ b/src/rpc/testtransactions.cpp @@ -0,0 +1,268 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include + +#include "amount.h" +#include "chain.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "crosschain.h" +#include "base58.h" +#include "consensus/validation.h" +#include "cc/eval.h" +#include "main.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "streams.h" +#include "sync.h" +#include "util.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" + +#include + +#include + +#include + + +#include "cc/CCinclude.h" +#include "cc/CCPrices.h" + +using namespace std; + +int32_t ensure_CCrequirements(uint8_t evalcode); + +UniValue test_ac(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 4)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_HEIR) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + std::vector pubkey1; + std::vector pubkey2; + + pubkey1 = ParseHex(params[0].get_str().c_str()); + pubkey2 = ParseHex(params[1].get_str().c_str()); + + CPubKey pk1 = pubkey2pk(pubkey1); + CPubKey pk2 = pubkey2pk(pubkey2); + + if (!pk1.IsValid() || !pk2.IsValid()) + throw runtime_error("invalid pubkey\n"); + + int64_t txfee = 10000; + int64_t amount = atoll(params[2].get_str().c_str()) * COIN; + uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); + + if (normalInputs < txfee + amount) + throw runtime_error("not enough normals\n"); + + mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); +} + +UniValue test_heirmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_HEIR) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(fundingtxid, 1)); + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); +} + +UniValue test_burntx(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + + mtx.vin.push_back(CTxIn(tokenid, 0)); + mtx.vin.push_back(CTxIn(tokenid, 1)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); + + std::vector voutPubkeys; + voutPubkeys.push_back(burnpk); + + cp = CCinit(&C, EVAL_TOKENS); + + std::vector vopret; + GetNonfungibleData(tokenid, vopret); + if (vopret.size() > 0) + cp->additionalTokensEvalcode2 = vopret.begin()[0]; + + uint8_t tokenpriv[33]; + char unspendableTokenAddr[64]; + CPubKey unspPk = GetUnspendable(cp, tokenpriv); + GetCCaddress(cp, unspendableTokenAddr, unspPk); + CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); +} + +UniValue test_proof(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + std::vectorproof; + + if (fHelp || (params.size() != 2)) + throw runtime_error("incorrect params\n"); + + + proof = ParseHex(params[0].get_str()); + uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); + + std::vector txids; + + CMerkleBlock merkleBlock; + if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { + result.push_back(Pair("error", "could not unmarshal proof")); + return result; + } + uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); + + result.push_back(Pair("source_root", merkleRoot.GetHex())); + + for (int i = 0; i < txids.size(); i++) + std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; + + + std::vector vMatches(txids.size()); + for (auto v : vMatches) v = true; + CPartialMerkleTree verifTree(txids, vMatches); + + result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); + + if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { + fprintf(stderr, "invalid proof for this cointxid\n"); + } + + std::vector vMerkleTree; + bool f; + ::BuildMerkleTree(&f, txids, vMerkleTree); + + std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); + + uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); + result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); + + return result; +} + +extern CScript prices_costbasisopret(uint256 bettxid, CPubKey mypk, int32_t height, int64_t costbasis); +UniValue test_pricesmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_PRICES) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 bettxid = Parseuint256((char *)params[0].get_str().c_str()); + + cp = CCinit(&C, EVAL_PRICES); + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(bettxid, 1)); + mtx.vout.push_back(CTxOut(1000, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)); + + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, prices_costbasisopret(bettxid, myPubkey, 100, 100))); +} + + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + + /* Not shown in help */ + { "hidden", "test_ac", &test_ac, true }, + { "hidden", "test_heirmarker", &test_heirmarker, true }, + { "hidden", "test_proof", &test_proof, true }, + { "hidden", "test_burntx", &test_burntx, true }, + { "hidden", "test_pricesmarker", &test_pricesmarker, true } +}; + +void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +} \ No newline at end of file diff --git a/src/txdb.cpp b/src/txdb.cpp index 4c9ea31ca..82885044e 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -458,13 +458,13 @@ uint32_t komodo_segid32(char *coinaddr); {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} \ }; -int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector > &vaddr, UniValue *ret) +bool CBlockTreeDB::Snapshot2(std::map &addressAmounts, UniValue *ret) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; int64_t utxos = 0; int64_t ignoredAddresses = 0, cryptoConditionsUTXOs = 0, cryptoConditionsTotals = 0; DECLARE_IGNORELIST boost::scoped_ptr iter(NewIterator()); - std::map addressAmounts; + //std::map addressAmounts; for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { boost::this_thread::interruption_point(); @@ -481,45 +481,46 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector try { CAmount nValue; iter->GetValue(nValue); + if ( nValue == 0 ) + continue; getAddressFromIndex(indexKey.type, indexKey.hashBytes, address); if ( indexKey.type == 3 ) { cryptoConditionsUTXOs++; cryptoConditionsTotals += nValue; + total += nValue; continue; } - if ( nValue > dustthreshold ) + std::map ::iterator ignored = ignoredMap.find(address); + if (ignored != ignoredMap.end()) { - std::map ::iterator ignored = ignoredMap.find(address); - if (ignored != ignoredMap.end()) - { - fprintf(stderr,"ignoring %s\n", address.c_str()); - ignoredAddresses++; - continue; - } - std::map ::iterator pos = addressAmounts.find(address); - if ( pos == addressAmounts.end() ) - { - // insert new address + utxo amount - //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); - addressAmounts[address] = nValue; - totalAddresses++; - } - else - { - // update unspent tally for this address - //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); - addressAmounts[address] += nValue; - } - //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); - // total += nValue; - utxos++; - } //else fprintf(stderr,"ignoring amount=0 UTXO for %s\n", address.c_str()); + fprintf(stderr,"ignoring %s\n", address.c_str()); + ignoredAddresses++; + continue; + } + std::map ::iterator pos = addressAmounts.find(address); + if ( pos == addressAmounts.end() ) + { + // insert new address + utxo amount + //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); + addressAmounts[address] = nValue; + totalAddresses++; + } + else + { + // update unspent tally for this address + //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); + addressAmounts[address] += nValue; + } + //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); + // total += nValue; + utxos++; + total += nValue; } catch (const std::exception& e) { fprintf(stderr, "DONE %s: LevelDB addressindex exception! - %s\n", __func__, e.what()); - break; + return false; //break; this means failiure of DB? we need to exit here if so for consensus code! } } } @@ -530,30 +531,18 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector } } //fprintf(stderr, "total=%f, totalAddresses=%li, utxos=%li, ignored=%li\n", (double) total / COIN, totalAddresses, utxos, ignoredAddresses); - for (std::pair element : addressAmounts) - vaddr.push_back( make_pair(element.second, element.first) ); - std::sort(vaddr.rbegin(), vaddr.rend()); - int topN = 0; - for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) - { - total += it->first; - topN++; - // If requested, only show top N addresses in output JSON - if ( top == topN ) - break; - } + // this is for the snapshot RPC, you can skip this by passing a 0 as the last argument. if (ret) { - // Total amount in this snapshot, which is less than circulating supply if top parameter is used - // Use the address_total for a total of all address included when using top parameter. - ret->push_back(make_pair("total", (double) (total+cryptoConditionsTotals)/ COIN )); + // Total circulating supply without CC vouts. + ret->push_back(make_pair("total", (double) (total)/ COIN )); // Average amount in each address of this snapshot ret->push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); // Total number of utxos processed in this snaphot ret->push_back(make_pair("utxos", utxos)); // Total number of addresses in this snaphot - ret->push_back(make_pair("total_addresses", top ? top : totalAddresses )); + ret->push_back(make_pair("total_addresses", totalAddresses )); // Total number of ignored addresses in this snaphot ret->push_back(make_pair("ignored_addresses", ignoredAddresses)); // Total number of crypto condition utxos we skipped @@ -561,22 +550,39 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector // Total value of skipped crypto condition utxos ret->push_back(make_pair("cc_utxo_value", (double) cryptoConditionsTotals / COIN)); // total of all the address's, does not count coins in CC vouts. - ret->push_back(make_pair("address_total", (double) total/ COIN )); + ret->push_back(make_pair("total_includeCCvouts", (double) (total+cryptoConditionsTotals)/ COIN )); // The snapshot finished at this block height ret->push_back(make_pair("ending_height", chainActive.Height())); } - return(topN); + return true; } +extern std::vector > vAddressSnapshot; + UniValue CBlockTreeDB::Snapshot(int top) { int topN = 0; std::vector > vaddr; + //std::vector >> tokenids; + std::map addressAmounts; UniValue result(UniValue::VOBJ); UniValue addressesSorted(UniValue::VARR); result.push_back(Pair("start_time", (int) time(NULL))); - if ( Snapshot2(0,top,vaddr,&result) != 0 ) + if ( (vAddressSnapshot.size() > 0 && top < 0) || (Snapshot2(addressAmounts,&result) && top >= 0) ) { + if ( top > -1 ) + { + for (std::pair element : addressAmounts) + vaddr.push_back( make_pair(element.second, element.first) ); + std::sort(vaddr.rbegin(), vaddr.rend()); + } + else + { + for ( auto address : vAddressSnapshot ) + vaddr.push_back(make_pair(address.first, CBitcoinAddress(address.second).ToString())); + top = vAddressSnapshot.size(); + } + int topN = 0; for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) { UniValue obj(UniValue::VOBJ); @@ -712,6 +718,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nTx = diskindex.nTx; pindexNew->nSproutValue = diskindex.nSproutValue; pindexNew->nSaplingValue = diskindex.nSaplingValue; + pindexNew->segid = diskindex.segid; + pindexNew->nNotaryPay = diskindex.nNotaryPay; //fprintf(stderr,"loadguts ht.%d\n",pindexNew->GetHeight()); // Consistency checks auto header = pindexNew->GetBlockHeader(); diff --git a/src/txdb.h b/src/txdb.h index b4c4cd6bd..195f4c183 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -116,7 +116,7 @@ public: bool LoadBlockIndexGuts(); bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); - int32_t Snapshot2(int64_t dustthreshold, int32_t top,std::vector > &vaddr, UniValue *ret); + bool Snapshot2(std::map &addressAmounts, UniValue *ret); }; #endif // BITCOIN_TXDB_H diff --git a/src/util.cpp b/src/util.cpp index 980d82ac9..1aa079085 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -394,27 +394,23 @@ void ParseParameters(int argc, const char* const argv[]) } } +// split string using by space or comma as a delimiter char void SplitStr(const std::string& strVal, std::vector &outVals) { stringstream ss(strVal); - std::string str; - while ( ss.peek() == ' ' ) - ss.ignore(); - - while ( ss >> str ) - { - if ( str.size() == 0 ) - continue; - if ( str[str.size()-1] == ',' ) - str.resize(str.size()-1); - outVals.push_back(str); - while ( ss.peek() == ' ' ) - ss.ignore(); - if ( ss.peek() == ',' ) - ss.ignore(); - while ( ss.peek() == ' ' ) + while (!ss.eof()) { + int c; + std::string str; + + while (std::isspace(ss.peek())) ss.ignore(); + + while ((c = ss.get()) != EOF && !std::isspace(c) && c != ',') + str += c; + + if (!str.empty()) + outVals.push_back(str); } } diff --git a/src/util.h b/src/util.h index dc421092a..617faaf36 100644 --- a/src/util.h +++ b/src/util.h @@ -287,6 +287,9 @@ template void TraceThread(const char* name, Callable func) } } +// split string using by space or comma as a delimiter char +void SplitStr(const std::string& strVal, std::vector &outVals); + #define KOMODO_ASSETCHAIN_MAXLEN 65 diff --git a/src/wallet/db.h b/src/wallet/db.h index 19b9b6079..e1ae52909 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -33,7 +33,9 @@ #include +// If CCLIB fails to compile with this, use the one below. #include +//#include "../depends/x86_64-unknown-linux-gnu/include/db_cxx.h" extern unsigned int nWalletDBUpdated; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c830d37f9..0d364c43b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -59,6 +59,7 @@ #include #include "komodo_defs.h" +#include using namespace std; @@ -5603,6 +5604,19 @@ UniValue payments_fund(const UniValue& params, bool fHelp) return(PaymentsFund(cp,(char *)params[0].get_str().c_str())); } +UniValue payments_merge(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsmerge \"[%22createtxid%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsMerge(cp,(char *)params[0].get_str().c_str())); +} + UniValue payments_txidopret(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; @@ -5629,6 +5643,19 @@ UniValue payments_create(const UniValue& params, bool fHelp) return(PaymentsCreate(cp,(char *)params[0].get_str().c_str())); } +UniValue payments_airdrop(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsairdrop \"[lockedblocks,minamount,mintoaddress,top,bottom,fixedFlag,%22excludeAddress%22,...,%22excludeAddressN%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsAirdrop(cp,(char *)params[0].get_str().c_str())); +} + UniValue payments_info(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; @@ -6919,25 +6946,61 @@ UniValue faucetget(const UniValue& params, bool fHelp) return(result); } +uint32_t pricesGetParam(UniValue param) { + uint32_t filter = 0; + if (STR_TOLOWER(param.get_str()) == "all") + filter = 0; + else if (STR_TOLOWER(param.get_str()) == "open") + filter = 1; + else if (STR_TOLOWER(param.get_str()) == "closed") + filter = 2; + else + throw runtime_error("incorrect parameter\n"); + return filter; +} + UniValue priceslist(const UniValue& params, bool fHelp) { - if ( fHelp || params.size() > 0 ) - throw runtime_error("priceslist\n"); + if ( fHelp || params.size() != 0 && params.size() != 1) + throw runtime_error("priceslist [all|open|closed]\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - return(PricesList()); + uint32_t filter = 0; + if (params.size() == 1) + filter = pricesGetParam(params[0]); + + CPubKey emptypk; + + return(PricesList(filter, emptypk)); +} + +UniValue mypriceslist(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0 && params.size() != 1) + throw runtime_error("mypriceslist [all|open|closed]\n"); + if (ensure_CCrequirements(EVAL_PRICES) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint32_t filter = 0; + if (params.size() == 1) + filter = pricesGetParam(params[0]); + CPubKey mypk = pubkey2pk(Mypubkey()); + + return(PricesList(filter, mypk)); } UniValue pricesinfo(const UniValue& params, bool fHelp) { uint256 bettxid; int32_t height; - if ( fHelp || params.size() != 2 ) - throw runtime_error("pricesinfo fundingtxid\n"); + if ( fHelp || params.size() != 1 && params.size() != 2) + throw runtime_error("pricesinfo bettxid [height]\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); bettxid = Parseuint256((char *)params[0].get_str().c_str()); - height = atoi(params[1].get_str().c_str()); - return(PricesInfo(bettxid,height)); + height = 0; + if (params.size() == 2) + height = atoi(params[1].get_str().c_str()); + return(PricesInfo(bettxid, height)); } UniValue dicefund(const UniValue& params, bool fHelp) @@ -7939,170 +8002,34 @@ void RegisterWalletRPCCommands(CRPCTable &tableRPC) tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); } -UniValue test_ac(const UniValue& params, bool fHelp) +UniValue opreturn_burn(const UniValue& params, bool fHelp) { - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 4)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - std::vector pubkey1; - std::vector pubkey2; - - pubkey1 = ParseHex(params[0].get_str().c_str()); - pubkey2 = ParseHex(params[1].get_str().c_str()); - - CPubKey pk1 = pubkey2pk(pubkey1); - CPubKey pk2 = pubkey2pk(pubkey2); - - if(!pk1.IsValid() || !pk2.IsValid()) - throw runtime_error("invalid pubkey\n"); - - int64_t txfee = 10000; - int64_t amount = atoll(params[2].get_str().c_str()) * COIN; - uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); + std::vector vHexStr; CScript opret; int32_t txfee = 10000; + if (fHelp || (params.size() != 2)) + throw runtime_error("amount to burn, hexstring to send\n"); + struct CCcontract_info *cp, C; UniValue ret(UniValue::VOBJ); + if (ensure_CCrequirements(EVAL_PAYMENTS) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + cp = CCinit(&C, EVAL_PAYMENTS); + + CAmount nAmount = AmountFromValue(params[0]); + if (nAmount <= 10000) + throw JSONRPCError(RPC_TYPE_ERROR, "must burn at least 10000 sat"); + vHexStr = ParseHex(params[1].get_str()); + if ( vHexStr.size() == 0 ) + throw JSONRPCError(RPC_TYPE_ERROR, "hexstring is not valid."); CPubKey myPubkey = pubkey2pk(Mypubkey()); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); - - if( normalInputs < txfee + amount) + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, nAmount, 60); + if (normalInputs < nAmount) throw runtime_error("not enough normals\n"); - - mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); -} - -UniValue test_heirmarker(const UniValue& params, bool fHelp) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - mtx.vin.push_back(CTxIn(fundingtxid, 1)); - mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); -} - -UniValue test_burntx(const UniValue& params, bool fHelp) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); - - mtx.vin.push_back(CTxIn(tokenid, 0)); - mtx.vin.push_back(CTxIn(tokenid, 1)); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); - - std::vector voutPubkeys; - voutPubkeys.push_back(burnpk); - cp = CCinit(&C, EVAL_TOKENS); - - std::vector vopret; - GetNonfungibleData(tokenid, vopret); - if (vopret.size() > 0) - cp->additionalTokensEvalcode2 = vopret.begin()[0]; - - uint8_t tokenpriv[33]; - char unspendableTokenAddr[64]; - CPubKey unspPk = GetUnspendable(cp, tokenpriv); - GetCCaddress(cp, unspendableTokenAddr, unspPk); - CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); -} - -UniValue test_proof(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); - std::vectorproof; - - if (fHelp || (params.size() != 2)) - throw runtime_error("incorrect params\n"); - - - proof = ParseHex(params[0].get_str()); - uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); - - std::vector txids; - - CMerkleBlock merkleBlock; - if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { - result.push_back(Pair("error", "could not unmarshal proof")); - return result; - } - uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); - - result.push_back(Pair("source_root", merkleRoot.GetHex())); - - for (int i = 0; i < txids.size(); i++) - std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; - - - std::vector vMatches(txids.size()); - for (auto v : vMatches) v = true; - CPartialMerkleTree verifTree(txids, vMatches); - - result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); - - if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { - fprintf(stderr, "invalid proof for this cointxid\n"); - } - - std::vector vMerkleTree; - bool f; - ::BuildMerkleTree(&f, txids, vMerkleTree); - - std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); - - uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); - result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); - - return result; + opret << OP_RETURN << E_MARSHAL(ss << vHexStr); + + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(nAmount,opret)); + ret.push_back(Pair("hex",FinalizeCCTx(0, cp, mtx, myPubkey, txfee, CScript()))); + return(ret); } diff --git a/zcutil/build.sh b/zcutil/build.sh index df3dfa234..dc4d027b1 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -106,7 +106,7 @@ CONFIG_SITE="$PWD/depends/$HOST/share/config.site" ./configure "$HARDENING_ARG" WD=$PWD cd src/cc echo $PWD -./makerogue +./makecclib cd $WD "$MAKE" "$@" V=1