From 451773dcc13387036aac90daf52080dfc245395a Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Mon, 22 Apr 2019 00:00:01 +0800 Subject: [PATCH 1/8] initial commit for -earlytxid use with ac_script and ccvout_opret --- src/cc/CCutils.cpp | 4 +-- src/cc/payments.cpp | 62 ++++++++++++++++++++++++++++++++++------ src/komodo_bitcoind.h | 44 +++++++++++++++++++++++++--- src/komodo_defs.h | 2 +- src/komodo_utils.h | 1 + src/miner.cpp | 11 +++++-- src/rpc/server.cpp | 1 + src/rpc/server.h | 1 + src/wallet/rpcwallet.cpp | 36 ++++++++++++++++++++++- 9 files changed, 143 insertions(+), 19 deletions(-) diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 3c4c6c67c..1610e2426 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -76,11 +76,9 @@ CScript getCCopret(const CScript &scriptPubKey) CScript dummy; CScript opret; if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) ) { - //opret << E_MARSHAL(ss << vParams[0]); + //fprintf(stderr, "vparams.%s\n", HexStr(vParams[0].begin(), vParams[0].end()).c_str()); opret = CScript(vParams[0].begin()+6, vParams[0].end()); } - //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; } diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 526fea52f..7d6fcba32 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,11 +16,59 @@ #include "CCPayments.h" /* +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ac_script + -earlytxid instructions with payments cc + rewards CC as an example. +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 --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 +start chain and make sure to do the following steps before block 100 (set generate false/true is a good idea between steps) +create rewards plan and fund it with all or a % of the premine. Must be some amount. eg. + ./komodo-cli -ac_name=TEST rewardscreatefunding test 1000 10 0 10 10 + +do rewards add funding and get the script pubkey and op_return from this tx (no need to send it) eg. + scriptPubKey: 2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc + OP_RETURN: 6a2ae541746573740000000061e7063fa8f99ef92a47e4aebf7ea28c59aeadaf3c1784312de64e4bcb3666f1 + +create txidopreturn for this payment: + ./komodo-cli -ac_name=TEST paymentstxidopret '[50,"2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc","6a2ae541746573740000000061e7063fa8f99ef92a47e4aebf7ea28c59aeadaf3c1784312de64e4bcb3666f1"]' + +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=TEST paymentstxidopret '[50,"76a9146bf5dd9f679c87a3f83ea176f82148d26653c04388ac"]' + +create payments plan: + ./komodo-cli -ac_name=TEST paymentscreate '[0,0,"273d193e5d09928e471926827dcac1f06c4801bdaa5524a84b17a00f4eaf8d38","81264daf7874b2041802ac681e49618413313cc2f29b47d47bd8e63dc2a06cad"]' +gives plan txid eg. 5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682 + +paymentsfund: + send some of the premine to this payments fund to get the rest of the scriptpubkey payload. (could skip send and just gen/decode the tx if required.) + send opret path this time to get the required script pubkey. For payments this mode is enabled by default rather than a traditional OP_RETURN, + for other CC we would need to modify daemon to get the correct info. + ./komodo-cli -ac_name=TEST paymentsfund '["5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682",1000,1]' + +get the payment fund script pubkey: (the split it at OP_CHECKCRYPTOCONDITION or 'cc' ) + 2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401cc 2a0401f00101246a22f0466b75e35aa4d8ea6c3dd1b76141a0acbd06dfb4897288a62b8a8ec31b75a5b6cb75 + + put the second half into an OP_RETURN: (the remaining part of the the above scriptpubkey) eg. + ./komodo-cli -ac_name=TEST opreturn_burn 1 2a0401f00101246a22f0466b75e35aa4d8ea6c3dd1b76141a0acbd06dfb4897288a62b8a8ec31b75a5b6cb75 + 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 txid to locate it in the chain eg: + -earlytxid=1acd0b9b728feaea37a3f52d4106c35b0f8cfd19f9f3e64815d23ace0721d69d + restart the chain with earlytxid param before height 100 on BOTH NODES! + +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=TEST paymentsrelease '["5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682",500]' +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret @@ -643,13 +691,11 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) else { opret = EncodePaymentsFundOpRet(txid); - fprintf(stderr, "opret.%s\n", HexStr(opret.begin(), opret.end()).c_str()); 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()); - } + //fprintf(stderr, "scriptpubkey.%s\n", mtx.vout.back().scriptPubKey.ToString().c_str()); + //fprintf(stderr, "hex.%s\n", HexStr(mtx.vout.back().scriptPubKey.begin(), mtx.vout.back().scriptPubKey.end()).c_str()); } rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); if ( params != 0 ) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index bb5a093d3..3dd590417 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -2030,9 +2030,39 @@ 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; +} + 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); @@ -2054,6 +2084,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()); @@ -2190,11 +2226,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]; @@ -2206,7 +2242,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 ecaf7339a..ecb503be9 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -63,7 +63,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; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 88efb0264..cfb133a9d 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -2072,6 +2072,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); } } diff --git a/src/miner.cpp b/src/miner.cpp index e199acccd..a2ff8c933 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) { @@ -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()); @@ -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/rpc/server.cpp b/src/rpc/server.cpp index 3ed082455..79a782c35 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -639,6 +639,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..bcbe644a3 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -466,6 +466,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 diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 847205ec1..de446390e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -7979,9 +7979,11 @@ UniValue test_ac(const UniValue& params, bool fHelp) return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); } +extern bool komodo_appendACscriptpub(); + UniValue test_heirmarker(const UniValue& params, bool fHelp) { - // make fake token tx: + //make fake token tx: struct CCcontract_info *cp, C; if (fHelp || (params.size() != 1)) @@ -8010,6 +8012,38 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp) return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); } +UniValue opreturn_burn(const UniValue& params, bool fHelp) +{ + 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); + if (fHelp || (params.size() != 2)) + throw runtime_error("amount to burn, hexstring to send\n"); + + CAmount nAmount = AmountFromValue(params[0]); + if (nAmount <= 10000) + throw JSONRPCError(RPC_TYPE_ERROR, "must send at least 10000 sat"); + std::string strHex = params[1].get_str(); + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, nAmount, 60); + if (normalInputs < nAmount) + throw runtime_error("not enough normals\n"); + + CScript opret; uint8_t scripthex[8192]; + + decode_hex(scripthex,strHex.size()/2,(char *)strHex.c_str()); + std::string test; + test.append((char*)scripthex); + std::vector opretdata(test.begin(), test.end()); + opret << OP_RETURN << E_MARSHAL(ss << opretdata); + mtx.vout.push_back(CTxOut(nAmount,opret)); + ret.push_back(Pair("hex",FinalizeCCTx(0, cp, mtx, myPubkey, 10000, CScript()))); + return(ret); +} + UniValue test_burntx(const UniValue& params, bool fHelp) { // make fake token tx: From 489485252b68597bc9173328ed21674b58f90b8b Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Mon, 22 Apr 2019 18:02:19 +0800 Subject: [PATCH 2/8] fixes for TESTHC chain and add notes for others to make similar chains. --- src/cc/hempcoin_notes.txt | 65 +++++++++++++++++++++++++++++++ src/cc/payments.cpp | 80 +++++++-------------------------------- src/komodo_bitcoind.h | 3 +- src/wallet/rpcwallet.cpp | 20 +++++----- 4 files changed, 91 insertions(+), 77 deletions(-) create mode 100644 src/cc/hempcoin_notes.txt diff --git a/src/cc/hempcoin_notes.txt b/src/cc/hempcoin_notes.txt new file mode 100644 index 000000000..f8003ac95 --- /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 (using this RPC currently adds 3 extra bytes to the front (6a2d2c), which is truncated later on, this should be fixed if possible before making any real chains as its consensus code. Need to try diffrent methods to decode the hex correctly.) + -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/payments.cpp b/src/cc/payments.cpp index 7d6fcba32..ba915e586 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,60 +16,6 @@ #include "CCPayments.h" /* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -ac_script + -earlytxid instructions with payments cc + rewards CC as an example. -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 - -start chain and make sure to do the following steps before block 100 (set generate false/true is a good idea between steps) -create rewards plan and fund it with all or a % of the premine. Must be some amount. eg. - ./komodo-cli -ac_name=TEST rewardscreatefunding test 1000 10 0 10 10 - -do rewards add funding and get the script pubkey and op_return from this tx (no need to send it) eg. - scriptPubKey: 2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc - OP_RETURN: 6a2ae541746573740000000061e7063fa8f99ef92a47e4aebf7ea28c59aeadaf3c1784312de64e4bcb3666f1 - -create txidopreturn for this payment: - ./komodo-cli -ac_name=TEST paymentstxidopret '[50,"2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc","6a2ae541746573740000000061e7063fa8f99ef92a47e4aebf7ea28c59aeadaf3c1784312de64e4bcb3666f1"]' - -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=TEST paymentstxidopret '[50,"76a9146bf5dd9f679c87a3f83ea176f82148d26653c04388ac"]' - -create payments plan: - ./komodo-cli -ac_name=TEST paymentscreate '[0,0,"273d193e5d09928e471926827dcac1f06c4801bdaa5524a84b17a00f4eaf8d38","81264daf7874b2041802ac681e49618413313cc2f29b47d47bd8e63dc2a06cad"]' -gives plan txid eg. 5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682 - -paymentsfund: - send some of the premine to this payments fund to get the rest of the scriptpubkey payload. (could skip send and just gen/decode the tx if required.) - send opret path this time to get the required script pubkey. For payments this mode is enabled by default rather than a traditional OP_RETURN, - for other CC we would need to modify daemon to get the correct info. - ./komodo-cli -ac_name=TEST paymentsfund '["5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682",1000,1]' - -get the payment fund script pubkey: (the split it at OP_CHECKCRYPTOCONDITION or 'cc' ) - 2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401cc 2a0401f00101246a22f0466b75e35aa4d8ea6c3dd1b76141a0acbd06dfb4897288a62b8a8ec31b75a5b6cb75 - - put the second half into an OP_RETURN: (the remaining part of the the above scriptpubkey) eg. - ./komodo-cli -ac_name=TEST opreturn_burn 1 2a0401f00101246a22f0466b75e35aa4d8ea6c3dd1b76141a0acbd06dfb4897288a62b8a8ec31b75a5b6cb75 - 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 txid to locate it in the chain eg: - -earlytxid=1acd0b9b728feaea37a3f52d4106c35b0f8cfd19f9f3e64815d23ace0721d69d - restart the chain with earlytxid param before height 100 on BOTH NODES! - -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=TEST paymentsrelease '["5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682",500]' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret @@ -347,14 +293,14 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & CScript opret; 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); + // get op_return from CCvout, + opret = getCCopret(txin.vout[vin.prevout.n].scriptPubKey); } 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? + } 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()); @@ -381,7 +327,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t latestheight) { - 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; + 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; if ( maxinputs > CC_MAXVINS ) maxinputs = CC_MAXVINS; @@ -400,7 +346,7 @@ 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 ) { @@ -421,14 +367,14 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP if ( (opret_ind= has_opret(vintx, EVAL_PAYMENTS)) == 0 ) { // get op_return from CCvout - opret = getCCopret(vintx.vout[0].scriptPubKey); + opret = getCCopret(vintx.vout[vout].scriptPubKey); } 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; @@ -690,16 +636,18 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) } else { - opret = EncodePaymentsFundOpRet(txid); + mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk)); + // Use the below one along with other FinalizeCCTx/return, to get the ccvout scriptpubkey + /*opret = EncodePaymentsFundOpRet(txid); std::vector> vData = std::vector>(); if ( makeCCopret(opret, vData) ) - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData)); - //fprintf(stderr, "scriptpubkey.%s\n", mtx.vout.back().scriptPubKey.ToString().c_str()); - //fprintf(stderr, "hex.%s\n", HexStr(mtx.vout.back().scriptPubKey.begin(), mtx.vout.back().scriptPubKey.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,EncodePaymentsFundOpRet(txid)); + //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 diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 3dd590417..788f3795a 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -2047,7 +2047,8 @@ bool komodo_appendACscriptpub() { 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())); + // encoded opreturn incorrectly on TESTHC chain, once we no longer need this it can be changed to a straight +1 to drop OP_RETURN opcode. + ASSETCHAINS_SCRIPTPUB.append(HexStr(tx.vout[i].scriptPubKey.begin()+(strcmp("TESTHC",ASSETCHAINS_SYMBOL) == 0 ? 3 : 1), tx.vout[i].scriptPubKey.end())); //fprintf(stderr, "ac_script.%s\n",ASSETCHAINS_SCRIPTPUB.c_str()); didinit = true; return true; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index de446390e..8152e22b8 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8014,13 +8014,13 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp) UniValue opreturn_burn(const UniValue& params, bool fHelp) { + 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); - if (fHelp || (params.size() != 2)) - throw runtime_error("amount to burn, hexstring to send\n"); - + CAmount nAmount = AmountFromValue(params[0]); if (nAmount <= 10000) throw JSONRPCError(RPC_TYPE_ERROR, "must send at least 10000 sat"); @@ -8032,13 +8032,13 @@ UniValue opreturn_burn(const UniValue& params, bool fHelp) if (normalInputs < nAmount) throw runtime_error("not enough normals\n"); - CScript opret; uint8_t scripthex[8192]; - - decode_hex(scripthex,strHex.size()/2,(char *)strHex.c_str()); - std::string test; - test.append((char*)scripthex); - std::vector opretdata(test.begin(), test.end()); - opret << OP_RETURN << E_MARSHAL(ss << opretdata); + CScript opret; uint8_t *ptr; + opret << OP_RETURN; + int32_t len = strlen(strHex.c_str()); + len >>=1; + opret.resize(len+1); + ptr = (uint8_t *)&opret[1]; + decode_hex(ptr,len,(char *)strHex.c_str()); mtx.vout.push_back(CTxOut(nAmount,opret)); ret.push_back(Pair("hex",FinalizeCCTx(0, cp, mtx, myPubkey, 10000, CScript()))); return(ret); From ed6fd7e8c611aab49250a1aa95c03a056102342f Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sat, 27 Apr 2019 02:21:40 +0800 Subject: [PATCH 3/8] attempt at daily snapshot --- src/cc/CCPayments.h | 2 + src/cc/hempcoin_notes.txt | 2 +- src/cc/payments.cpp | 86 +++++++----------------------- src/komodo_defs.h | 1 + src/main.cpp | 107 +++++++++++++++++++++++++++++++++++++- src/rpc/server.cpp | 13 ++--- src/rpc/server.h | 1 + src/txdb.cpp | 89 +++++++++++++++---------------- src/txdb.h | 2 +- src/wallet/rpcwallet.cpp | 13 +++++ 10 files changed, 190 insertions(+), 126 deletions(-) diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 247a0f2ec..6eeb7bcb3 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -20,6 +20,7 @@ #include "CCinclude.h" #define PAYMENTS_TXFEE 10000 +bool komodo_snapshot2(std::map &addressAmounts); bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); @@ -28,6 +29,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsFund(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); diff --git a/src/cc/hempcoin_notes.txt b/src/cc/hempcoin_notes.txt index f8003ac95..2fd72c897 100644 --- a/src/cc/hempcoin_notes.txt +++ b/src/cc/hempcoin_notes.txt @@ -56,7 +56,7 @@ get the payment fund scriptpubkey hex from vout 0: (the split it at OP_CHECKCRYP 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 (using this RPC currently adds 3 extra bytes to the front (6a2d2c), which is truncated later on, this should be fixed if possible before making any real chains as its consensus code. Need to try diffrent methods to decode the hex correctly.) + 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 diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 7dc8cb1b6..6f6ae1a84 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,6 +16,18 @@ #include "CCPayments.h" /* +payments airdrop: + - extra RPC to merge all payments inputs to a single utxo, this must be called first and be confirmed before payments release, + or tx will be too big, we can check add payments inputs is only 1 input, at RPC and in validation very early on. + - do getsnapshot2 every 1440 blocks and save the result into some global sorted vector or DB? + -this allows any address balance to be calculated by only iterating each 1439 blocks maximum. + - calculate scriptpubkey to pay from each address, set allocations from balance of each address. allocation = balance? + +payments airdrop paying a token: ( maybe this is more reasonable speed wise? ) + - tokenid, top number of tokens to pay, list of pubkeys to exclude as optional param + - add vector of tokenids to getsnapshot2 - then sort each tokenid by balance. + put the pubkey to pay to as scriptpubkey in the saved vector. + 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret @@ -781,76 +793,14 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) return(result); } +extern bool komodo_dailysnapshot(int32_t height); + 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; - cJSON *params = payments_reparse(&n,jsonstr); - if ( params != 0 && n >= 4 ) - { - lockedblocks = juint(jitem(params,0),0); - minrelease = juint(jitem(params,1),0); - if ( lockedblocks < 0 || minrelease < 0 ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","negative parameter")); - if ( params != 0 ) - free_json(params); - return(result); - } - for (i=0; i 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' ) - { - totalallocations += allocation; - if ( opret.size() > 0 ) - numoprets++; - } - 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)); - 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")); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","parameters error")); - } - if ( params != 0 ) - free_json(params); - return(result); + uint64_t start = time(NULL); + komodo_dailysnapshot(chainActive.Height()); + //CScript scriptPubKey = GetScriptForDestination(dest); + return(time(NULL)-start); } UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 2efe5efa2..a9df99c0e 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -20,6 +20,7 @@ #define ASSETCHAINS_MINHEIGHT 128 #define ASSETCHAINS_MAX_ERAS 3 #define KOMODO_ELECTION_GAP 2000 +#define KOMODO_SNAPSHOT_INTERVAL 1440 // 1440 is approx 1 day. Maybe this can be -ac param to allow for diffrent block times etc.? #define ROUNDROBIN_DELAY 61 #define KOMODO_ASSETCHAIN_MAXLEN 65 #define KOMODO_LIMITED_NETWORKSIZE 4 diff --git a/src/main.cpp b/src/main.cpp index 2e0102b8b..f4511c284 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -642,6 +642,96 @@ 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) +{ + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height,undo_height; + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + if ( notarized_height > height-100 ) + { + // notarized height is higher than 100 blocks before this height, so snapshot the notarized height. + undo_height = notarized_height; + } + else + { + //snapshot 100 blocks ago. Could still be reorged but very unlikley and expensive to carry out constantly. + undo_height = height-100; + } + fprintf(stderr, "doing snapshot for height.%i lastSnapShotHeight.%i\n", undo_height, lastSnapShotHeight); + // 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]; + uint256 hash = tx.GetHash(); + CTxDestination vDest; + //fprintf(stderr, "undong tx.%s\n",hash.GetHex().c_str()); + // loop vouts reverse order + for (unsigned int k = tx.vout.size(); k-- > 0;) + { + const CTxOut &out = tx.vout[k]; + //fprintf(stderr, "scriptpubkey.%s\n",out.scriptPubKey.ToString().c_str() ); + if ( ExtractDestination(out.scriptPubKey, vDest) ) + { + // add outputs to destination + addressAmounts[CBitcoinAddress(vDest).ToString()] += out.nValue; + //fprintf(stderr, "address.%s addcoins.%li\n",CBitcoinAddress(vDest).ToString().c_str(), out.nValue); + } + } + // loop vins in reverse order, get prevout and remove the balance from its destination + for (unsigned int j = tx.vin.size(); j-- > 0;) + { + uint256 blockhash; CTransaction txin; + if ( !tx.IsCoinImport() && !tx.IsCoinBase() && GetTransaction(tx.vin[j].prevout.hash,txin,blockhash,false) ) // myGetTransaction! + { + int vout = tx.vin[j].prevout.n; + if ( ExtractDestination(txin.vout[vout].scriptPubKey, vDest) ) + { + // remove outputs from destination + //fprintf(stderr, "address.%s removecoins.%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()); + // include only top 5000 address. + if ( vAddressSnapshot.size() > 5000 ) vAddressSnapshot.resize(5000); + lastSnapShotHeight = undo_height; + fprintf(stderr, "vAddressSnapshot.size.%li\n", vAddressSnapshot.size()); + return true; +} + ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -3096,7 +3186,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))); } @@ -4156,6 +4245,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 && (pindexNew->GetHeight() % KOMODO_SNAPSHOT_INTERVAL) == 0 ) + { + 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 +6191,14 @@ 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); - + + if ( ASSETCHAINS_CC != 0 && lastSnapShotHeight == 0 ) + { + int32_t init_SS_height = chainActive.Height() - (chainActive.Height() % KOMODO_SNAPSHOT_INTERVAL); + if ( !komodo_dailysnapshot(init_SS_height) ) + fprintf(stderr, "daily snapshot failed, please reindex your chain\n"); // maybe force shutdown here? + } + return true; } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 79a782c35..7bd5d3419 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -480,12 +480,13 @@ 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", "paymentsrelease", &payments_release, true }, { "CClib", "cclibaddress", &cclibaddress, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index bcbe644a3..cf6e4c254 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -287,6 +287,7 @@ extern UniValue payments_release(const UniValue& params, bool fHelp); extern UniValue payments_fund(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); diff --git a/src/txdb.cpp b/src/txdb.cpp index 4c9ea31ca..a7415834c 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(); @@ -486,40 +486,39 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector { 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 +529,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 +548,28 @@ 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; } 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 ( Snapshot2(addressAmounts,&result) ) { + 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) { UniValue obj(UniValue::VOBJ); 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/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8de0ae773..66c534f11 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5629,6 +5629,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("paymentscreate \"[lockedblocks,minamount,%22paytxid0%22,...,%22paytxidN%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(0,(char *)params[0].get_str().c_str())); +} + UniValue payments_info(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; From 339e00f4d96d53d0fc821489a802f0fab8cea010 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sat, 27 Apr 2019 17:45:26 +0800 Subject: [PATCH 4/8] add dpow to BEER, fix crashed node not being able to sync due to future checkpoints. --- src/komodo.h | 2 +- src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo.h b/src/komodo.h index 15c1e5c02..b4c7d12bf 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -621,7 +621,7 @@ int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notar memset(&MoMoMdata,0,sizeof(MoMoMdata)); if ( matched == 0 && signedmask != 0 && bitweight(signedmask) >= KOMODO_MINRATIFY ) notarized = 1; - if ( strcmp("PIZZA",ccdata.symbol) == 0 || strncmp("TXSCL",ccdata.symbol,5) == 0 ) + if ( strcmp("PIZZA",ccdata.symbol) == 0 || strncmp("TXSCL",ccdata.symbol,5) == 0 || strcmp("BEER",ccdata.symbol) == 0) notarized = 1; if ( 0 && opretlen != 149 ) printf("[%s].%d (%s) matched.%d i.%d j.%d notarized.%d %llx opretlen.%d len.%d offset.%d opoffset.%d\n",ASSETCHAINS_SYMBOL,height,ccdata.symbol,matched,i,j,notarized,(long long)signedmask,opretlen,len,offset,opoffset); diff --git a/src/main.cpp b/src/main.cpp index 2e0102b8b..933a5bb15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4243,7 +4243,7 @@ static bool ActivateBestChainStep(bool fSkipdpow, CValidationState &state, CBloc // stay on the same chain tip! int32_t notarizedht,prevMoMheight; uint256 notarizedhash,txid; notarizedht = komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); - if ( !fSkipdpow && pindexFork != 0 && pindexFork->GetHeight() < notarizedht ) + if ( !fSkipdpow && pindexFork != 0 && pindexOldTip->GetHeight() > notarizedht && pindexFork->GetHeight() < notarizedht ) { fprintf(stderr,"pindexFork->GetHeight().%d is < notarizedht %d, so ignore it\n",(int32_t)pindexFork->GetHeight(),notarizedht); return state.DoS(100, error("ActivateBestChainStep(): pindexFork->GetHeight().%d is < notarizedht %d, so ignore it",(int32_t)pindexFork->GetHeight(),notarizedht), From 8b70bbf3070578ea5dd0d8f9c35ec38d6d2141fa Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sat, 27 Apr 2019 22:30:43 +0800 Subject: [PATCH 5/8] fix --- src/ac/koin | 2 + src/assetchains.json | 11 ++-- src/assetchains.old | 1 + src/cc/CCPayments.h | 2 +- src/cc/COptCCParams.cpp | 116 ++++++++++++++++++++++++++++++++++++++++ src/cc/payments.cpp | 14 +++-- src/fiat/koin | 2 + src/komodo.h | 2 +- src/main.cpp | 65 +++++++++++++--------- 9 files changed, 177 insertions(+), 38 deletions(-) create mode 100755 src/ac/koin create mode 100644 src/cc/COptCCParams.cpp create mode 100755 src/fiat/koin diff --git a/src/ac/koin b/src/ac/koin new file mode 100755 index 000000000..7e76c6b54 --- /dev/null +++ b/src/ac/koin @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=KOIN $1 $2 $3 $4 $5 $6 diff --git a/src/assetchains.json b/src/assetchains.json index 87173697b..99c6557b6 100644 --- a/src/assetchains.json +++ b/src/assetchains.json @@ -237,8 +237,8 @@ "217.182.129.38", "37.187.225.231" ] - }, - { + }, + { "ac_name": "ILN", "ac_supply": "10000000000", "ac_cc": "2", @@ -258,10 +258,15 @@ "ac_cc": "3", "addnode": ["138.201.136.145"] }, -{ + { "ac_name": "VOTE2019", "ac_supply": "123651638", "ac_public": "1", "addnode": ["95.213.238.98"] + }, + { + "ac_name": "KOIN", + "ac_supply": "125000000", + "addnode": ["3.0.32.10"] } ] diff --git a/src/assetchains.old b/src/assetchains.old index 8f0d763b5..a0cbd3b9c 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -50,3 +50,4 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=RICK -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -addnode=138.201.136.145 & ./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 & diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 6eeb7bcb3..3f5a10087 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -20,7 +20,7 @@ #include "CCinclude.h" #define PAYMENTS_TXFEE 10000 -bool komodo_snapshot2(std::map &addressAmounts); +extern std::vector > vAddressSnapshot; bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); 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/payments.cpp b/src/cc/payments.cpp index 6f6ae1a84..569c4eb14 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,17 +16,18 @@ #include "CCPayments.h" /* +use notarizations DB to scan back from the correct height, then undo ALL blocks back to this notarized height! payments airdrop: - extra RPC to merge all payments inputs to a single utxo, this must be called first and be confirmed before payments release, or tx will be too big, we can check add payments inputs is only 1 input, at RPC and in validation very early on. - - do getsnapshot2 every 1440 blocks and save the result into some global sorted vector or DB? + - do getsnapshot2 every 1440 blocks and save the result into some global sorted vector. -this allows any address balance to be calculated by only iterating each 1439 blocks maximum. - calculate scriptpubkey to pay from each address, set allocations from balance of each address. allocation = balance? -payments airdrop paying a token: ( maybe this is more reasonable speed wise? ) +payments airdrop paying a token: - tokenid, top number of tokens to pay, list of pubkeys to exclude as optional param - - add vector of tokenids to getsnapshot2 - then sort each tokenid by balance. - put the pubkey to pay to as scriptpubkey in the saved vector. + - token airdrop code should be the same as normal snapshot, but call a diffrent snapshot function that only fetches the info for a specific tokenid given. + this should be fine to work in validation/rpc level rather than a global saved result, as a single token id doesnt require iterating the whole DB. 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret @@ -793,13 +794,10 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) return(result); } -extern bool komodo_dailysnapshot(int32_t height); - UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) { uint64_t start = time(NULL); - komodo_dailysnapshot(chainActive.Height()); - //CScript scriptPubKey = GetScriptForDestination(dest); + return(time(NULL)-start); } diff --git a/src/fiat/koin b/src/fiat/koin new file mode 100755 index 000000000..7e76c6b54 --- /dev/null +++ b/src/fiat/koin @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=KOIN $1 $2 $3 $4 $5 $6 diff --git a/src/komodo.h b/src/komodo.h index 15c1e5c02..b4c7d12bf 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -621,7 +621,7 @@ int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notar memset(&MoMoMdata,0,sizeof(MoMoMdata)); if ( matched == 0 && signedmask != 0 && bitweight(signedmask) >= KOMODO_MINRATIFY ) notarized = 1; - if ( strcmp("PIZZA",ccdata.symbol) == 0 || strncmp("TXSCL",ccdata.symbol,5) == 0 ) + if ( strcmp("PIZZA",ccdata.symbol) == 0 || strncmp("TXSCL",ccdata.symbol,5) == 0 || strcmp("BEER",ccdata.symbol) == 0) notarized = 1; if ( 0 && opretlen != 149 ) printf("[%s].%d (%s) matched.%d i.%d j.%d notarized.%d %llx opretlen.%d len.%d offset.%d opoffset.%d\n",ASSETCHAINS_SYMBOL,height,ccdata.symbol,matched,i,j,notarized,(long long)signedmask,opretlen,len,offset,opoffset); diff --git a/src/main.cpp b/src/main.cpp index f4511c284..8bace2cb0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -656,19 +656,34 @@ std::vector > vAddressSnapshot; bool komodo_dailysnapshot(int32_t height) { - uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height,undo_height; - notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); - if ( notarized_height > height-100 ) + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height,undo_height,extraoffset; + if ( (extraoffset= height % KOMODO_SNAPSHOT_INTERVAL) != 0 ) { - // notarized height is higher than 100 blocks before this height, so snapshot the notarized height. - undo_height = notarized_height; + // 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); + ScanNotarisationsDB(height-extraoffset, symbol, 100, nota); + undo_height = nota.second.height; + if ( undo_height == 0 ) undo_height = height-extraoffset-100; + fprintf(stderr, "height.%i-extraoffset.%i = startscanfrom.%i to get undo_height.%i\n", height, extraoffset, height-extraoffset, undo_height); } - else + else { - //snapshot 100 blocks ago. Could still be reorged but very unlikley and expensive to carry out constantly. - undo_height = height-100; + // 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); + if ( notarized_height > height-100 ) + { + // notarized height is higher than 100 blocks before this height, so snapshot the notarized height. + undo_height = notarized_height; + } + else + { + //snapshot 100 blocks ago. Could still be reorged but very unlikley and expensive to carry out constantly. + undo_height = height-100; + } } - fprintf(stderr, "doing snapshot for height.%i lastSnapShotHeight.%i\n", undo_height, lastSnapShotHeight); + 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; @@ -690,30 +705,29 @@ bool komodo_dailysnapshot(int32_t height) uint256 hash = tx.GetHash(); CTxDestination vDest; //fprintf(stderr, "undong tx.%s\n",hash.GetHex().c_str()); - // loop vouts reverse order + // loop vouts reverse order, remove value recieved. for (unsigned int k = tx.vout.size(); k-- > 0;) { const CTxOut &out = tx.vout[k]; - //fprintf(stderr, "scriptpubkey.%s\n",out.scriptPubKey.ToString().c_str() ); if ( ExtractDestination(out.scriptPubKey, vDest) ) { - // add outputs to destination - addressAmounts[CBitcoinAddress(vDest).ToString()] += out.nValue; - //fprintf(stderr, "address.%s addcoins.%li\n",CBitcoinAddress(vDest).ToString().c_str(), out.nValue); + 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 remove the balance from its destination + // 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() && GetTransaction(tx.vin[j].prevout.hash,txin,blockhash,false) ) // myGetTransaction! + if ( !tx.IsCoinImport() && !tx.IsCoinBase() && myGetTransaction(tx.vin[j].prevout.hash,txin,blockhash) ) // myGetTransaction! { int vout = tx.vin[j].prevout.n; if ( ExtractDestination(txin.vout[vout].scriptPubKey, vDest) ) { - // remove outputs from destination - //fprintf(stderr, "address.%s removecoins.%li\n",CBitcoinAddress(vDest).ToString().c_str(), txin.vout[vout].nValue); - addressAmounts[CBitcoinAddress(vDest).ToString()] -= txin.vout[vout].nValue; + //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; } } } @@ -725,6 +739,8 @@ bool komodo_dailysnapshot(int32_t height) 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() > 5000 ) vAddressSnapshot.resize(5000); lastSnapShotHeight = undo_height; @@ -4339,10 +4355,10 @@ static bool ActivateBestChainStep(bool fSkipdpow, CValidationState &state, CBloc // stay on the same chain tip! int32_t notarizedht,prevMoMheight; uint256 notarizedhash,txid; notarizedht = komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); - if ( !fSkipdpow && pindexFork != 0 && pindexFork->GetHeight() < notarizedht ) + if ( !fSkipdpow && pindexFork != 0 && pindexOldTip->GetHeight() > notarizedht && pindexFork->GetHeight() < notarizedht ) { - fprintf(stderr,"pindexFork->GetHeight().%d is < notarizedht %d, so ignore it\n",(int32_t)pindexFork->GetHeight(),notarizedht); - return state.DoS(100, error("ActivateBestChainStep(): pindexFork->GetHeight().%d is < notarizedht %d, so ignore it",(int32_t)pindexFork->GetHeight(),notarizedht), + fprintf(stderr,"pindexOldTip->GetHeight().%d > notarizedht %d && pindexFork->GetHeight().%d is < notarizedht %d, so ignore it\n",(int32_t)pindexFork->GetHeight(),notarizedht,(int32_t)pindexOldTip->GetHeight(),notarizedht); + return state.DoS(100, error("ActivateBestChainStep(): pindexOldTip->GetHeight().%d > notarizedht %d && pindexFork->GetHeight().%d is < notarizedht %d, so ignore it",(int32_t)pindexFork->GetHeight(),notarizedht,(int32_t)pindexOldTip->GetHeight(),notarizedht), REJECT_INVALID, "past-notarized-height"); } // - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks. @@ -6192,10 +6208,9 @@ 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); - if ( ASSETCHAINS_CC != 0 && lastSnapShotHeight == 0 ) + if ( ASSETCHAINS_CC != 0 && chainActive.Height() > KOMODO_SNAPSHOT_INTERVAL ) { - int32_t init_SS_height = chainActive.Height() - (chainActive.Height() % KOMODO_SNAPSHOT_INTERVAL); - if ( !komodo_dailysnapshot(init_SS_height) ) + if ( !komodo_dailysnapshot(chainActive.Height()) ) fprintf(stderr, "daily snapshot failed, please reindex your chain\n"); // maybe force shutdown here? } From 2a0ae738743a250622b5d3f8266aa51b6878889c Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Mon, 29 Apr 2019 22:45:56 +0800 Subject: [PATCH 6/8] payments Airdrop. Working airdrop and syncs TESTHC chain. --- src/cc/CCPayments.h | 2 + src/cc/CCutils.cpp | 2 +- src/cc/eval.cpp | 9 + src/cc/payments.cpp | 541 +++++++++++++++++++++++++++++---------- src/main.cpp | 24 +- src/wallet/rpcwallet.cpp | 14 +- 6 files changed, 425 insertions(+), 167 deletions(-) diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 3f5a10087..cff304a2e 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -18,6 +18,7 @@ #define CC_PAYMENTS_H #include "CCinclude.h" +#include #define PAYMENTS_TXFEE 10000 extern std::vector > vAddressSnapshot; @@ -34,3 +35,4 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr); #endif + diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 65b390c41..acf0da766 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -74,7 +74,7 @@ CScript getCCopret(const CScript &scriptPubKey) { std::vector> vParams = std::vector>(); CScript dummy; CScript opret; - if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) ) + if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() == 1 ) { //fprintf(stderr, "vparams.%s\n", HexStr(vParams[0].begin(), vParams[0].end()).c_str()); opret = CScript(vParams[0].begin()+6, vParams[0].end()); 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/payments.cpp b/src/cc/payments.cpp index 569c4eb14..e72184719 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,6 +16,14 @@ #include "CCPayments.h" /* +192.168.0.139: RH side screen. + ./komodod -ac_name=TESTDP -ac_supply=10000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -addndoe=192.168.0.112 + - TESTDP.tar saved after distributing funds randomly. approx block 120. +LH screen: + ./komodod -ac_name=TESTDP -ac_supply=10000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -pubkey=0244a96824fa317433f0eaa6d5b1faf68e802b1958df273c24cb82bce1ef8e1aec -gen -genproclimit=1 + +./komodo-cli -ac_name=TESTDP paymentsairdrop '[10,2000,500,"76a9149758abb81ee168dd3824cb55e94df509b35462d788ac",76a9144cfd873dadbfbb4b9c03e77ecaa6cfb74a484f4888ac"]' + use notarizations DB to scan back from the correct height, then undo ALL blocks back to this notarized height! payments airdrop: - extra RPC to merge all payments inputs to a single utxo, this must be called first and be confirmed before payments release, @@ -144,12 +152,53 @@ 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); } + +CScript EncodePaymentsSnapsShotOpRet(int32_t lockedblocks,int32_t minrelease,int32_t top,std::vector> excludeScriptPubKeys) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << top << excludeScriptPubKeys); + return(opret); +} + +uint8_t DecodePaymentsSnapsShotOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &top,std::vector> &excludeScriptPubKeys) +{ + 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) != 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) { char destaddr[64]; @@ -184,8 +233,10 @@ 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; + char temp[128], coinaddr[64], txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash, tokenid; CTransaction plantx; uint8_t funcid = 0; int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; + int32_t top; std::vector> excludeScriptPubKeys; + 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")); @@ -200,13 +251,15 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //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,top,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 ) + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 ) return(eval->Invalid("negative values")); 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"); @@ -215,65 +268,101 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & std::vector scriptPubKeys; int64_t checkallocations = 0; i = 0; - BOOST_FOREACH(const uint256& txidopret, txidoprets) + if ( funcid == 'C' ) { - 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' ) + // normal payment + for (const uint256& txidopret : txidoprets) { - 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() ) + 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' ) { - if ( !fHasOpret ) + 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() ) { - 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")); + 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++; } - i++; + mpz_set_si(mpzTotalAllocations,totalallocations); + } + else if ( funcid == 'S' ) + { + // need time for TX to me mined before the next snapshot. + if ( top > 5000 ) + return(eval->Invalid("transaction too big")); + for ( auto address : vAddressSnapshot ) + { + CScript scriptPubKey = GetScriptForDestination(address.second); + for ( auto skipkey : excludeScriptPubKeys ) + { + //fprintf(stderr, "scriptpubkey.%s\n skipkey.%s", HexStr(scriptPubKey).c_str(), HexStr(CScript(skipkey.begin(), skipkey.end())).c_str()); + if ( scriptPubKey != CScript(skipkey.begin(), skipkey.end()) ) + { + 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 == 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 + //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 ( totalallocations != checkallocations ) + if ( funcid == 'C' && totalallocations != checkallocations ) // only check for normal payments release. 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 ) 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; + 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++) { - 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 ) + if ( scriptPubKeys[n] != tx.vout[i].scriptPubKey ) { - fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", destscriptPubKey.c_str(), voutscriptPubKey.c_str()); + 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 = allocations[n]; - test *= checkamount; - test /= totalallocations; - if ( test != tx.vout[i].nValue && test != tx.vout[i].nValue-1 ) + mpz_init(mpzAllocation); + mpz_set_si(mpzAllocation,allocations[n]); + mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); + mpz_cdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); + int64_t 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")); @@ -281,6 +370,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & 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")); @@ -291,6 +381,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & return(eval->Invalid("amount is too small")); } + // Check vins i = 0; int32_t ht = chainActive.LastTip()->GetHeight(); BOOST_FOREACH(const CTxIn& vin, tx.vin) @@ -303,17 +394,12 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); if ( strcmp(destaddr,coinaddr) != 0 ) { + // if does not come from address its in the global payments adddress and we need to check the opreturn. CScript opret; uint256 checktxid; int32_t opret_ind; if ( (opret_ind= has_opret(txin, EVAL_PAYMENTS)) == 0 ) - { - // get op_return from CCvout, - opret = getCCopret(txin.vout[vin.prevout.n].scriptPubKey); - } + opret = getCCopret(txin.vout[vin.prevout.n].scriptPubKey); // get op_return from CCvout, else - { - // get op_return from the op_return - opret = txin.vout[opret_ind].scriptPubKey; - } + 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()); @@ -404,6 +490,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; } //else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); + iter++; } } } @@ -482,8 +569,10 @@ 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; + 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; std::vector> excludeScriptPubKeys; int8_t funcid; + mpz_t mpzTotalAllocations; mpz_init(mpzTotalAllocations); cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -491,14 +580,16 @@ 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,top,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 ) { 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); @@ -508,94 +599,163 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) 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 + i = 0; + if ( top > 5000 ) + { + // need to test the maximum number, this is an estimate. + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot pay more than 5000 addresses")); + if ( params != 0 ) + free_json(params); + return(result); + } + for ( auto address : vAddressSnapshot ) + { + CScript scriptPubKey = GetScriptForDestination(address.second); + for ( auto skipkey : excludeScriptPubKeys ) + { + if ( scriptPubKey != CScript(skipkey.begin(), skipkey.end()) ) + { + 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); //totalallocations += address.first; + mtx.vout.push_back(vout); + mpz_clear(mpzAllocation); + } else fprintf(stderr, "SKIPPED::: %s\n", CBitcoinAddress(address.second).ToString().c_str()); } - } else break; - mtx.vout.push_back(vout); + if ( i == 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")); - } + //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(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,CC_MAXVINS/2,createtxid,latestheight)) >= 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,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough locked funds")); } } else @@ -618,6 +778,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; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -627,14 +789,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,top,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 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -650,13 +812,13 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) else { mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk)); + opret = EncodePaymentsFundOpRet(txid); // Use the below one along with other FinalizeCCTx/return, to get the ccvout scriptpubkey - /*opret = EncodePaymentsFundOpRet(txid); - std::vector> vData = std::vector>(); + /*std::vector> vData = std::vector>(); if ( makeCCopret(opret, vData) ) mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData)); */ } - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsFundOpRet(txid)); + 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); @@ -796,22 +958,73 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) { - uint64_t start = time(NULL); - - return(time(NULL)-start); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); + uint256 hashBlock; CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::string rawtx; + int32_t lockedblocks,minrelease,top,n,i; std::vector> excludeScriptPubKeys; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n >= 4 ) + { + lockedblocks = juint(jitem(params,0),0); + minrelease = juint(jitem(params,1),0); + top = juint(jitem(params,2),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); + } + for (i=0; i scriptPubKey; + int32_t len = strlen(inputhex)/2; + scriptPubKey.resize(len); + decode_hex((uint8_t *)scriptPubKey.data(),len,(char *)inputhex); + excludeScriptPubKeys.push_back(scriptPubKey); + } + 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,EncodePaymentsSnapsShotOpRet(lockedblocks,minrelease,top,excludeScriptPubKeys)); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,0)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough normal funds")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); } 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; + int32_t top; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; 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 ) { @@ -821,6 +1034,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)); @@ -849,29 +1063,71 @@ 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,top,excludeScriptPubKeys) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 ) { - 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); } + 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("top",(int64_t)top)); + 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())); + 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 = CCaddress_balance(fundsaddr,1); + result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); + GetCCaddress(cp,fundsopretaddr,Paymentspk); + // TODO: Shows balance for ALL payments plans, not just the one asked for! Needs to be reworked. + fundsopret = CCaddress_balance(fundsopretaddr,1); + result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); + result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); + 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")); @@ -889,8 +1145,9 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { - std::vector > addressIndex; uint256 txid,hashBlock; + 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; + int32_t top; std::vector> excludeScriptPubKeys; Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); SetCCtxids(addressIndex,markeraddr,true); @@ -899,7 +1156,7 @@ 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,top,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 ) { diff --git a/src/main.cpp b/src/main.cpp index 8bace2cb0..3168b773c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -656,6 +656,7 @@ 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 ) { @@ -663,25 +664,16 @@ bool komodo_dailysnapshot(int32_t height) // 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); - ScanNotarisationsDB(height-extraoffset, symbol, 100, nota); - undo_height = nota.second.height; - if ( undo_height == 0 ) undo_height = height-extraoffset-100; - fprintf(stderr, "height.%i-extraoffset.%i = startscanfrom.%i to get undo_height.%i\n", height, extraoffset, height-extraoffset, undo_height); + 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); - if ( notarized_height > height-100 ) - { - // notarized height is higher than 100 blocks before this height, so snapshot the notarized height. - undo_height = notarized_height; - } - else - { - //snapshot 100 blocks ago. Could still be reorged but very unlikley and expensive to carry out constantly. - undo_height = height-100; - } + notarized_height > height-100 ? 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. @@ -702,9 +694,7 @@ bool komodo_dailysnapshot(int32_t height) for (int32_t i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = block.vtx[i]; - uint256 hash = tx.GetHash(); CTxDestination vDest; - //fprintf(stderr, "undong tx.%s\n",hash.GetHex().c_str()); // loop vouts reverse order, remove value recieved. for (unsigned int k = tx.vout.size(); k-- > 0;) { @@ -721,7 +711,7 @@ bool komodo_dailysnapshot(int32_t height) 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) ) // myGetTransaction! + 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) ) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 66c534f11..8733b2894 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5632,14 +5632,14 @@ UniValue payments_create(const UniValue& params, bool fHelp) UniValue payments_airdrop(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; - //if ( fHelp || params.size() != 1 ) - // throw runtime_error("paymentscreate \"[lockedblocks,minamount,%22paytxid0%22,...,%22paytxidN%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; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsairdrop \"[lockedblocks,minamount,top,%22paytxid0%22,...,%22paytxidN%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(0,(char *)params[0].get_str().c_str())); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsAirdrop(cp,(char *)params[0].get_str().c_str())); } UniValue payments_info(const UniValue& params, bool fHelp) From b4a112888b0d882e5a56f77c777668785563d3bd Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 30 Apr 2019 00:12:00 +0800 Subject: [PATCH 7/8] fix tx expiry height, to 200 blocks --- src/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 8703305e7aa51801b165ca0358636634158059a5 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Thu, 2 May 2019 18:42:40 +0800 Subject: [PATCH 8/8] game commit --- src/cc/CCPayments.h | 1 + src/cc/payments.cpp | 341 +++++++++++++++++++++++++-------------- src/init.cpp | 13 ++ src/komodo_defs.h | 3 +- src/komodo_globals.h | 2 +- src/komodo_utils.h | 8 +- src/main.cpp | 16 +- src/miner.cpp | 6 +- src/wallet/rpcwallet.cpp | 2 +- 9 files changed, 250 insertions(+), 142 deletions(-) diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index cff304a2e..c45fbab9e 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -22,6 +22,7 @@ #define PAYMENTS_TXFEE 10000 extern std::vector > vAddressSnapshot; +extern int32_t lastSnapShotHeight; bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index e72184719..7ab3e8202 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,27 +16,6 @@ #include "CCPayments.h" /* -192.168.0.139: RH side screen. - ./komodod -ac_name=TESTDP -ac_supply=10000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -addndoe=192.168.0.112 - - TESTDP.tar saved after distributing funds randomly. approx block 120. -LH screen: - ./komodod -ac_name=TESTDP -ac_supply=10000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -pubkey=0244a96824fa317433f0eaa6d5b1faf68e802b1958df273c24cb82bce1ef8e1aec -gen -genproclimit=1 - -./komodo-cli -ac_name=TESTDP paymentsairdrop '[10,2000,500,"76a9149758abb81ee168dd3824cb55e94df509b35462d788ac",76a9144cfd873dadbfbb4b9c03e77ecaa6cfb74a484f4888ac"]' - -use notarizations DB to scan back from the correct height, then undo ALL blocks back to this notarized height! -payments airdrop: - - extra RPC to merge all payments inputs to a single utxo, this must be called first and be confirmed before payments release, - or tx will be too big, we can check add payments inputs is only 1 input, at RPC and in validation very early on. - - do getsnapshot2 every 1440 blocks and save the result into some global sorted vector. - -this allows any address balance to be calculated by only iterating each 1439 blocks maximum. - - calculate scriptpubkey to pay from each address, set allocations from balance of each address. allocation = balance? - -payments airdrop paying a token: - - tokenid, top number of tokens to pay, list of pubkeys to exclude as optional param - - token airdrop code should be the same as normal snapshot, but call a diffrent snapshot function that only fetches the info for a specific tokenid given. - this should be fine to work in validation/rpc level rather than a global saved result, as a single token id doesnt require iterating the whole DB. - 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret @@ -138,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; @@ -158,20 +157,19 @@ uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t & return(0); } - -CScript EncodePaymentsSnapsShotOpRet(int32_t lockedblocks,int32_t minrelease,int32_t top,std::vector> excludeScriptPubKeys) +CScript EncodePaymentsSnapsShotOpRet(int32_t lockedblocks,int32_t minrelease,int32_t top,int32_t bottom,int8_t fixedAmount,std::vector> excludeScriptPubKeys) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << top << excludeScriptPubKeys); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << top << bottom << fixedAmount << excludeScriptPubKeys); return(opret); } -uint8_t DecodePaymentsSnapsShotOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &top,std::vector> &excludeScriptPubKeys) +uint8_t DecodePaymentsSnapsShotOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,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 ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> top; ss >> excludeScriptPubKeys) != 0 ) + 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); @@ -233,9 +231,9 @@ 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, tokenid; CTransaction plantx; uint8_t funcid = 0; + char temp[128], coinaddr[64], 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; CPubKey txidpk,Paymentspk; - int32_t top; std::vector> excludeScriptPubKeys; + int32_t top,bottom=0; std::vector> excludeScriptPubKeys; bool fFixedAmount = false; mpz_t mpzTotalAllocations, mpzAllocation;; mpz_init(mpzTotalAllocations); // user marker vout to get the createtxid if ( tx.vout.size() < 2 ) @@ -253,9 +251,9 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // use the createtxid to fetch the tx and all of the plans info. 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,top,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) + 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,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 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) return(eval->Invalid("negative values")); Paymentspk = GetUnspendable(cp,0); txidpk = CCtxidaddr(txidaddr,createtxid); @@ -301,32 +299,60 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & } else if ( funcid == 'S' ) { + if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) + return(eval->Invalid("snapshots not activated on this chain")); + if ( vAddressSnapshot.size() == 0 ) + return(eval->Invalid("need first snapshot")); // need time for TX to me mined before the next snapshot. - if ( top > 5000 ) + if ( top > 3999 ) return(eval->Invalid("transaction too big")); - for ( auto address : vAddressSnapshot ) + if ( fixedAmount == 7 ) { - CScript scriptPubKey = GetScriptForDestination(address.second); - for ( auto skipkey : excludeScriptPubKeys ) + // game setting, randomise bottom and top values + 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); + fFixedAmount = true; + } + 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 ) { - //fprintf(stderr, "scriptpubkey.%s\n skipkey.%s", HexStr(scriptPubKey).c_str(), HexStr(CScript(skipkey.begin(), skipkey.end())).c_str()); - if ( scriptPubKey != CScript(skipkey.begin(), skipkey.end()) ) + if ( scriptPubKey == CScript(skipkey.begin(), skipkey.end()) ) { - 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); - } + skip = true; + //fprintf(stderr, "SKIPPED::: %s\n", CBitcoinAddress(address.second).ToString().c_str()); + } } - if ( i == top ) // we reached top amount to pay, it can be less than this! + 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. @@ -355,12 +381,20 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & 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")); } - mpz_init(mpzAllocation); - mpz_set_si(mpzAllocation,allocations[n]); - mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); - mpz_cdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); - int64_t test = mpz_get_si(mpzAllocation); - mpz_clear(mpzAllocation); + 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 ) { @@ -571,7 +605,7 @@ 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,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; std::vector> excludeScriptPubKeys; int8_t funcid; + int32_t top,bottom=0; 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()); @@ -582,9 +616,9 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( myGetTransaction(createtxid,tx,hashBlock) != 0 && tx.vout.size() > 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,top,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid)) == 'O') ) + 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,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 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -605,7 +639,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) } txidpk = CCtxidaddr(txidaddr,createtxid); mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk)); - if ( funcid = 'C' ) + //fprintf(stderr, "funcid.%i\n", funcid); + if ( funcid == 'C' ) { // normal payments for (i=0; i 5000 ) + if ( vAddressSnapshot.size() == 0 ) { - // need to test the maximum number, this is an estimate. result.push_back(Pair("result","error")); - result.push_back(Pair("error","cannot pay more than 5000 addresses")); + result.push_back(Pair("error","first snapshot has not happened yet")); if ( params != 0 ) free_json(params); return(result); } - for ( auto address : vAddressSnapshot ) + if ( top > 3999 ) { - CScript scriptPubKey = GetScriptForDestination(address.second); - for ( auto skipkey : excludeScriptPubKeys ) + 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; + //for ( auto address : vAddressSnapshot ) + if ( fixedAmount == 7 ) + { + // game setting, randomise bottom and top values + 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); + fFixedAmount = true; + } + 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()) ) + if ( scriptPubKey == CScript(skipkey.begin(), skipkey.end()) ) { - 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); //totalallocations += address.first; - mtx.vout.push_back(vout); - mpz_clear(mpzAllocation); - } else fprintf(stderr, "SKIPPED::: %s\n", CBitcoinAddress(address.second).ToString().c_str()); + skip = true; + //fprintf(stderr, "SKIPPED::: %s\n", CBitcoinAddress(address.second).ToString().c_str()); + } } - if ( i == top ) // we reached top amount to pay, it can be less than this! + 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. } - else if ( funcid = 'O' ) + else if ( funcid == 'O' ) { // token snapshot } newamount = amount; int64_t totalamountsent = 0; mpz_t mpzAmount; mpz_init(mpzAmount); mpz_set_si(mpzAmount,amount); + fprintf(stderr, "m.%i\n",m); for (i=0; i txidoprets; - int32_t top; std::vector> excludeScriptPubKeys; // snapshot - uint256 tokenid; + int32_t top,bottom; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -789,14 +869,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 && DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys) == 0 && DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 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,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 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -961,43 +1041,54 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::string rawtx; - int32_t lockedblocks,minrelease,top,n,i; std::vector> excludeScriptPubKeys; + int32_t lockedblocks,minrelease,top,bottom,n,i; 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); top = juint(jitem(params,2),0); - if ( lockedblocks < 0 || minrelease < 0 || top < 0 ) + bottom = juint(jitem(params,3),0); + fixedAmount = juint(jitem(params,4),0); // fixed amount is a flag set to 0 or 1. It means allocations are equal rather than weighted by address balance. + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 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 5 ) { - /* 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,3+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); + for (i=0; i scriptPubKey; + int32_t len = strlen(inputhex)/2; + scriptPubKey.resize(len); + decode_hex((uint8_t *)scriptPubKey.data(),len,(char *)inputhex); + excludeScriptPubKeys.push_back(scriptPubKey); + } } 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,EncodePaymentsSnapsShotOpRet(lockedblocks,minrelease,top,excludeScriptPubKeys)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsSnapsShotOpRet(lockedblocks,minrelease,top,bottom,fixedAmount,excludeScriptPubKeys)); if ( params != 0 ) free_json(params); - return(payments_rawtxresult(result,rawtx,0)); + return(payments_rawtxresult(result,rawtx,1)); } result.push_back(Pair("result","error")); result.push_back(Pair("error","not enough normal funds")); @@ -1015,8 +1106,8 @@ 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; - int32_t top; std::vector> excludeScriptPubKeys; // snapshot - uint256 tokenid; + int32_t top,bottom; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) { @@ -1071,9 +1162,9 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("error","too many opreturns")); } else result.push_back(Pair("txidoprets",a)); } - else if ( DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys) != 0 ) + else if ( DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,bottom,fixedAmount,excludeScriptPubKeys) != 0 ) { - if ( lockedblocks < 0 || minrelease < 0 || top <= 0 ) + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || fixedAmount < 0 || top > 3999 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -1085,6 +1176,9 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) 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("bottom",(int64_t)bottom)); + 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)); @@ -1104,6 +1198,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) 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)); @@ -1146,8 +1241,8 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { 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; - int32_t top; std::vector> excludeScriptPubKeys; + 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; std::vector> excludeScriptPubKeys; int8_t fixedAmount = 0; Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); SetCCtxids(addressIndex,markeraddr,true); @@ -1156,9 +1251,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' || DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys) == 'S' || DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,top,excludeScriptPubKeys,tokenid) == 'O') ) + 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,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 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -1169,6 +1264,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/init.cpp b/src/init.cpp index 4003f9ced..bab2aba77 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_defs.h b/src/komodo_defs.h index a9df99c0e..a3bde708b 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -20,7 +20,6 @@ #define ASSETCHAINS_MINHEIGHT 128 #define ASSETCHAINS_MAX_ERAS 3 #define KOMODO_ELECTION_GAP 2000 -#define KOMODO_SNAPSHOT_INTERVAL 1440 // 1440 is approx 1 day. Maybe this can be -ac param to allow for diffrent block times etc.? #define ROUNDROBIN_DELAY 61 #define KOMODO_ASSETCHAIN_MAXLEN 65 #define KOMODO_LIMITED_NETWORKSIZE 4 @@ -79,7 +78,7 @@ 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; int tx_height( const uint256 &hash ); extern std::vector vWhiteListAddress; void komodo_netevent(std::vector payload); diff --git a/src/komodo_globals.h b/src/komodo_globals.h index dc8cbda35..8d440cbaf 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -109,7 +109,7 @@ uint64_t PENDING_KOMODO_TX; extern int32_t KOMODO_LOADINGBLOCKS; unsigned int MAX_BLOCK_SIGOPS = 20000; -int32_t KOMODO_TESTNODE; +int32_t KOMODO_TESTNODE, KOMODO_SNAPSHOT_INTERVAL; struct komodo_kv *KOMODO_KV; pthread_mutex_t KOMODO_KV_mutex,KOMODO_CC_mutex; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index cfb133a9d..137bace4c 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1752,6 +1752,7 @@ void komodo_args(char *argv0) ASSETCHAINS_BLOCKTIME = GetArg("-ac_blocktime",60); ASSETCHAINS_PUBLIC = GetArg("-ac_public",0); ASSETCHAINS_PRIVATE = GetArg("-ac_private",0); + KOMODO_SNAPSHOT_INTERVAL = GetArg("-ac_snapshot",0); Split(GetArg("-ac_nk",""), ASSETCHAINS_NK, 0); if ( (KOMODO_REWIND= GetArg("-rewind",0)) != 0 ) { @@ -2012,7 +2013,7 @@ void komodo_args(char *argv0) fprintf(stderr,"-ac_script and -ac_marmara are mutually exclusive\n"); StartShutdown(); } - 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) ) + 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 ) { 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; @@ -2148,6 +2149,11 @@ 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); + fprintf(stderr, "snapshot interval.%i\n",KOMODO_SNAPSHOT_INTERVAL); + } } addn = GetArg("-seednode",""); diff --git a/src/main.cpp b/src/main.cpp index 3168b773c..ef1a1a40b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -673,7 +673,7 @@ bool komodo_dailysnapshot(int32_t height) { // 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-100 ? undo_height = notarized_height : undo_height = height-reorglimit; + 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. @@ -732,7 +732,7 @@ bool komodo_dailysnapshot(int32_t height) //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() > 5000 ) vAddressSnapshot.resize(5000); + if ( vAddressSnapshot.size() > 3999 ) vAddressSnapshot.resize(3999); lastSnapShotHeight = undo_height; fprintf(stderr, "vAddressSnapshot.size.%li\n", vAddressSnapshot.size()); return true; @@ -3592,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); @@ -4251,7 +4251,7 @@ 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 && (pindexNew->GetHeight() % KOMODO_SNAPSHOT_INTERVAL) == 0 ) + 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()) ) @@ -6198,12 +6198,6 @@ 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); - if ( ASSETCHAINS_CC != 0 && chainActive.Height() > KOMODO_SNAPSHOT_INTERVAL ) - { - if ( !komodo_dailysnapshot(chainActive.Height()) ) - fprintf(stderr, "daily snapshot failed, please reindex your chain\n"); // maybe force shutdown here? - } - return true; } diff --git a/src/miner.cpp b/src/miner.cpp index a2ff8c933..810607d5a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -291,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); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 8733b2894..8a671b322 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5633,7 +5633,7 @@ UniValue payments_airdrop(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) - throw runtime_error("paymentsairdrop \"[lockedblocks,minamount,top,%22paytxid0%22,...,%22paytxidN%22]\"\n"); + throw runtime_error("paymentsairdrop \"[lockedblocks,minamount,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;