diff --git a/src/Makefile.am b/src/Makefile.am index 5e665cc3c..96ab3f1fa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -571,7 +571,8 @@ komodod_LDADD += \ $(LIBBITCOIN_CRYPTO) \ $(LIBVERUS_CRYPTO) \ $(LIBVERUS_PORTABLE_CRYPTO) \ - $(LIBZCASH_LIBS) + $(LIBZCASH_LIBS) \ + cclib.so if ENABLE_PROTON komodod_LDADD += $(LIBBITCOIN_PROTON) $(PROTON_LIBS) diff --git a/src/cc/CC made easy.md b/src/cc/CC made easy.md index 453aa5467..2d1081003 100644 --- a/src/cc/CC made easy.md +++ b/src/cc/CC made easy.md @@ -212,7 +212,7 @@ bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) -int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddFaucetInputs(struct CCcontract_infoCC_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) std::string FaucetGet(uint64_t txfee) diff --git a/src/cc/CCMarmara.h b/src/cc/CCMarmara.h index 753f8edb6..65269df77 100644 --- a/src/cc/CCMarmara.h +++ b/src/cc/CCMarmara.h @@ -23,15 +23,18 @@ #define MARMARA_GROUPSIZE 60 #define MARMARA_MINLOCK (1440 * 3 * 30) #define MARMARA_MAXLOCK (1440 * 24 * 30) +#define MARMARA_VINS 16 +extern uint8_t ASSETCHAINS_MARMARA; uint64_t komodo_block_prg(uint32_t nHeight); int32_t MarmaraGetcreatetxid(uint256 &createtxid,uint256 txid); int32_t MarmaraGetbatontxid(std::vector &creditloop,uint256 &batontxid,uint256 txid); UniValue MarmaraCreditloop(uint256 txid); UniValue MarmaraSettlement(uint64_t txfee,uint256 batontxid); +UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height); UniValue MarmaraPoolPayout(uint64_t txfee,int32_t firstheight,double perc,char *jsonstr); // [[pk0, shares0], [pk1, shares1], ...] -UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid); +UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid,bool automaticflag); UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t amount,std::string currency,int32_t matures,uint256 approvaltxid,uint256 batontxid); UniValue MarmaraInfo(CPubKey refpk,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,std::string currency); diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 918ceb24a..577332b63 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -234,6 +234,34 @@ uint8_t TokensCCpriv[32] = { 0x1d, 0x0d, 0x0d, 0xce, 0x2d, 0xd2, 0xe1, 0x9d, 0xf #undef FUNCNAME #undef EVALCODE +#define FUNCNAME IsCClibInput +#define EVALCODE EVAL_FIRSTUSER +const char *CClibNormaladdr = "RVVeUg43rNcq3mZFnvZ8yqagyzqFgUnq4u"; +char CClibCChexstr[67] = { "032447d97655da079729dc024c61088ea415b22f4c15d4810ddaf2069ac6468d2f" }; +uint8_t CClibCCpriv[32] = { 0x57, 0xcf, 0x49, 0x71, 0x7d, 0xb4, 0x15, 0x1b, 0x4f, 0x98, 0xc5, 0x45, 0x8d, 0x26, 0x52, 0x4b, 0x7b, 0xe9, 0xbd, 0x55, 0xd8, 0x20, 0xd6, 0xc4, 0x82, 0x0f, 0xf5, 0xec, 0x6c, 0x1c, 0xa0, 0xc0 }; +#include "CCcustom.inc" +#undef FUNCNAME +#undef EVALCODE + +int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode) +{ + CPubKey pk; uint8_t pub33[33]; char CCaddr[64]; + if ( evalcode == EVAL_FIRSTUSER ) // eventually make a hashchain for each evalcode + { + cp->evalcode = evalcode; + cp->ismyvin = IsCClibInput; + strcpy(cp->CChexstr,CClibCChexstr); + memcpy(cp->CCpriv,CClibCCpriv,32); + decode_hex(pub33,33,cp->CChexstr); + pk = buf2pk(pub33); + Getscriptaddress(cp->normaladdr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + if ( strcmp(cp->normaladdr,CClibNormaladdr) != 0 ) + fprintf(stderr,"CClib_initcp addr mismatch %s vs %s\n",cp->normaladdr,CClibNormaladdr); + GetCCaddress(cp,cp->unspendableCCaddr,pk); + return(0); + } + return(-1); +} struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) { @@ -369,6 +397,10 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->validate = TokensValidate; cp->ismyvin = IsTokensInput; break; + default: + if ( CClib_initcp(cp,evalcode) < 0 ) + return(0); + break; } return(cp); } diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 982f5aa77..1953fad34 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -54,7 +54,8 @@ one other technical note is that komodod has the insight-explorer extensions bui extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE; extern uint32_t ASSETCHAINS_CC; extern char ASSETCHAINS_SYMBOL[]; -extern std::string CCerror; +extern std::string CCerror,ASSETCHAINS_CCLIB; +extern uint8_t ASSETCHAINS_CCDISABLES[256]; #define CC_MAXVINS 1024 @@ -68,6 +69,7 @@ extern std::string CCerror; typedef union _bits256 bits256; #endif +#include "../komodo_cJSON.h" struct CC_utxo { @@ -133,6 +135,9 @@ CBlockIndex *komodo_getblockindex(uint256 hash); int32_t komodo_nextheight(); int32_t CCgetspenttxid(uint256 &spenttxid,int32_t &vini,int32_t &height,uint256 txid,int32_t vout); +void CCclearvars(struct CCcontract_info *cp); +UniValue CClib(struct CCcontract_info *cp,char *method,cJSON *params); +UniValue CClib_info(struct CCcontract_info *cp); static const uint256 zeroid; bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock); @@ -199,6 +204,7 @@ CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); +int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode); bool IsCCInput(CScript const& scriptSig); int32_t unstringbits(char *buf,uint64_t bits); @@ -225,7 +231,8 @@ bool komodo_txnotarizedconfirmed(uint256 txid); CPubKey check_signing_pubkey(CScript scriptSig); // CCtx bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey); -std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret); +extern std::vector NULL_pubkeys; +std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector pubkeys = NULL_pubkeys); void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr); void SetCCtxids(std::vector > &addressIndex,char *coinaddr); int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs); diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 2961f6597..bdb710afb 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -766,4 +766,4 @@ UniValue TokenList() } } return(result); -} \ No newline at end of file +} diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 1ca4fbf91..ae7483f4d 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -16,6 +16,8 @@ #include "CCinclude.h" #include "key_io.h" +std::vector NULL_pubkeys; + /* FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and the opreturn. @@ -38,18 +40,18 @@ bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScrip return(false); } -std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret) +std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector pubkeys) { auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - CTransaction vintx; std::string hex; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0; + CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0; int64_t utxovalues[CC_MAXVINS],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0; - int32_t i,utxovout,n,err = 0; + int32_t i,flag,utxovout,n,err = 0; char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], tokensunspendable[64]; uint8_t *privkey, myprivkey[32], unspendablepriv[32], tokensunspendablepriv[32], *msg32 = 0; - CC *mycond=0, *othercond=0, *othercond2=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; + CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; CPubKey unspendablepk /*, tokensunspendablepk*/; struct CCcontract_info *cpTokens, tokensC; - + globalpk = GetUnspendable(cp,0); n = mtx.vout.size(); for (i=0; i %s vs %s\n",HexStr(globalpk).c_str(),HexStr(pubkeys[i]).c_str(),coinaddr,destaddr); + if ( strcmp(destaddr,coinaddr) == 0 ) + { + privkey = cp->CCpriv; + if ( othercond4 != 0 ) + cc_free(othercond4); + othercond4 = MakeCCcond1of2(cp->evalcode,globalpk,pubkeys[i]); + cond = othercond4; + flag = 1; + } + } + if ( flag == 0 ) + { + fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr); + continue; + } } uint256 sighash = SignatureHash(CCPubKey(cond), mtx, i, SIGHASH_ALL, utxovalues[i],consensusBranchId, &txdata); if ( cc_signTreeSecp256k1Msg32(cond,privkey,sighash.begin()) != 0 ) @@ -252,6 +273,8 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran cc_free(othercond2); if ( othercond3 != 0 ) cc_free(othercond3); + if ( othercond4 != 0 ) + cc_free(othercond4); std::string strHex = EncodeHexTx(mtx); if ( strHex.size() > 0 ) return(strHex); @@ -450,8 +473,8 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 { int32_t abovei,belowi,ind,vout,i,n = 0,maxutxos=64; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; #ifdef ENABLE_WALLET - const CKeyStore& keystore = *pwalletMain; assert(pwalletMain != NULL); + const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index e9c30acc1..67ee7d5db 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -493,39 +493,10 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv) return(pubkey2pk(ParseHex(cp->CChexstr))); } -bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector paramsNull,const CTransaction &ctx, unsigned int nIn) +void CCclearvars(struct CCcontract_info *cp) { - CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector origpubkey; - height = KOMODO_CONNECTING; - if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation - return(true); - if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) - return eval->Invalid("CC are disabled or not active yet"); - if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) - { - from_mempool = 1; - height &= ((1<<30) - 1); - } - //fprintf(stderr,"KOMODO_CONNECTING.%d mempool.%d vs CCactive.%d\n",height,from_mempool,KOMODO_CCACTIVATE); - // there is a chance CC tx is valid in mempool, but invalid when in block, so we cant filter duplicate requests. if any of the vins are spent, for example - //txid = ctx.GetHash(); - //if ( txid == cp->prevtxid ) - // return(true); - //fprintf(stderr,"process CC %02x\n",cp->evalcode); cp->evalcode2 = cp->evalcode3 = 0; cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0; - if ( paramsNull.size() != 0 ) // Don't expect params - return eval->Invalid("Cannot have params"); - //else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses - // return eval->Invalid("no-vouts"); - else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 ) - { - //fprintf(stderr,"done CC %02x\n",cp->evalcode); - //cp->prevtxid = txid; - return(true); - } - //fprintf(stderr,"invalid CC %02x\n",cp->evalcode); - return(false); } int64_t CCduration(int32_t &numblocks,uint256 txid) @@ -630,3 +601,79 @@ CPubKey check_signing_pubkey(CScript scriptSig) } return CPubKey(); } + +bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector paramsNull,const CTransaction &ctx, unsigned int nIn) +{ + CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector origpubkey; + height = KOMODO_CONNECTING; + if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation + return(true); + if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) + return eval->Invalid("CC are disabled or not active yet"); + if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) + { + from_mempool = 1; + height &= ((1<<30) - 1); + } + //fprintf(stderr,"KOMODO_CONNECTING.%d mempool.%d vs CCactive.%d\n",height,from_mempool,KOMODO_CCACTIVATE); + // there is a chance CC tx is valid in mempool, but invalid when in block, so we cant filter duplicate requests. if any of the vins are spent, for example + //txid = ctx.GetHash(); + //if ( txid == cp->prevtxid ) + // return(true); + //fprintf(stderr,"process CC %02x\n",cp->evalcode); + CCclearvars(cp); + if ( paramsNull.size() != 0 ) // Don't expect params + return eval->Invalid("Cannot have params"); + //else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses + // return eval->Invalid("no-vouts"); + else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 ) + { + //fprintf(stderr,"done CC %02x\n",cp->evalcode); + //cp->prevtxid = txid; + return(true); + } + //fprintf(stderr,"invalid CC %02x\n",cp->evalcode); + return(false); +} + +extern struct CCcontract_info CCinfos[0x100]; +extern std::string MYCCLIBNAME; +bool CClib_validate(struct CCcontract_info *cp,Eval *eval,const CTransaction tx,unsigned int nIn); + +bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector paramsNull,const CTransaction &txTo,unsigned int nIn) +{ + uint8_t evalcode; int32_t height,from_mempool; struct CCcontract_info *cp; + if ( ASSETCHAINS_CCLIB != MYCCLIBNAME ) + { + fprintf(stderr,"-ac_cclib=%s vs myname %s\n",ASSETCHAINS_CCLIB.c_str(),MYCCLIBNAME.c_str()); + return eval->Invalid("-ac_cclib name mismatches myname"); + } + height = KOMODO_CONNECTING; + if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation + return(true); + if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE ) + return eval->Invalid("CC are disabled or not active yet"); + if ( (KOMODO_CONNECTING & (1<<30)) != 0 ) + { + from_mempool = 1; + height &= ((1<<30) - 1); + } + evalcode = cond->code[0]; + if ( evalcode >= EVAL_FIRSTUSER && evalcode <= EVAL_LASTUSER ) + { + cp = &CCinfos[(int32_t)evalcode]; + if ( cp->didinit == 0 ) + { + if ( CClib_initcp(cp,evalcode) == 0 ) + cp->didinit = 1; + else return eval->Invalid("unsupported CClib evalcode"); + } + CCclearvars(cp); + if ( paramsNull.size() != 0 ) // Don't expect params + return eval->Invalid("Cannot have params"); + else if ( CClib_validate(cp,eval,txTo,nIn) != 0 ) + return(true); + return eval->Invalid("error in CClib_validate"); + } + return eval->Invalid("cclib CC must have evalcode between 16 and 127"); +} diff --git a/src/cc/Makefile b/src/cc/Makefile new file mode 100644 index 000000000..a3c54d81d --- /dev/null +++ b/src/cc/Makefile @@ -0,0 +1,33 @@ +SHELL = /bin/sh +CC = gcc +CC_DARWIN = g++-6 +CC_WIN = x86_64-w64-mingw32-gcc-posix +CFLAGS_DARWIN = -std=c++11 -arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -c -Wl,-undefined -Wl,dynamic_lookup +CFLAGS = -std=c++11 -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c +CFLAGS_WIN = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c +DEBUGFLAGS = -O0 -D _DEBUG +RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program +$(info $(OS)) +OS := $(shell uname -s) +$(info $(OS)) +TARGET = ../cclib.so +TARGET_WIN = ../cclib.dll +SOURCES = cclib.cpp +#HEADERS = $(shell echo ../cryptoconditions/include/*.h) + +all: $(TARGET) + +$(TARGET): $(SOURCES) + $(info Building cclib to src/) +ifeq ($(OS),Darwin) + $(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET) $(SOURCES) +else ifeq ($(OS),Linux) + $(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) $(SOURCES) +#else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host +else + $(info WINDOWS) + $(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) $(SOURCES) +endif + +clean: + rm -rf $(TARGET) diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp new file mode 100644 index 000000000..5a9c3b4af --- /dev/null +++ b/src/cc/cclib.cpp @@ -0,0 +1,317 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include +#include + +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "script/cc.h" +#include "cc/eval.h" +#include "cc/utils.h" +#include "cc/CCinclude.h" +#include "main.h" +#include "chain.h" +#include "core_io.h" +#include "crosschain.h" + +#define FAUCET2SIZE COIN + +struct CClib_rpcinfo +{ + char *method,*help; + int32_t numrequiredargs,maxargs; // frontloaded with required + uint8_t funcid; +} +CClib_methods[] = +{ + { (char *)"faucet2_fund", (char *)"amount", 1, 1, 'F' }, + { (char *)"faucet2_get", (char *)"", 0, 0, 'G' }, +}; + +std::string MYCCLIBNAME = (char *)"faucet2"; + +char *CClib_name() { return((char *)MYCCLIBNAME.c_str()); } + +std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params); + +UniValue CClib_info(struct CCcontract_info *cp) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i; char str[2]; + result.push_back(Pair("result","success")); + result.push_back(Pair("CClib",CClib_name())); + for (i=0; i= 128 ) + obj.push_back(Pair("funcid",CClib_methods[i].funcid)); + else + { + str[0] = CClib_methods[i].funcid; + str[1] = 0; + obj.push_back(Pair("funcid",str)); + } + obj.push_back(Pair("name",CClib_methods[i].method)); + obj.push_back(Pair("help",CClib_methods[i].help)); + obj.push_back(Pair("params_required",CClib_methods[i].numrequiredargs)); + obj.push_back(Pair("params_max",CClib_methods[i].maxargs)); + a.push_back(obj); + } + result.push_back(Pair("methods",a)); + return(result); +} + +UniValue CClib(struct CCcontract_info *cp,char *method,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; + for (i=0; i 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + return(tx.vout[v].nValue); + } + return(0); +} + +bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) +{ + static uint256 zerohash; + CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) + { + //fprintf(stderr,"vini.%d check mempool\n",i); + if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("cant find vinTx"); + else + { + //fprintf(stderr,"vini.%d check hash and vout\n",i); + if ( hashBlock == zerohash ) + return eval->Invalid("cant faucet2 from mempool"); + if ( (assetoshis= IsCClibvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) + inputs += assetoshis; + } + } + } + for (i=0; iInvalid("mismatched inputs != outputs + FAUCET2SIZE + txfee"); + } + else return(true); +} + +bool CClib_validate(struct CCcontract_info *cp,Eval *eval,const CTransaction tx,unsigned int nIn) +{ + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; + std::vector > txids; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + preventCCvins = preventCCvouts = -1; + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else + { + for (i=0; iInvalid("illegal normal vini"); + } + } + //fprintf(stderr,"check amounts\n"); + if ( CClibExactAmounts(cp,eval,tx,1,10000) == false ) + { + fprintf(stderr,"faucetget invalid amount\n"); + return false; + } + else + { + preventCCvouts = 1; + if ( IsCClibvout(cp,tx,0) != 0 ) + { + preventCCvouts++; + i = 1; + } else i = 0; + txid = tx.GetHash(); + memcpy(hash,&txid,sizeof(hash)); + fprintf(stderr,"check faucetget txid %s %02x/%02x\n",uint256_str(str,txid),hash[0],hash[31]); + if ( tx.vout[i].nValue != FAUCET2SIZE ) + return eval->Invalid("invalid faucet output"); + else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 ) + return eval->Invalid("invalid faucetget txid"); + Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); + SetCCtxids(txids,destaddr); + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + { + //int height = it->first.blockHeight; + if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 ) + { + //fprintf(stderr,"would return error %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks); + return eval->Invalid("faucet2 is only for brand new addresses"); + } + } + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + fprintf(stderr,"faucet2get validated\n"); + else fprintf(stderr,"faucet2get invalid\n"); + return(retval); + } + } +} + +int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +{ + char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + std::vector > unspentOutputs; + GetCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr); + threshold = total/(maxinputs+1); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( it->second.satoshis < threshold ) + continue; + //char str[65]; fprintf(stderr,"check %s/v%d %.8f`\n",uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + // no need to prevent dup + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( (nValue= IsCClibvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } else fprintf(stderr,"nValue too small or already spent in mempool\n"); + } else fprintf(stderr,"couldnt get tx\n"); + } + return(totalinputs); +} + + +std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,cclibpk; CScript opret; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + cclibpk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,cclibpk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + } + return(""); +} + +/*UniValue FaucetInfo() +{ + UniValue result(UniValue::VOBJ); char numstr[64]; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey faucetpk; struct CCcontract_info *cp,C; int64_t funding; + result.push_back(Pair("result","success")); + result.push_back(Pair("name","Faucet")); + cp = CCinit(&C,EVAL_FAUCET); + faucetpk = GetUnspendable(cp,0); + funding = AddFaucetInputs(cp,mtx,faucetpk,0,0); + sprintf(numstr,"%.8f",(double)funding/COIN); + result.push_back(Pair("funding",numstr)); + return(result); +}*/ + +std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params) +{ + CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,cclibpk; int64_t funds,txfee=0,inputs,CCchange=0,nValue=FAUCET2SIZE; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; + if ( txfee == 0 ) + txfee = 10000; + if ( funcid == 'F' ) + { + if ( cJSON_GetArraySize(params) > 0 ) + { + funds = (int64_t)jdouble(jitem(params,0),0)*COIN + 0.0000000049; + return(Faucet2Fund(cp,0,funds)); + } else return(""); + } + else if ( funcid != 'G' ) + return(""); + cclibpk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + if ( (inputs= AddCClibInputs(cp,mtx,cclibpk,nValue+txfee,60)) > 0 ) + { + if ( inputs > nValue ) + CCchange = (inputs - nValue - txfee); + if ( CCchange != 0 ) + mtx.vout.push_back(MakeCC1vout(EVAL_FIRSTUSER,CCchange,cclibpk)); + mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); + j = rand() & 0xfffffff; + for (i=0; i<1000000; i++,j++) + { + tmpmtx = mtx; + rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FIRSTUSER << (uint8_t)'G' << j)); + if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) + { + len >>= 1; + decode_hex(buf,len,(char *)rawhex.c_str()); + hash = bits256_doublesha256(0,buf,len); + if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) + { + fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); + return(rawhex); + } + //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]); + } + } + fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); + return(""); + } else fprintf(stderr,"cant find faucet inputs\n"); + return(""); +} diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 9161f2007..ca2703bff 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -27,6 +27,8 @@ #include "core_io.h" #include "crosschain.h" +bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector paramsNull,const CTransaction &txTo,unsigned int nIn); +char *CClib_name(); Eval* EVAL_TEST = 0; struct CCcontract_info CCinfos[0x100]; @@ -65,13 +67,24 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) return Invalid("empty-eval"); uint8_t ecode = cond->code[0]; + if ( ASSETCHAINS_CCDISABLES[ecode] != 0 ) + { + fprintf(stderr,"%s evalcode.%d %02x\n",txTo.GetHash().GetHex().c_str(),ecode,ecode); + return Invalid("disabled-code, -ac_ccenables didnt include this ecode"); + } + std::vector vparams(cond->code+1, cond->code+cond->codeLength); + if ( ecode >= EVAL_FIRSTUSER && ecode <= EVAL_LASTUSER ) + { + if ( ASSETCHAINS_CCLIB.size() > 0 && ASSETCHAINS_CCLIB == CClib_name() ) + return CClib_Dispatch(cond,this,vparams,txTo,nIn); + else return Invalid("mismatched -ac_cclib vs CClib_name"); + } cp = &CCinfos[(int32_t)ecode]; if ( cp->didinit == 0 ) { CCinit(cp,ecode); cp->didinit = 1; } - std::vector vparams(cond->code+1, cond->code+cond->codeLength); switch ( ecode ) { case EVAL_IMPORTPAYOUT: diff --git a/src/cc/eval.h b/src/cc/eval.h index de1636ab8..a42bbdb2f 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -59,6 +59,10 @@ EVAL(EVAL_TOKENS, 0xf2) +// evalcodes 0x10 to 0x7f are reserved for cclib dynamic CC +#define EVAL_FIRSTUSER 0x10 +#define EVAL_LASTUSER 0x7f + typedef uint8_t EvalCode; diff --git a/src/cc/heir.cpp b/src/cc/heir.cpp index 4a78c69ac..ccf9da1bf 100644 --- a/src/cc/heir.cpp +++ b/src/cc/heir.cpp @@ -21,14 +21,14 @@ class TokenHelper; /* The idea of Heir CC is to allow crypto inheritance. - A special 1of2 CC address is created that is freely spendable by the creator (funds owner). + A special 1of2 CC address is created that is freely spendable by the creator (funds owner). The owner may add additional funds to this 1of2 address. - The heir is only allowed to spend after "the specified amount of idle blocks" (changed to "the owner inactivityTime"). - The idea is that if the address doesnt spend any funds for a year (or whatever amount set), then it is time to allow the heir to spend. + The heir is only allowed to spend after "the specified amount of idle blocks" (changed to "the owner inactivityTime"). + The idea is that if the address doesnt spend any funds for a year (or whatever amount set), then it is time to allow the heir to spend. "The design requires the heir to spend all the funds at once" (this requirement was changed to "after the inactivity time both the heir and owner may freely spend available funds") After the first heir spending a flag is set that spending is allowed for the heir whether the owner adds more funds or spends them. This Heir contract supports both coins and tokens. -*/ + */ // tx validation code @@ -36,69 +36,69 @@ class TokenHelper; // (sadly we cannot have yet 'templatized' lambdas, if we could we could capture all these params inside HeirValidation()...) template bool RunValidationPlans(uint8_t funcId, struct CCcontract_info* cp, Eval* eval, const CTransaction& tx, uint256 latestTxid, CScript fundingOpretScript, uint8_t hasHeirSpendingBegun) { - int32_t numvins = tx.vin.size(); - int32_t numvouts = tx.vout.size(); - - // setup validation framework (please see its description in heir_validate.h): - // validation 'plans': - CInputValidationPlan vinPlan; - COutputValidationPlan voutPlan; - - // vin 'identifiers' - CNormalInputIdentifier normalInputIdentifier(cp); - CCCInputIdentifier ccInputIdentifier(cp); - - // vin and vout 'validators' - // not used, too strict for 2 pubkeys: CMyPubkeyVoutValidator normalInputValidator(cp, fundingOpretScript, true); // check normal input for this opret cause this is first tx - CCC1of2AddressValidator cc1of2ValidatorThis(cp, fundingOpretScript, "checking this tx opreturn:"); // 1of2add validator with pubkeys from this tx opreturn - CHeirSpendValidator heirSpendValidator(cp, fundingOpretScript, latestTxid, hasHeirSpendingBegun); // check if heir allowed to spend - - // only for tokens: - CMyPubkeyVoutValidator ownerCCaddrValidator(cp, fundingOpretScript, false); // check if this correct owner's cc user addr corresponding to opret - COpRetValidator opRetValidator(cp, fundingOpretScript); // compare opRets in this and last tx - CNullValidator nullValidator(cp); - - switch (funcId) { - case 'F': // fund tokens - // vin validation plan: - vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr - - // vout validation plan: - voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding - // do not check change at this time - // no checking for opret yet - break; - - case 'A': // add tokens - // vin validation plan: - vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr - - // vout validation plan: - voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding - // do not check change at this time - voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A: - break; - - case 'C': - // vin validation plan: - vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin - vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &cc1of2ValidatorThis); // cc1of2 funding addr - - // vout validation plan: - voutPlan.pushValidators(0, &heirSpendValidator); // check if heir is allowed to spend - voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A - break; - } - - // call vin/vout validation - if (!vinPlan.validate(tx, eval)) - return false; - if (!voutPlan.validate(tx, eval)) - return false; - - return true; + int32_t numvins = tx.vin.size(); + int32_t numvouts = tx.vout.size(); + + // setup validation framework (please see its description in heir_validate.h): + // validation 'plans': + CInputValidationPlan vinPlan; + COutputValidationPlan voutPlan; + + // vin 'identifiers' + CNormalInputIdentifier normalInputIdentifier(cp); + CCCInputIdentifier ccInputIdentifier(cp); + + // vin and vout 'validators' + // not used, too strict for 2 pubkeys: CMyPubkeyVoutValidator normalInputValidator(cp, fundingOpretScript, true); // check normal input for this opret cause this is first tx + CCC1of2AddressValidator cc1of2ValidatorThis(cp, fundingOpretScript, "checking this tx opreturn:"); // 1of2add validator with pubkeys from this tx opreturn + CHeirSpendValidator heirSpendValidator(cp, fundingOpretScript, latestTxid, hasHeirSpendingBegun); // check if heir allowed to spend + + // only for tokens: + CMyPubkeyVoutValidator ownerCCaddrValidator(cp, fundingOpretScript, false); // check if this correct owner's cc user addr corresponding to opret + COpRetValidator opRetValidator(cp, fundingOpretScript); // compare opRets in this and last tx + CNullValidator nullValidator(cp); + + switch (funcId) { + case 'F': // fund tokens + // vin validation plan: + vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr + + // vout validation plan: + voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding + // do not check change at this time + // no checking for opret yet + break; + + case 'A': // add tokens + // vin validation plan: + vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &ownerCCaddrValidator); // check cc owner addr + + // vout validation plan: + voutPlan.pushValidators(0, &cc1of2ValidatorThis); // check 1of2 addr funding + // do not check change at this time + voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A: + break; + + case 'C': + // vin validation plan: + vinPlan.pushValidators((CInputIdentifierBase*)&normalInputIdentifier, &nullValidator); // txfee vin + vinPlan.pushValidators((CInputIdentifierBase*)&ccInputIdentifier, &cc1of2ValidatorThis); // cc1of2 funding addr + + // vout validation plan: + voutPlan.pushValidators(0, &heirSpendValidator); // check if heir is allowed to spend + voutPlan.pushValidators(numvouts - 1, &opRetValidator); // opreturn check, NOTE: only for C or A + break; + } + + // call vin/vout validation + if (!vinPlan.validate(tx, eval)) + return false; + if (!voutPlan.validate(tx, eval)) + return false; + + return true; } /** @@ -110,121 +110,121 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction int32_t numvouts = tx.vout.size(); //int32_t preventCCvins = -1; //int32_t preventCCvouts = -1; - - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - + + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + if (numvouts < 1) return eval->Invalid("no vouts"); - - //if (chainActive.Height() < 741) - // return true; - + + //if (chainActive.Height() < 741) + // return true; + uint256 fundingTxidInOpret = zeroid, latestTxid = zeroid, dummyTokenid, tokenidThis, tokenid = zeroid; - CScript fundingTxOpRetScript; - uint8_t hasHeirSpendingBegun = 0, hasHeirSpendingBegunDummy; - - CScript opret = (tx.vout.size() > 0) ? tx.vout[tx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenidThis, fundingTxidInOpret, hasHeirSpendingBegunDummy, true); - if (funcId == 0) + CScript fundingTxOpRetScript; + uint8_t hasHeirSpendingBegun = 0, hasHeirSpendingBegunDummy; + + CScript opret = (tx.vout.size() > 0) ? tx.vout[tx.vout.size() - 1].scriptPubKey : CScript(); // check boundary + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenidThis, fundingTxidInOpret, hasHeirSpendingBegunDummy, true); + if (funcId == 0) return eval->Invalid("invalid opreturn format"); - + if (funcId != 'F') { if (fundingTxidInOpret == zeroid) { return eval->Invalid("incorrect tx opreturn: no fundingtxid present"); } - latestTxid = FindLatestFundingTx(fundingTxidInOpret, tokenid, fundingTxOpRetScript, hasHeirSpendingBegun); - - if( tokenid != zeroid && tokenid != tokenidThis ) - return eval->Invalid("incorrect tx tokenid"); - - if (latestTxid == zeroid) { + latestTxid = FindLatestFundingTx(fundingTxidInOpret, tokenid, fundingTxOpRetScript, hasHeirSpendingBegun); + + if( tokenid != zeroid && tokenid != tokenidThis ) + return eval->Invalid("incorrect tx tokenid"); + + if (latestTxid == zeroid) { return eval->Invalid("no fundingtx found"); } - } - else { - fundingTxOpRetScript = opret; - } - - std::cerr << "HeirValidate funcid=" << (char)funcId << " evalcode=" << (int)cpHeir->evalcode << std::endl; - - //////////////// temp //////////////////////// - ///return true; - + } + else { + fundingTxOpRetScript = opret; + } + + std::cerr << "HeirValidate funcid=" << (char)funcId << " evalcode=" << (int)cpHeir->evalcode << std::endl; + + //////////////// temp //////////////////////// + ///return true; + switch (funcId) { - case 'F': - // fund coins: - // vins.*: normal inputs - // ----------------------------- - // vout.0: funding CC 1of2 addr for the owner and heir - // vout.1: txfee for CC addr used as a marker - // vout.2: normal change - // vout.n-1: opreturn 'F' ownerpk heirpk inactivitytime heirname - - // fund tokens: - // vin.0: normal inputs txfee - // vins.1+: user's CC addr inputs - // ----------------------- - // vout.0: funding heir CC 1of2 addr for the owner and heir - // vout.1: txfee for CC addr used as a marker - // vout.2: normal change - // vout.n-1: opreturn 't' tokenid 'F' ownerpk heirpk inactivitytime heirname tokenid - if (tokenid != zeroid) - return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - else - return eval->Invalid("unexpected HeirValidate for heirfund"); - // break; - - case 'A': - // add funding coins: - // vins.*: normal inputs - // ------------------------ - // vout.0: funding CC 1of2 addr for the owner and heir - // vout.1: normal change - // vout.n-1: opreturn 'A' ownerpk heirpk inactivitytime fundingtx - - // add funding tokens: - // vins.0: normal inputs txfee - // vins.1+: user's CC addr inputs - // ------------------------ - // vout.0: funding CC 1of2 addr for the owner and heir - // vout.1: normal change - // vout.n-1: opreturn 't' tokenid 'A' ownerpk heirpk inactivitytime fundingtx - if (tokenid != zeroid) - return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - else - return eval->Invalid("unexpected HeirValidate for heiradd"); - //break; - - case 'C': - // claim coins: - // vin.0: normal input txfee - // vin.1+: input from CC 1of2 addr - // ------------------------------------- - // vout.0: normal output to owner or heir address - // vout.1: change to CC 1of2 addr - // vout.2: change to user's addr from txfee input if any - // vout.n-1: opreturn 'C' ownerpk heirpk inactivitytime fundingtx - - // claim tokens: - // vin.0: normal input txfee - // vin.1+: input from CC 1of2 addr - // -------------------------------------------- - // vout.0: output to user's cc address - // vout.1: change to CC 1of2 addr - // vout.2: change to normal from txfee input if any - // vout.n-1: opreturn 't' tokenid 'C' ownerpk heirpk inactivitytime fundingtx - if (tokenid != zeroid) - return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - else - return RunValidationPlans(funcId, cpHeir, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); - // break; - - default: - std::cerr << "HeirValidate() illegal heir funcid=" << (char)funcId << std::endl; - return eval->Invalid("unexpected HeirValidate funcid"); - // break; + case 'F': + // fund coins: + // vins.*: normal inputs + // ----------------------------- + // vout.0: funding CC 1of2 addr for the owner and heir + // vout.1: txfee for CC addr used as a marker + // vout.2: normal change + // vout.n-1: opreturn 'F' ownerpk heirpk inactivitytime heirname + + // fund tokens: + // vin.0: normal inputs txfee + // vins.1+: user's CC addr inputs + // ----------------------- + // vout.0: funding heir CC 1of2 addr for the owner and heir + // vout.1: txfee for CC addr used as a marker + // vout.2: normal change + // vout.n-1: opreturn 't' tokenid 'F' ownerpk heirpk inactivitytime heirname tokenid + if (tokenid != zeroid) + return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + else + return eval->Invalid("unexpected HeirValidate for heirfund"); + // break; + + case 'A': + // add funding coins: + // vins.*: normal inputs + // ------------------------ + // vout.0: funding CC 1of2 addr for the owner and heir + // vout.1: normal change + // vout.n-1: opreturn 'A' ownerpk heirpk inactivitytime fundingtx + + // add funding tokens: + // vins.0: normal inputs txfee + // vins.1+: user's CC addr inputs + // ------------------------ + // vout.0: funding CC 1of2 addr for the owner and heir + // vout.1: normal change + // vout.n-1: opreturn 't' tokenid 'A' ownerpk heirpk inactivitytime fundingtx + if (tokenid != zeroid) + return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + else + return eval->Invalid("unexpected HeirValidate for heiradd"); + //break; + + case 'C': + // claim coins: + // vin.0: normal input txfee + // vin.1+: input from CC 1of2 addr + // ------------------------------------- + // vout.0: normal output to owner or heir address + // vout.1: change to CC 1of2 addr + // vout.2: change to user's addr from txfee input if any + // vout.n-1: opreturn 'C' ownerpk heirpk inactivitytime fundingtx + + // claim tokens: + // vin.0: normal input txfee + // vin.1+: input from CC 1of2 addr + // -------------------------------------------- + // vout.0: output to user's cc address + // vout.1: change to CC 1of2 addr + // vout.2: change to normal from txfee input if any + // vout.n-1: opreturn 't' tokenid 'C' ownerpk heirpk inactivitytime fundingtx + if (tokenid != zeroid) + return RunValidationPlans(funcId, cpTokens, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + else + return RunValidationPlans(funcId, cpHeir, eval, tx, latestTxid, fundingTxOpRetScript, hasHeirSpendingBegun); + // break; + + default: + std::cerr << "HeirValidate() illegal heir funcid=" << (char)funcId << std::endl; + return eval->Invalid("unexpected HeirValidate funcid"); + // break; } return eval->Invalid("unexpected"); // (PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts)); } @@ -234,194 +234,194 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction // helper functions used in implementations of rpc calls (in rpcwallet.cpp) or validation code /** -* Checks if vout is to cryptocondition address -* @return vout value in satoshis -*/ + * Checks if vout is to cryptocondition address + * @return vout value in satoshis + */ template int64_t IsHeirFundingVout(struct CCcontract_info* cp, const CTransaction& tx, int32_t voutIndex, CPubKey ownerPubkey, CPubKey heirPubkey) { - char destaddr[65], heirFundingAddr[65]; - - Helper::GetCoinsOrTokensCCaddress1of2(cp, heirFundingAddr, ownerPubkey, heirPubkey); - if (tx.vout[voutIndex].scriptPubKey.IsPayToCryptoCondition() != 0) { - // NOTE: dimxy it was unsafe 'Getscriptaddress(destaddr,tx.vout[voutIndex].scriptPubKey) > 0' here: - if (Getscriptaddress(destaddr, tx.vout[voutIndex].scriptPubKey) && strcmp(destaddr, heirFundingAddr) == 0) - return (tx.vout[voutIndex].nValue); - else - std::cerr << "IsHeirFundingVout() heirFundingAddr=" << heirFundingAddr << " not equal to destaddr=" << destaddr << std::endl; - } - return (0); + char destaddr[65], heirFundingAddr[65]; + + Helper::GetCoinsOrTokensCCaddress1of2(cp, heirFundingAddr, ownerPubkey, heirPubkey); + if (tx.vout[voutIndex].scriptPubKey.IsPayToCryptoCondition() != 0) { + // NOTE: dimxy it was unsafe 'Getscriptaddress(destaddr,tx.vout[voutIndex].scriptPubKey) > 0' here: + if (Getscriptaddress(destaddr, tx.vout[voutIndex].scriptPubKey) && strcmp(destaddr, heirFundingAddr) == 0) + return (tx.vout[voutIndex].nValue); + else + std::cerr << "IsHeirFundingVout() heirFundingAddr=" << heirFundingAddr << " not equal to destaddr=" << destaddr << std::endl; + } + return (0); } // makes coin initial tx opret CScript EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - uint8_t evalcode = EVAL_HEIR; - - return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName); + uint8_t evalcode = EVAL_HEIR; + + return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName); } // makes coin additional tx opret CScript EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpendingBegun) { - uint8_t evalcode = EVAL_HEIR; - - fundingtxid = revuint256(fundingtxid); - return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); + uint8_t evalcode = EVAL_HEIR; + + fundingtxid = revuint256(fundingtxid); + return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); } // decode opret vout for Heir contract uint8_t _DecodeHeirOpRet(std::vector vopret, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - uint8_t evalCodeInOpret = 0; - uint8_t heirFuncId = 0; - - fundingTxidInOpret = zeroid; //to init - - evalCodeInOpret = vopret.begin()[0]; - - if (vopret.size() > 1 && evalCodeInOpret == EVAL_HEIR) { - // NOTE: it unmarshals for all F, A and C - uint8_t heirFuncId = 0; - hasHeirSpendingBegun = 0; - - bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; \ - if (heirFuncId == 'F') { \ - ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; \ - } \ - else { \ - ss >> fundingTxidInOpret >> hasHeirSpendingBegun; \ - } \ - }); - - if (!result) { - if (!noLogging) std::cerr << "_DecodeHeirOpRet() could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl; - return (uint8_t)0; - } - - /* std::cerr << "DecodeHeirOpRet()" - << " heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') - << " ownerPubkey=" << HexStr(ownerPubkey) - << " heirPubkey=" << HexStr(heirPubkey) - << " heirName=" << heirName << " inactivityTime=" << inactivityTime - << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << std::endl; */ - - if (isMyFuncId(heirFuncId)) { - fundingTxidInOpret = revuint256(fundingTxidInOpret); - return heirFuncId; - } - else { - if(!noLogging) std::cerr << "_DecodeHeirOpRet() unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; - } - } - else { - if (!noLogging) std::cerr << "_DecodeHeirOpRet() not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; - } - return (uint8_t)0; + uint8_t evalCodeInOpret = 0; + uint8_t heirFuncId = 0; + + fundingTxidInOpret = zeroid; //to init + + evalCodeInOpret = vopret.begin()[0]; + + if (vopret.size() > 1 && evalCodeInOpret == EVAL_HEIR) { + // NOTE: it unmarshals for all F, A and C + uint8_t heirFuncId = 0; + hasHeirSpendingBegun = 0; + + bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; \ + if (heirFuncId == 'F') { \ + ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; \ + } \ + else { \ + ss >> fundingTxidInOpret >> hasHeirSpendingBegun; \ + } \ + }); + + if (!result) { + if (!noLogging) std::cerr << "_DecodeHeirOpRet() could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl; + return (uint8_t)0; + } + + /* std::cerr << "DecodeHeirOpRet()" + << " heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') + << " ownerPubkey=" << HexStr(ownerPubkey) + << " heirPubkey=" << HexStr(heirPubkey) + << " heirName=" << heirName << " inactivityTime=" << inactivityTime + << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << std::endl; */ + + if (isMyFuncId(heirFuncId)) { + fundingTxidInOpret = revuint256(fundingTxidInOpret); + return heirFuncId; + } + else { + if(!noLogging) std::cerr << "_DecodeHeirOpRet() unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; + } + } + else { + if (!noLogging) std::cerr << "_DecodeHeirOpRet() not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; + } + return (uint8_t)0; } /* not used, see DecodeHeirOpRet(vopret,...) -// overload for 'F' opret -uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) -{ + // overload for 'F' opret + uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) + { uint256 dummytxid; uint8_t dummyHasHeirSpendingBegun; std::vector vopret; - + GetOpReturnData(scriptPubKey, vopret); if (vopret.size() == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; - return (uint8_t)0; + if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; + return (uint8_t)0; } return _DecodeHeirOpRet(vopret, ownerPubkey, heirPubkey, inactivityTime, heirName, dummytxid, dummyHasHeirSpendingBegun, noLogging); -}*/ + }*/ /* not used, see DecodeHeirOpRet(vopret,...) -// overload for A, C oprets and AddHeirContractInputs -uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) -{ + // overload for A, C oprets and AddHeirContractInputs + uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) + { CPubKey dummyOwnerPubkey, dummyHeirPubkey; int64_t dummyInactivityTime; std::string dummyHeirName; std::vector vopret; - + GetOpReturnData(scriptPubKey, vopret); if (vopret.size() == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; - return (uint8_t)0; + if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; + return (uint8_t)0; } - + return _DecodeHeirOpRet(vopret, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingtxidInOpret, hasHeirSpendingBegun, noLogging); -} */ + } */ // decode combined opret: uint8_t _DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - uint8_t evalCodeTokens = 0; - std::vector voutPubkeysDummy; - std::vector vopretExtra, vopretStripped; - - if (DecodeTokenOpRet(scriptPubKey, evalCodeTokens, tokenid, voutPubkeysDummy, vopretExtra) != 0) { - if (vopretExtra.size() > 1) { - // restore the second opret: - - if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size - if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() could not unmarshal vopretStripped" << std::endl; - return (uint8_t)0; - } - } - else { - if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; - return (uint8_t)0; - } - } - else - GetOpReturnData(scriptPubKey, vopretStripped); - - return _DecodeHeirOpRet(vopretStripped, ownerPubkey, heirPubkey, inactivityTime, heirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); - + uint8_t evalCodeTokens = 0; + std::vector voutPubkeysDummy; + std::vector vopretExtra, vopretStripped; + + if (DecodeTokenOpRet(scriptPubKey, evalCodeTokens, tokenid, voutPubkeysDummy, vopretExtra) != 0) { + if (vopretExtra.size() > 1) { + // restore the second opret: + + if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size + if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() could not unmarshal vopretStripped" << std::endl; + return (uint8_t)0; + } + } + else { + if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; + return (uint8_t)0; + } + } + else + GetOpReturnData(scriptPubKey, vopretStripped); + + return _DecodeHeirOpRet(vopretStripped, ownerPubkey, heirPubkey, inactivityTime, heirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); + } // overload to decode opret in fundingtxid: uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) { - uint256 dummyFundingTxidInOpret; - uint8_t dummyHasHeirSpendingBegun; - - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); + uint256 dummyFundingTxidInOpret; + uint8_t dummyHasHeirSpendingBegun; + + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); } // overload to decode opret in A and C heir tx: uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - CPubKey dummyOwnerPubkey, dummyHeirPubkey; - int64_t dummyInactivityTime; - std::string dummyHeirName; - - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); + CPubKey dummyOwnerPubkey, dummyHeirPubkey; + int64_t dummyInactivityTime; + std::string dummyHeirName; + + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); } /** - * find the latest funding tx: it may be the first F tx or one of A or C tx's - * Note: this function is also called from validation code (use non-locking calls) + * find the latest funding tx: it may be the first F tx or one of A or C tx's + * Note: this function is also called from validation code (use non-locking calls) */ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, CScript& fundingOpretScript, uint8_t &hasHeirSpendingBegun) { - CTransaction fundingtx; - uint256 hashBlock; + CTransaction fundingtx; + uint256 hashBlock; const bool allowSlow = false; - + //char markeraddr[64]; //CCtxidaddr(markeraddr, fundingtxid); //SetCCunspents(unspentOutputs, markeraddr); - - hasHeirSpendingBegun = 0; - funcId = 0; - + + hasHeirSpendingBegun = 0; + funcId = 0; + // get initial funding tx and set it as initial lasttx: if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { - - CScript heirScript = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + + CScript heirScript = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); if (funcId != 0) { // found at least funding tx! //std::cerr << "FindLatestFundingTx() lasttx currently is fundingtx, txid=" << fundingtxid.GetHex() << " opreturn type=" << (char)funcId << '\n'; @@ -434,55 +434,55 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke std::cerr << "FindLatestFundingTx() could not find funding tx for fundingtxid=" << fundingtxid.GetHex() << '\n'; return zeroid; } - - // TODO: correct cc addr: + + // TODO: correct cc addr: std::vector> unspentOutputs; struct CCcontract_info *cp, C; cp = CCinit(&C, EVAL_HEIR); char coinaddr[64]; GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' - + SetCCunspents(unspentOutputs, coinaddr); // get vector with tx's with unspent vouts of 1of2pubkey address: //std::cerr << "FindLatestFundingTx() using 1of2address=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; - + int32_t maxBlockHeight = 0; // max block height uint256 latesttxid = fundingtxid; - + // try to find the last funding or spending tx by checking fundingtxid in 'opreturn': for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { CTransaction regtx; uint256 hash; - + uint256 txid = it->first.txhash; //std::cerr << "FindLatestFundingTx() checking unspents for txid=" << txid.GetHex() << '\n'; - + int32_t blockHeight = (int32_t)it->second.blockHeight; - - //NOTE: maybe called from validation code: - if (myGetTransaction(txid, regtx, hash)) { - //std::cerr << "FindLatestFundingTx() found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; - uint256 fundingTxidInOpret; - uint256 tokenidInOpret; // not to contaminate the tokenid from the params! - uint8_t tmpFuncId; - uint8_t hasHeirSpendingBegunInOpret; - - CScript heirScript = (regtx.vout.size() > 0) ? regtx.vout[regtx.vout.size() - 1].scriptPubKey : CScript(); - tmpFuncId = DecodeHeirEitherOpRet(heirScript, tokenidInOpret, fundingTxidInOpret, hasHeirSpendingBegunInOpret, true); + + //NOTE: maybe called from validation code: + if (myGetTransaction(txid, regtx, hash)) { + //std::cerr << "FindLatestFundingTx() found tx for txid=" << txid.GetHex() << " blockHeight=" << blockHeight << " maxBlockHeight=" << maxBlockHeight << '\n'; + uint256 fundingTxidInOpret; + uint256 tokenidInOpret; // not to contaminate the tokenid from the params! + uint8_t tmpFuncId; + uint8_t hasHeirSpendingBegunInOpret; + + CScript heirScript = (regtx.vout.size() > 0) ? regtx.vout[regtx.vout.size() - 1].scriptPubKey : CScript(); + tmpFuncId = DecodeHeirEitherOpRet(heirScript, tokenidInOpret, fundingTxidInOpret, hasHeirSpendingBegunInOpret, true); if (tmpFuncId != 0 && fundingtxid == fundingTxidInOpret && (tokenid == zeroid || tokenid == tokenidInOpret)) { // check tokenid also - + if (blockHeight > maxBlockHeight) { maxBlockHeight = blockHeight; latesttxid = txid; - funcId = tmpFuncId; - hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; - - //std::cerr << "FindLatestFundingTx() txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight - // << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; + funcId = tmpFuncId; + hasHeirSpendingBegun = hasHeirSpendingBegunInOpret; + + //std::cerr << "FindLatestFundingTx() txid=" << latesttxid.GetHex() << " at blockHeight=" << maxBlockHeight + // << " opreturn type=" << (char)(funcId ? funcId : ' ') << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << " - set as current lasttxid" << '\n'; } } } } - + return latesttxid; } @@ -494,7 +494,7 @@ uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRe CPubKey heirPubkey; int64_t inactivityTime; std::string heirName; - + return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, opRetScript, hasHeirSpendingBegun); } @@ -502,7 +502,7 @@ uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRe uint256 FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint8_t &hasHeirSpendingBegun) { CScript opRetScript; - + return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, opRetScript, hasHeirSpendingBegun); } @@ -514,41 +514,41 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, CTransaction heirtx; int32_t n = 0; std::vector> unspentOutputs; - + char coinaddr[64]; - Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 of 2 pubkeys' + Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 of 2 pubkeys' SetCCunspents(unspentOutputs, coinaddr); - + // char markeraddr[64]; // CCtxidaddr(markeraddr, fundingtxid); // SetCCunspents(unspentOutputs, markeraddr); - + std::cerr << "Add1of2AddressInputs() using 1of2addr=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << std::endl; - + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { uint256 txid = it->first.txhash; uint256 hashBlock; int32_t voutIndex = (int32_t)it->first.index; // no need to prevent dup // dimxy: maybe it is good to put tx's in cache? - - std::cerr << "Add1of2AddressInputs() txid=" << txid.GetHex() << std::endl; - + + std::cerr << "Add1of2AddressInputs() txid=" << txid.GetHex() << std::endl; + if (GetTransaction(txid, heirtx, hashBlock, false) != 0) { - uint256 tokenid; + uint256 tokenid; uint256 fundingTxidInOpret; - uint8_t hasHeirSpendingBegunDummy; - - CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); - - if ((txid == fundingtxid || fundingTxidInOpret == fundingtxid) && - funcId != 0 && - isMyFuncId(funcId) && - (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, voutIndex, tokenid) > 0) && // token validation logic + uint8_t hasHeirSpendingBegunDummy; + + CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); + + if ((txid == fundingtxid || fundingTxidInOpret == fundingtxid) && + funcId != 0 && + isMyFuncId(funcId) && + (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, voutIndex, tokenid) > 0) && // token validation logic //(voutValue = IsHeirFundingVout(cp, heirtx, voutIndex, ownerPubkey, heirPubkey)) > 0 && // heir contract vout validation logic - not used since we moved to 2-eval vouts - !myIsutxo_spentinmempool(txid, voutIndex)) - { + !myIsutxo_spentinmempool(txid, voutIndex)) + { std::cerr << "Add1of2AddressInputs() satoshis=" << it->second.satoshis << std::endl; if (total != 0 && maxinputs != 0) mtx.vin.push_back(CTxIn(txid, voutIndex, CScript())); @@ -564,42 +564,42 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, } /** - * enumerate all tx's sending to CCHeir 1of2address and calc total lifetime funds + * enumerate all tx's sending to CCHeir 1of2address and calc total lifetime funds */ template int64_t LifetimeHeirContractFunds(struct CCcontract_info* cp, uint256 fundingtxid, CPubKey ownerPubkey, CPubKey heirPubkey) { char coinaddr[64]; Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' - + std::vector> addressIndexes; SetCCtxids(addressIndexes, coinaddr); - + //fprintf(stderr,"LifetimeHeirContractFunds() scan lifetime of %s\n",coinaddr); int64_t total = 0; for (std::vector>::const_iterator it = addressIndexes.begin(); it != addressIndexes.end(); it++) { uint256 hashBlock; uint256 txid = it->first.txhash; CTransaction heirtx; - - // TODO: check all funding tx should contain unspendable markers + + // TODO: check all funding tx should contain unspendable markers if (GetTransaction(txid, heirtx, hashBlock, false) && heirtx.vout.size() > 0) { - uint256 tokenid; + uint256 tokenid; uint256 fundingTxidInOpret; - uint8_t hasHeirSpendingBegunDummy; + uint8_t hasHeirSpendingBegunDummy; const int32_t ivout = 0; - - CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); - + + CScript heirScript = (heirtx.vout.size() > 0) ? heirtx.vout[heirtx.vout.size() - 1].scriptPubKey : CScript(); // check boundary + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, fundingTxidInOpret, hasHeirSpendingBegunDummy, false); + //std::cerr << "LifetimeHeirContractFunds() found tx=" << txid.GetHex() << " vout[0].nValue=" << subtx.vout[ccVoutIdx].nValue << " opreturn=" << (char)funcId << '\n'; - - if (funcId != 0 && - (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && - isMyFuncId(funcId) && !isSpendingTx(funcId) && - (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, ivout, tokenid) > 0) && - !myIsutxo_spentinmempool(txid, ivout)) // exclude tx in mempool + + if (funcId != 0 && + (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && + isMyFuncId(funcId) && !isSpendingTx(funcId) && + (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, ivout, tokenid) > 0) && + !myIsutxo_spentinmempool(txid, ivout)) // exclude tx in mempool { - total += it->second; // dont do this: tx.vout[ivout].nValue; // in vin[0] always is the pay to 1of2 addr (funding or change) + total += it->second; // dont do this: tx.vout[ivout].nValue; // in vin[0] always is the pay to 1of2 addr (funding or change) //std::cerr << "LifetimeHeirContractFunds() added tx=" << txid.GetHex() << " it->second=" << it->second << " vout[0].nValue=" << tx.vout[ivout].nValue << " opreturn=" << (char)funcId << '\n'; } } @@ -611,90 +611,90 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info /** * heirfund rpc call implementation - * creates tx for initial funds deposit on cryptocondition address which locks funds for spending by either of address. + * creates tx for initial funds deposit on cryptocondition address which locks funds for spending by either of address. * and also for setting spending plan for the funds' owner and heir * @return fundingtxid handle for subsequent references to this heir funding plan */ template UniValue HeirFund(uint64_t txfee, int64_t amount, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { - UniValue result(UniValue::VOBJ); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp, C; - + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cp, C; + cp = CCinit(&C, Helper::getMyEval()); if (txfee == 0) txfee = 10000; - + //std::cerr << "HeirFund() amount=" << amount << " txfee=" << txfee << " heirPubkey IsValid()=" << heirPubkey.IsValid() << " inactivityTime(sec)=" << inactivityTimeSec << " tokenid=" << tokenid.GetHex() << std::endl; - - if (!heirPubkey.IsValid()) { - std::cerr << "HeirFund() heirPubkey is not valid!" << std::endl; - return std::string(""); - } - + + if (!heirPubkey.IsValid()) { + std::cerr << "HeirFund() heirPubkey is not valid!" << std::endl; + return std::string(""); + } + CPubKey myPubkey = pubkey2pk(Mypubkey()); - - if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners - int64_t inputs, change; - - if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, (int32_t)64)) > 0) { // 2 x txfee: 1st for marker vout, 2nd to miners - //mtx.vout.push_back(MakeTokensCC1of2vout(/*Helper::getMyEval()*/EVAL_HEIR, amount, myPubkey, heirPubkey)); // add cryptocondition to spend amount for either pk - mtx.vout.push_back(Helper::make1of2Vout(amount, myPubkey, heirPubkey)); - - // add a marker for finding all plans in HeirList() - // TODO: change marker either to cc or normal txidaddr unspendable - CPubKey heirUnspendablePubKey = GetUnspendable(cp, 0); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(heirUnspendablePubKey)) << OP_CHECKSIG)); // TODO: do we need this marker? - - // calc and add change vout: - if (inputs > amount) - change = (inputs - amount); // -txfee <-- txfee pays user - - //std::cerr << "HeirFund() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; - - if (change != 0) { // vout[1] - mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); - } - - // add 1of2 vout validation pubkeys: - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(myPubkey); - voutTokenPubkeys.push_back(heirPubkey); - - // add change for txfee and opreturn vouts and sign tx: - std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - Helper::makeCreateOpRet(tokenid, voutTokenPubkeys, myPubkey, heirPubkey, inactivityTimeSec, heirName)); - if (!rawhextx.empty()) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); - } - else { - std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "sign error")); - } - } - else { // TODO: need result return unification with heiradd and claim - std::cerr << "HeirFund() could not find owner cc inputs" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find owner cc inputs")); - } - } - else { - std::cerr << "HeirFund() could not find normal inputs" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find normal inputs")); - } + + if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners + int64_t inputs, change; + + if ((inputs=Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, (int32_t)64)) > 0) { // 2 x txfee: 1st for marker vout, 2nd to miners + //mtx.vout.push_back(MakeTokensCC1of2vout(/*Helper::getMyEval()*/EVAL_HEIR, amount, myPubkey, heirPubkey)); // add cryptocondition to spend amount for either pk + mtx.vout.push_back(Helper::make1of2Vout(amount, myPubkey, heirPubkey)); + + // add a marker for finding all plans in HeirList() + // TODO: change marker either to cc or normal txidaddr unspendable + CPubKey heirUnspendablePubKey = GetUnspendable(cp, 0); + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(heirUnspendablePubKey)) << OP_CHECKSIG)); // TODO: do we need this marker? + + // calc and add change vout: + if (inputs > amount) + change = (inputs - amount); // -txfee <-- txfee pays user + + //std::cerr << "HeirFund() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; + + if (change != 0) { // vout[1] + mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); + } + + // add 1of2 vout validation pubkeys: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(myPubkey); + voutTokenPubkeys.push_back(heirPubkey); + + // add change for txfee and opreturn vouts and sign tx: + std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, + Helper::makeCreateOpRet(tokenid, voutTokenPubkeys, myPubkey, heirPubkey, inactivityTimeSec, heirName)); + if (!rawhextx.empty()) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hextx", rawhextx)); + } + else { + std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "sign error")); + } + } + else { // TODO: need result return unification with heiradd and claim + std::cerr << "HeirFund() could not find owner cc inputs" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find owner cc inputs")); + } + } + else { + std::cerr << "HeirFund() could not find normal inputs" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find normal inputs")); + } return result; } -// if no these callers - it could not link +// if no these callers - it could not link UniValue HeirFundCoinCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid){ - return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); + return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); } UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { - return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); + return HeirFund(txfee, funds, heirName, heirPubkey, inactivityTimeSec, tokenid); } /** @@ -704,113 +704,113 @@ UniValue HeirFundTokenCaller(uint64_t txfee, int64_t funds, std::string heirName */ template UniValue _HeirAdd(uint256 fundingtxid, uint64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) { - UniValue result(UniValue::VOBJ); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); int64_t inputs, CCchange = 0; struct CCcontract_info *cp, C; std::string rawhex; - - cp = CCinit(&C, Helper::getMyEval()); // for tokens shoud be EVAL_TOKENS to sign it correctly! - + + cp = CCinit(&C, Helper::getMyEval()); // for tokens shoud be EVAL_TOKENS to sign it correctly! + if (txfee == 0) txfee = 10000; - + CPubKey myPubkey = pubkey2pk(Mypubkey()); - + // check if it is the owner if (myPubkey != ownerPubkey) { result.push_back(Pair("result", "error")); result.push_back(Pair("error", "adding funds is only allowed for the owner of this contract")); return result; } - - if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners - - int64_t inputs, change; - - if ((inputs = Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, 64)) > 0) { // TODO: why 64 max inputs? - - // we do not use markers anymore - storing data in opreturn is better - // add marker vout: - /* char markeraddr[64]; - CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners - std::cerr << "HeirAdd() adding markeraddr=" << markeraddr << '\n'; */ - - // add cryptocondition to spend this funded amount for either pk - mtx.vout.push_back(Helper::make1of2Vout(amount, ownerPubkey, heirPubkey)); - - if (inputs > amount) - change = (inputs - amount); // -txfee <-- txfee pays user - - //std::cerr << "HeirAdd() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; - - if (change != 0) { // vout[1] - mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); - } - - // add 1of2 vout validation pubkeys - needed only for tokens: - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(ownerPubkey); - voutTokenPubkeys.push_back(heirPubkey); - - // add opreturn 'A' and sign tx: // this txfee ignored - std::string rawhextx = (FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - Helper::makeAddOpRet(tokenid, voutTokenPubkeys, fundingtxid, hasHeirSpendingBegun))); - - if (!rawhextx.empty()) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); - } - else { - std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "sign error")); - } - - } - else { - std::cerr << "HeirAdd cannot find owner cc inputs" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find owner cc inputs")); - } - } - else { - std::cerr << "HeirAdd cannot find normal inputs for tx fee" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find normal inputs for tx fee")); - } - - - + + if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { // txfee for miners + + int64_t inputs, change; + + if ((inputs = Helper::addOwnerInputs(tokenid, mtx, myPubkey, amount, 64)) > 0) { // TODO: why 64 max inputs? + + // we do not use markers anymore - storing data in opreturn is better + // add marker vout: + /* char markeraddr[64]; + CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners + std::cerr << "HeirAdd() adding markeraddr=" << markeraddr << '\n'; */ + + // add cryptocondition to spend this funded amount for either pk + mtx.vout.push_back(Helper::make1of2Vout(amount, ownerPubkey, heirPubkey)); + + if (inputs > amount) + change = (inputs - amount); // -txfee <-- txfee pays user + + //std::cerr << "HeirAdd() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; + + if (change != 0) { // vout[1] + mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); + } + + // add 1of2 vout validation pubkeys - needed only for tokens: + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(ownerPubkey); + voutTokenPubkeys.push_back(heirPubkey); + + // add opreturn 'A' and sign tx: // this txfee ignored + std::string rawhextx = (FinalizeCCTx(0, cp, mtx, myPubkey, txfee, + Helper::makeAddOpRet(tokenid, voutTokenPubkeys, fundingtxid, hasHeirSpendingBegun))); + + if (!rawhextx.empty()) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hextx", rawhextx)); + } + else { + std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "sign error")); + } + + } + else { + std::cerr << "HeirAdd cannot find owner cc inputs" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find owner cc inputs")); + } + } + else { + std::cerr << "HeirAdd cannot find normal inputs for tx fee" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find normal inputs for tx fee")); + } + + + return result; } UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTimeSec; - - uint256 latesttxid, tokenid = zeroid; - uint8_t funcId; - std::string heirName; - uint8_t hasHeirSpendingBegun = 0; - - if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { - if (tokenid == zeroid) - return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - else - return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - } - else { - UniValue result(UniValue::VOBJ); - - fprintf(stderr, "HeirAdd() can't find any heir CC funding tx's\n"); - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find any heir CC funding transactions")); - return result; - } + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTimeSec; + + uint256 latesttxid, tokenid = zeroid; + uint8_t funcId; + std::string heirName; + uint8_t hasHeirSpendingBegun = 0; + + if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { + if (tokenid == zeroid) + return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + else + return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + } + else { + UniValue result(UniValue::VOBJ); + + fprintf(stderr, "HeirAdd() can't find any heir CC funding tx's\n"); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find any heir CC funding transactions")); + return result; + } } @@ -821,110 +821,110 @@ UniValue HeirAddCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { */ template UniValue _HeirClaim(uint256 fundingtxid, uint64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) { - UniValue result(UniValue::VOBJ); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey myPubkey; + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey myPubkey; int64_t inputs, change = 0; struct CCcontract_info *cp, C; - - cp = CCinit(&C, EVAL_HEIR); - if (txfee == 0) + + cp = CCinit(&C, EVAL_HEIR); + if (txfee == 0) txfee = 10000; - + int32_t numblocks; uint64_t durationSec = 0; - - // we do not need to find duration if spending already has begun - if (!hasHeirSpendingBegun) { + + // we do not need to find duration if spending already has begun + if (!hasHeirSpendingBegun) { durationSec = CCduration(numblocks, latesttxid); std::cerr << "HeirClaim() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << std::endl; } - + // spending is allowed if there is already spending tx or inactivity time //bool isAllowedToHeir = (funcId == 'C' || durationSec > inactivityTimeSec) ? true : false; - bool isAllowedToHeir = (hasHeirSpendingBegun || durationSec > inactivityTimeSec) ? true : false; + bool isAllowedToHeir = (hasHeirSpendingBegun || durationSec > inactivityTimeSec) ? true : false; myPubkey = pubkey2pk(Mypubkey()); - + // if it is the heir, check if spending not allowed to heir yet if (myPubkey == heirPubkey && !isAllowedToHeir) { result.push_back(Pair("result", "error")); result.push_back(Pair("error", "spending is not allowed yet for the heir")); return result; } - - // we do not use markers any more: - // we allow owner to spend funds at any time: - // if it is the owner, check if spending already allowed to heir - /* if (myPubkey == ownerPubkey && isAllowedToHeir) { - result.push_back(Pair("result", "spending is not already allowed for the owner")); - return result; - } */ - - // add spending txfee from the calling user + + // we do not use markers any more: + // we allow owner to spend funds at any time: + // if it is the owner, check if spending already allowed to heir + /* if (myPubkey == ownerPubkey && isAllowedToHeir) { + result.push_back(Pair("result", "spending is not already allowed for the owner")); + return result; + } */ + + // add spending txfee from the calling user if (AddNormalinputs(mtx, myPubkey, txfee, 3) > 0) { - - // add spending from cc 1of2 address - if ((inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, amount, 60)) >= amount) // TODO: why only 60 inputs? + + // add spending from cc 1of2 address + if ((inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, amount, 60)) >= amount) // TODO: why only 60 inputs? { - /*if (inputs < amount) { - std::cerr << "HeirClaim() cant find enough HeirCC 1of2 inputs, found=" << inputs << " required=" << amount << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find heir CC funding")); - - return result; - }*/ - - // add vout with amount to claiming address - mtx.vout.push_back(Helper::makeUserVout(amount, myPubkey)); // vout[0] - + /*if (inputs < amount) { + std::cerr << "HeirClaim() cant find enough HeirCC 1of2 inputs, found=" << inputs << " required=" << amount << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find heir CC funding")); + + return result; + }*/ + + // add vout with amount to claiming address + mtx.vout.push_back(Helper::makeUserVout(amount, myPubkey)); // vout[0] + // calc and add change vout: if (inputs > amount) - change = (inputs - amount); // -txfee <-- txfee pays user - + change = (inputs - amount); // -txfee <-- txfee pays user + //std::cerr << "HeirClaim() inputs=" << inputs << " amount=" << amount << " txfee=" << txfee << " change=" << change << '\n'; - - // change to 1of2 funding addr: + + // change to 1of2 funding addr: if (change != 0) { // vout[1] mtx.vout.push_back(Helper::make1of2Vout(change, ownerPubkey, heirPubkey)); // using always pubkeys from OP_RETURN in order to not mixing them up! } - + // add marker vout: /*char markeraddr[64]; - CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); - // NOTE: amount = 0 is not working: causes error code: -26, error message : 64 : dust - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners - std::cerr << "HeirClaim() adding markeraddr=" << markeraddr << '\n'; */ - - // get address of 1of2 cond + CPubKey markerpubkey = CCtxidaddr(markeraddr, fundingtxid); + // NOTE: amount = 0 is not working: causes error code: -26, error message : 64 : dust + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); // txfee 1, txfee 2 - for miners + std::cerr << "HeirClaim() adding markeraddr=" << markeraddr << '\n'; */ + + // get address of 1of2 cond char coinaddr[64]; Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); - - // retrieve priv key addresses for FinalizeCCtx: - uint8_t myprivkey[32]; + + // retrieve priv key addresses for FinalizeCCtx: + uint8_t myprivkey[32]; Myprivkey(myprivkey); - - // set pubkeys for finding 1of2 cc in FinalizeCCtx to sign it: - Helper::CCaddrCoinsOrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - - // add 1of2 vout validation pubkeys (this is for tokens): - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(ownerPubkey); - voutTokenPubkeys.push_back(heirPubkey); - + + // set pubkeys for finding 1of2 cc in FinalizeCCtx to sign it: + Helper::CCaddrCoinsOrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + + // add 1of2 vout validation pubkeys (this is for tokens): + std::vector voutTokenPubkeys; + voutTokenPubkeys.push_back(ownerPubkey); + voutTokenPubkeys.push_back(heirPubkey); + // add opreturn 'C' and sign tx: // this txfee will be ignored - std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - Helper::makeClaimOpRet(tokenid, voutTokenPubkeys, fundingtxid, (myPubkey == heirPubkey) ? 1 : hasHeirSpendingBegun)); // forward isHeirSpending to the next latest tx - - if (!rawhextx.empty()) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); - } - else { - std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "sign error")); - } - + std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, + Helper::makeClaimOpRet(tokenid, voutTokenPubkeys, fundingtxid, (myPubkey == heirPubkey) ? 1 : hasHeirSpendingBegun)); // forward isHeirSpending to the next latest tx + + if (!rawhextx.empty()) { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hextx", rawhextx)); + } + else { + std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "sign error")); + } + } else { fprintf(stderr, "HeirClaim() cant find Heir CC inputs\n"); result.push_back(Pair("result", "error")); @@ -935,187 +935,187 @@ template UniValue _HeirClaim(uint256 fundingtxid, uint64_t txfe result.push_back(Pair("result", "error")); result.push_back(Pair("error", "can't find sufficient user inputs to pay transaction fee")); } - - + + return result; } UniValue HeirClaimCaller(uint256 fundingtxid, uint64_t txfee, int64_t amount) { - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTimeSec; - - uint256 latesttxid, tokenid = zeroid; - uint8_t funcId; - std::string heirName; - uint8_t hasHeirSpendingBegun = 0; - - if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { - if( tokenid == zeroid ) - return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - else - return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - - } - else { - UniValue result(UniValue::VOBJ); - - fprintf(stderr, "HeirClaim() can't find any heir CC funding tx's\n"); - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "can't find any heir CC funding transactions")); - return result; - } + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTimeSec; + + uint256 latesttxid, tokenid = zeroid; + uint8_t funcId; + std::string heirName; + uint8_t hasHeirSpendingBegun = 0; + + if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { + if( tokenid == zeroid ) + return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + else + return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + + } + else { + UniValue result(UniValue::VOBJ); + + fprintf(stderr, "HeirClaim() can't find any heir CC funding tx's\n"); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "can't find any heir CC funding transactions")); + return result; + } } /** * heirinfo rpc call implementation - * returns some information about heir CC contract plan by a handle of initial fundingtxid: + * returns some information about heir CC contract plan by a handle of initial fundingtxid: * plan name, owner and heir pubkeys, funds deposited and available, flag if spending is enabled for the heir * @return heir info data */ UniValue HeirInfo(uint256 fundingtxid) { UniValue result(UniValue::VOBJ); - - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction fundingtx; - uint256 hashBlock; - const bool allowSlow = false; - - //char markeraddr[64]; - //CCtxidaddr(markeraddr, fundingtxid); - //SetCCunspents(unspentOutputs, markeraddr); - - // get initial funding tx and set it as initial lasttx: - if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { - - CPubKey ownerPubkey, heirPubkey; - uint256 dummyTokenid, tokenid = zeroid; // important to clear tokenid - std::string heirName; - int64_t inactivityTimeSec; - const bool noLogging = false; - uint8_t funcId; - - /*CScript opret = fundingtx.vout.size() > 0 ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); - if (funcId == 0) { - std::cerr << "HeirInfo() this fundingtx is incorrect" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "initial tx F not found")); - return result; - }*/ - - struct CCcontract_info *cp, C; - cp = CCinit(&C, EVAL_HEIR); - - uint8_t hasHeirSpendingBegun = 0; - - uint256 latestFundingTxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); - - if (latestFundingTxid != zeroid) { - int32_t numblocks; - uint64_t durationSec = 0; - - //std::cerr << "HeirInfo() latesttxid=" << latestFundingTxid.GetHex() << '\n'; - - std::ostringstream stream; - std::string msg; - - result.push_back(Pair("fundingtxid", fundingtxid.GetHex())); - result.push_back(Pair("name", heirName.c_str())); - - if (tokenid != zeroid) { // tokens - stream << tokenid.GetHex(); - msg = "tokenid"; - result.push_back(Pair(msg, stream.str().c_str())); - stream.str(""); - stream.clear(); - } - - char hexbuf[67]; - stream << pubkey33_str(hexbuf, (uint8_t*)ownerPubkey.begin()); - result.push_back(Pair("owner", stream.str().c_str())); - stream.str(""); - stream.clear(); - - stream << pubkey33_str(hexbuf, (uint8_t*)heirPubkey.begin()); - result.push_back(Pair("heir", stream.str().c_str())); - stream.str(""); - stream.clear(); - - int64_t total; - if (tokenid == zeroid) - total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); - else - total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); - - if (tokenid == zeroid) { - msg = "funding total in coins"; - stream << (double)total / COIN; - } - else { - msg = "funding total in tokens"; - stream << total; - } - result.push_back(Pair(msg, stream.str().c_str())); - stream.str(""); - stream.clear(); - - int64_t inputs; - if (tokenid == zeroid) - inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); //NOTE: amount = 0 means all unspent inputs - else - inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); - - if (tokenid == zeroid) { - msg = "funding available in coins"; - stream << (double)inputs / COIN; - } - else { - msg = "funding available in tokens"; - stream << inputs; - } - result.push_back(Pair(msg, stream.str().c_str())); - stream.str(""); - stream.clear(); - - if (tokenid != zeroid) { - int64_t ownerInputs = TokenHelper::addOwnerInputs(tokenid, mtx, ownerPubkey, 0, (int32_t)64); - stream << ownerInputs; - msg = "owner funding available in tokens"; - result.push_back(Pair(msg, stream.str().c_str())); - stream.str(""); - stream.clear(); - } - - stream << inactivityTimeSec; - result.push_back(Pair("inactivity time setting", stream.str().c_str())); - stream.str(""); - stream.clear(); - - if (!hasHeirSpendingBegun) { // we do not need find duration if the spending already has begun - durationSec = CCduration(numblocks, latestFundingTxid); - std::cerr << "HeirInfo() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; - } - - stream << std::boolalpha << (hasHeirSpendingBegun || durationSec > inactivityTimeSec); - result.push_back(Pair("spending allowed for the heir", stream.str().c_str())); - stream.str(""); - stream.clear(); - - result.push_back(Pair("result", "success")); - } - else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find heir cc plan for this txid")); - } - } - else { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "could not find heir cc plan for this txid (no initial tx)")); - } - return (result); + + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction fundingtx; + uint256 hashBlock; + const bool allowSlow = false; + + //char markeraddr[64]; + //CCtxidaddr(markeraddr, fundingtxid); + //SetCCunspents(unspentOutputs, markeraddr); + + // get initial funding tx and set it as initial lasttx: + if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { + + CPubKey ownerPubkey, heirPubkey; + uint256 dummyTokenid, tokenid = zeroid; // important to clear tokenid + std::string heirName; + int64_t inactivityTimeSec; + const bool noLogging = false; + uint8_t funcId; + + /*CScript opret = fundingtx.vout.size() > 0 ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); + if (funcId == 0) { + std::cerr << "HeirInfo() this fundingtx is incorrect" << std::endl; + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "initial tx F not found")); + return result; + }*/ + + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_HEIR); + + uint8_t hasHeirSpendingBegun = 0; + + uint256 latestFundingTxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + + if (latestFundingTxid != zeroid) { + int32_t numblocks; + uint64_t durationSec = 0; + + //std::cerr << "HeirInfo() latesttxid=" << latestFundingTxid.GetHex() << '\n'; + + std::ostringstream stream; + std::string msg; + + result.push_back(Pair("fundingtxid", fundingtxid.GetHex())); + result.push_back(Pair("name", heirName.c_str())); + + if (tokenid != zeroid) { // tokens + stream << tokenid.GetHex(); + msg = "tokenid"; + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + } + + char hexbuf[67]; + stream << pubkey33_str(hexbuf, (uint8_t*)ownerPubkey.begin()); + result.push_back(Pair("owner", stream.str().c_str())); + stream.str(""); + stream.clear(); + + stream << pubkey33_str(hexbuf, (uint8_t*)heirPubkey.begin()); + result.push_back(Pair("heir", stream.str().c_str())); + stream.str(""); + stream.clear(); + + int64_t total; + if (tokenid == zeroid) + total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); + else + total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); + + if (tokenid == zeroid) { + msg = "funding total in coins"; + stream << (double)total / COIN; + } + else { + msg = "funding total in tokens"; + stream << total; + } + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + + int64_t inputs; + if (tokenid == zeroid) + inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); //NOTE: amount = 0 means all unspent inputs + else + inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); + + if (tokenid == zeroid) { + msg = "funding available in coins"; + stream << (double)inputs / COIN; + } + else { + msg = "funding available in tokens"; + stream << inputs; + } + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + + if (tokenid != zeroid) { + int64_t ownerInputs = TokenHelper::addOwnerInputs(tokenid, mtx, ownerPubkey, 0, (int32_t)64); + stream << ownerInputs; + msg = "owner funding available in tokens"; + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + } + + stream << inactivityTimeSec; + result.push_back(Pair("inactivity time setting", stream.str().c_str())); + stream.str(""); + stream.clear(); + + if (!hasHeirSpendingBegun) { // we do not need find duration if the spending already has begun + durationSec = CCduration(numblocks, latestFundingTxid); + std::cerr << "HeirInfo() duration=" << durationSec << " inactivityTime=" << inactivityTimeSec << " numblocks=" << numblocks << '\n'; + } + + stream << std::boolalpha << (hasHeirSpendingBegun || durationSec > inactivityTimeSec); + result.push_back(Pair("spending allowed for the heir", stream.str().c_str())); + stream.str(""); + stream.clear(); + + result.push_back(Pair("result", "success")); + } + else { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find heir cc plan for this txid")); + } + } + else { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not find heir cc plan for this txid (no initial tx)")); + } + return (result); } /** @@ -1125,47 +1125,47 @@ UniValue HeirInfo(uint256 fundingtxid) template void _HeirList(struct CCcontract_info *cp, UniValue &result) { - std::vector> unspentOutputs; - char coinaddr[64]; - CPubKey ccPubKeyEmpty; - GetCCaddress(cp, coinaddr, ccPubKeyEmpty); - SetCCunspents(unspentOutputs, cp->normaladdr); - - //std::cerr << "HeirList() finding heir marker from Heir contract addr=" << cp->normaladdr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; - - // TODO: move marker to special cc addr to prevent checking all tokens - for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { - uint256 hashBlock; - uint256 txid = it->first.txhash; - uint256 tokenid; - int32_t vout = (int32_t)it->first.index; - - //std::cerr << "HeirList() checking txid=" << txid.GetHex() << " vout=" << vout << '\n'; - - CTransaction fundingtx; - if (GetTransaction(txid, fundingtx, hashBlock, false)) { - CPubKey ownerPubkey, heirPubkey; - std::string heirName; - int64_t inactivityTimeSec; - const bool noLogging = true; - uint256 tokenid; - - CScript opret = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); - - // note: if it is not Heir token funcId would be equal to 0 - if (funcId == 'F') { - //result.push_back(Pair("fundingtxid kind name", txid.GetHex() + std::string(" ") + (typeid(Helper) == typeid(TokenHelper) ? std::string("token") : std::string("coin")) + std::string(" ") + heirName)); - result.push_back( Pair("fundingtxid", txid.GetHex()) ); - } - else { - std::cerr << "HeirList() this is not the initial F transaction=" << txid.GetHex() << std::endl; - } - } - else { - std::cerr << "HeirList() could not load transaction=" << txid.GetHex() << std::endl; - } - } + std::vector> unspentOutputs; + char coinaddr[64]; + CPubKey ccPubKeyEmpty; + GetCCaddress(cp, coinaddr, ccPubKeyEmpty); + SetCCunspents(unspentOutputs, cp->normaladdr); + + //std::cerr << "HeirList() finding heir marker from Heir contract addr=" << cp->normaladdr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; + + // TODO: move marker to special cc addr to prevent checking all tokens + for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { + uint256 hashBlock; + uint256 txid = it->first.txhash; + uint256 tokenid; + int32_t vout = (int32_t)it->first.index; + + //std::cerr << "HeirList() checking txid=" << txid.GetHex() << " vout=" << vout << '\n'; + + CTransaction fundingtx; + if (GetTransaction(txid, fundingtx, hashBlock, false)) { + CPubKey ownerPubkey, heirPubkey; + std::string heirName; + int64_t inactivityTimeSec; + const bool noLogging = true; + uint256 tokenid; + + CScript opret = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); + + // note: if it is not Heir token funcId would be equal to 0 + if (funcId == 'F') { + //result.push_back(Pair("fundingtxid kind name", txid.GetHex() + std::string(" ") + (typeid(Helper) == typeid(TokenHelper) ? std::string("token") : std::string("coin")) + std::string(" ") + heirName)); + result.push_back( Pair("fundingtxid", txid.GetHex()) ); + } + else { + std::cerr << "HeirList() this is not the initial F transaction=" << txid.GetHex() << std::endl; + } + } + else { + std::cerr << "HeirList() could not load transaction=" << txid.GetHex() << std::endl; + } + } } @@ -1173,15 +1173,15 @@ UniValue HeirList() { UniValue result(UniValue::VOBJ); result.push_back(Pair("result", "success")); - + struct CCcontract_info *cpHeir, *cpTokens, heirC, tokenC; // NOTE we must use a separate 'C' structure for each CCinit! - cpHeir = CCinit(&heirC, EVAL_HEIR); - cpTokens = CCinit(&tokenC, EVAL_TOKENS); - - _HeirList(cpHeir, result); - _HeirList(cpTokens, result); - - return result; + cpHeir = CCinit(&heirC, EVAL_HEIR); + cpTokens = CCinit(&tokenC, EVAL_TOKENS); + + _HeirList(cpHeir, result); + _HeirList(cpTokens, result); + + return result; } diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index d66777933..c4bb7ea60 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -22,100 +22,100 @@ inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); } // helper class to allow polymorphic behaviour for HeirXXX() functions in case of coins class CoinHelper { public: - - static uint8_t getMyEval() { return EVAL_HEIR; } - static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { - return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); - } - - static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); - } - static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); - } - static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); - } - static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { - return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); - } - static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { - return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); - } -/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { - return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); - } */ - static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { - struct CCcontract_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_HEIR); - return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); - } - static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - } + + static uint8_t getMyEval() { return EVAL_HEIR; } + static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { + return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); + } + + static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { + return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); + } + static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); + } + static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); + } + static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { + return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); + } + static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { + return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); + } + /* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { + return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); + } */ + static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); + } + static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { + CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + } }; // helper class to allow polymorphic behaviour for HeirXXX() functions in case of tokens class TokenHelper { public: - static uint8_t getMyEval() { return EVAL_TOKENS; } - static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { - struct CCcontract_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_TOKENS); - return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs); - } - - static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); - } - static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); - } - static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)); - } - - static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { - return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); - } - static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { - return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS - } -/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { - return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS - } */ - static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { - struct CCcontract_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_HEIR); - return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); - } - - static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - - CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - } + static uint8_t getMyEval() { return EVAL_TOKENS; } + static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_TOKENS); + return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs); + } + + static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); + } + static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); + } + static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)); + } + + static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { + return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); + } + static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { + return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS + } + /* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { + return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS + } */ + static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { + struct CCcontract_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); + } + + static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { + + CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + } }; /** -* Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern: -* It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators -* (These validators are derived from the class CValidatorBase). -* -* A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn. -* Or, vin validator may check if this vin depicts correctly to the CC contract's address. -* -* For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class. -* this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it. -* Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false. -* -* For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied -* (see constructors of both CInputValidator and COutputValidator) -*/ + * Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern: + * It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators + * (These validators are derived from the class CValidatorBase). + * + * A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn. + * Or, vin validator may check if this vin depicts correctly to the CC contract's address. + * + * For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class. + * this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it. + * Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false. + * + * For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied + * (see constructors of both CInputValidator and COutputValidator) + */ /** * base class for all validators @@ -123,13 +123,13 @@ public: class CValidatorBase { public: - CValidatorBase(CCcontract_info* cp) : m_cp(cp) {} - virtual bool isVinValidator() const = 0; - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const = 0; - virtual bool validateVout(CTxOut vout, std::string& message) const = 0; - + CValidatorBase(CCcontract_info* cp) : m_cp(cp) {} + virtual bool isVinValidator() const = 0; + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const = 0; + virtual bool validateVout(CTxOut vout, std::string& message) const = 0; + protected: - CCcontract_info * m_cp; + CCcontract_info * m_cp; }; /** @@ -138,494 +138,494 @@ protected: class CInputIdentifierBase { public: - CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {} - virtual std::string inputName() const = 0; - virtual bool identifyInput(CTxIn vin) const = 0; + CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {} + virtual std::string inputName() const = 0; + virtual bool identifyInput(CTxIn vin) const = 0; protected: - CCcontract_info * m_cp; + CCcontract_info * m_cp; }; /** -* Encapsulates an array containing rows of validators -* Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts -* this validation plan is used for validating tx inputs -*/ + * Encapsulates an array containing rows of validators + * Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts + * this validation plan is used for validating tx inputs + */ template class CInputValidationPlan { - using ValidatorsRow = std::vector; - + using ValidatorsRow = std::vector; + public: - - // Pushes a row of validators for validating a vin or vout - // @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs") - // @param pargs parameter pack of zero or more pointer to validator objects - // Why pointers? because we store the base class in validators' row and then call its virtual functions - template - void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... - { - ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); - m_arrayValidators.push_back(std::make_pair(identifier, vValidators)); - } - - // validate tx inputs and corresponding prev tx vouts - bool validate(const CTransaction& tx, Eval* eval) - { - std::string message = ""; - //std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl; - - int32_t ival = 0; - int32_t iv = 0; - int32_t numv = tx.vin.size(); - int32_t numValidators = m_arrayValidators.size(); - - // run over vins: - while (iv < numv && ival < numValidators) { - - int32_t identifiedCount = 0; - CInputIdentifierBase *identifier = m_arrayValidators[ival].first; - // check if this is 'our' input: - while (iv < numv && identifier->identifyInput(tx.vin[iv])) { - - // get prev tx: - CTransaction prevTx, *pPrevTxOrNull = NULL; - uint256 hashBlock; - - if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) { - std::ostringstream stream; - stream << "can't find vinTx for vin=" << iv << "."; - return eval->Invalid(stream.str().c_str()); - } - pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present) - - // exec 'validators' from validator row of ival index, for tx.vin[iv] - if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) { - std::ostringstream stream; - stream << "invalid tx vin[" << iv << "]:" << message; - return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' - } - - identifiedCount++; // how many vins we identified - iv++; // advance to the next vin - } - - // CInputValidationPlan treats that there must be at least one identified vin for configured validators' row - // like in 'vin.0: normal input' - if (identifiedCount == 0) { - std::ostringstream stream; - stream << "can't find required vins for " << identifier->inputName() << "."; - return eval->Invalid(stream.str().c_str()); - } - - ival++; // advance to the next validator row - // and it will try the same vin with the new CInputIdentifierBase and validators row - } - - // validation is successful if all validators have been used (i.e. ival = numValidators) - if (ival < numValidators) { - std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl; - return eval->Invalid("incorrect tx structure: not all required vins are present."); - } - - //std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl; - return true; - } - + + // Pushes a row of validators for validating a vin or vout + // @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs") + // @param pargs parameter pack of zero or more pointer to validator objects + // Why pointers? because we store the base class in validators' row and then call its virtual functions + template + void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... + { + ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); + m_arrayValidators.push_back(std::make_pair(identifier, vValidators)); + } + + // validate tx inputs and corresponding prev tx vouts + bool validate(const CTransaction& tx, Eval* eval) + { + std::string message = ""; + //std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl; + + int32_t ival = 0; + int32_t iv = 0; + int32_t numv = tx.vin.size(); + int32_t numValidators = m_arrayValidators.size(); + + // run over vins: + while (iv < numv && ival < numValidators) { + + int32_t identifiedCount = 0; + CInputIdentifierBase *identifier = m_arrayValidators[ival].first; + // check if this is 'our' input: + while (iv < numv && identifier->identifyInput(tx.vin[iv])) { + + // get prev tx: + CTransaction prevTx, *pPrevTxOrNull = NULL; + uint256 hashBlock; + + if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) { + std::ostringstream stream; + stream << "can't find vinTx for vin=" << iv << "."; + return eval->Invalid(stream.str().c_str()); + } + pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present) + + // exec 'validators' from validator row of ival index, for tx.vin[iv] + if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) { + std::ostringstream stream; + stream << "invalid tx vin[" << iv << "]:" << message; + return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' + } + + identifiedCount++; // how many vins we identified + iv++; // advance to the next vin + } + + // CInputValidationPlan treats that there must be at least one identified vin for configured validators' row + // like in 'vin.0: normal input' + if (identifiedCount == 0) { + std::ostringstream stream; + stream << "can't find required vins for " << identifier->inputName() << "."; + return eval->Invalid(stream.str().c_str()); + } + + ival++; // advance to the next validator row + // and it will try the same vin with the new CInputIdentifierBase and validators row + } + + // validation is successful if all validators have been used (i.e. ival = numValidators) + if (ival < numValidators) { + std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl; + return eval->Invalid("incorrect tx structure: not all required vins are present."); + } + + //std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl; + return true; + } + private: - // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) - bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const - { - // check boundaries: - if (ival < 0 || ival >= m_arrayValidators.size()) { - std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param ival index"; - return false; - } - - if (iv < 0 || iv >= pTx->vin.size()) { - std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param iv index"; - return false; - } - - // get requested row of validators: - ValidatorsRow vValidators = m_arrayValidators[ival].second; - - //std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl; - - for (auto v : vValidators) { - bool result; - - if (v->isVinValidator()) - // validate this vin and previous vout: - result = v->validateVin(pTx->vin[iv], pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); - else - // if it is vout validator pass the previous tx vout: - result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); - if (!result) { - return result; - } - } - return true; // validation OK - } - - + // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) + bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const + { + // check boundaries: + if (ival < 0 || ival >= m_arrayValidators.size()) { + std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param ival index"; + return false; + } + + if (iv < 0 || iv >= pTx->vin.size()) { + std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param iv index"; + return false; + } + + // get requested row of validators: + ValidatorsRow vValidators = m_arrayValidators[ival].second; + + //std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl; + + for (auto v : vValidators) { + bool result; + + if (v->isVinValidator()) + // validate this vin and previous vout: + result = v->validateVin(pTx->vin[iv], pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); + else + // if it is vout validator pass the previous tx vout: + result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], refMessage); + if (!result) { + return result; + } + } + return true; // validation OK + } + + private: - //std::map m_arrayValidators; - std::vector< std::pair > m_arrayValidators; - + //std::map m_arrayValidators; + std::vector< std::pair > m_arrayValidators; + }; /** -* Encapsulates an array containing rows of validators -* Each row is a vector of validators (zero is possible) for validating vouts -* this validation plan is used for validating tx outputs -*/ + * Encapsulates an array containing rows of validators + * Each row is a vector of validators (zero is possible) for validating vouts + * this validation plan is used for validating tx outputs + */ template class COutputValidationPlan { - using ValidatorsRow = std::vector; - + using ValidatorsRow = std::vector; + public: - // Pushes a row of validators for validating a vout - // @param ivout index to vout to validate - // @param pargs parameter pack of zero or more pointer to validator objects - // Why pointers? because we store base class and call its virtual functions - - template - void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... - { - ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); - m_arrayValidators.push_back(std::make_pair(ivout, vValidators)); - } - - // validate tx outputs - bool validate(const CTransaction& tx, Eval* eval) - { - std::string message = ""; - //std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl; - - int32_t ival = 0; - int32_t numVouts = tx.vout.size(); - int32_t numValidators = m_arrayValidators.size(); - - // run over vouts: - while (ival < numValidators) { - - int32_t ivout = m_arrayValidators[ival].first; - if (ivout >= numVouts) { - std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl; - return eval->Invalid("incorrect tx structure: not all required vouts are present."); - } - else - { - // exec 'validators' from validator row of ival index, for tx.vout[ivout] - if (!execValidatorsInRow(&tx, ivout, ival, message)) { - std::ostringstream stream; - stream << "invalid tx vout[" << ivout << "]:" << message; - return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' - } - } - ival++; // advance to the next vout - } - //std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl; - return true; - } - + // Pushes a row of validators for validating a vout + // @param ivout index to vout to validate + // @param pargs parameter pack of zero or more pointer to validator objects + // Why pointers? because we store base class and call its virtual functions + + template + void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... + { + ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); + m_arrayValidators.push_back(std::make_pair(ivout, vValidators)); + } + + // validate tx outputs + bool validate(const CTransaction& tx, Eval* eval) + { + std::string message = ""; + //std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl; + + int32_t ival = 0; + int32_t numVouts = tx.vout.size(); + int32_t numValidators = m_arrayValidators.size(); + + // run over vouts: + while (ival < numValidators) { + + int32_t ivout = m_arrayValidators[ival].first; + if (ivout >= numVouts) { + std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl; + return eval->Invalid("incorrect tx structure: not all required vouts are present."); + } + else + { + // exec 'validators' from validator row of ival index, for tx.vout[ivout] + if (!execValidatorsInRow(&tx, ivout, ival, message)) { + std::ostringstream stream; + stream << "invalid tx vout[" << ivout << "]:" << message; + return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' + } + } + ival++; // advance to the next vout + } + //std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl; + return true; + } + private: - // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) - bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const - { - // check boundaries: - if (ival < 0 || ival >= m_arrayValidators.size()) { - std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param ival index"; - return false; - } - - if (iv < 0 || iv >= pTx->vout.size()) { - std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); - refMessage = "internal error: incorrect param iv index"; - return false; - } - - // get requested row of validators: - ValidatorsRow vValidators = m_arrayValidators[ival].second; - - //std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl; - - for (auto v : vValidators) { - - if (!v->isVinValidator()) { - // if this is a 'in' validation plan then pass the previous tx vout: - bool result = v->validateVout(pTx->vout[iv], refMessage); - if (!result) - return result; - } - } - return true; // validation OK - } - + // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) + bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const + { + // check boundaries: + if (ival < 0 || ival >= m_arrayValidators.size()) { + std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param ival index"; + return false; + } + + if (iv < 0 || iv >= pTx->vout.size()) { + std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); + refMessage = "internal error: incorrect param iv index"; + return false; + } + + // get requested row of validators: + ValidatorsRow vValidators = m_arrayValidators[ival].second; + + //std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl; + + for (auto v : vValidators) { + + if (!v->isVinValidator()) { + // if this is a 'in' validation plan then pass the previous tx vout: + bool result = v->validateVout(pTx->vout[iv], refMessage); + if (!result) + return result; + } + } + return true; // validation OK + } + private: - //std::map m_mapValidators; - std::vector< std::pair > m_arrayValidators; - + //std::map m_mapValidators; + std::vector< std::pair > m_arrayValidators; + }; class CNormalInputIdentifier : CInputIdentifierBase { public: - CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} - virtual std::string inputName() const { return std::string("normal input"); } - virtual bool identifyInput(CTxIn vin) const { - return !IsCCInput(vin.scriptSig); - } + CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} + virtual std::string inputName() const { return std::string("normal input"); } + virtual bool identifyInput(CTxIn vin) const { + return !IsCCInput(vin.scriptSig); + } }; class CCCInputIdentifier : CInputIdentifierBase { public: - CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} - virtual std::string inputName() const { return std::string("CC input"); } - virtual bool identifyInput(CTxIn vin) const { - return IsCCInput(vin.scriptSig); - } + CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} + virtual std::string inputName() const { return std::string("CC input"); } + virtual bool identifyInput(CTxIn vin) const { + return IsCCInput(vin.scriptSig); + } }; /** -* Validates 1of2address for vout (may be used for either this or prev tx) -*/ + * Validates 1of2address for vout (may be used for either this or prev tx) + */ template class CCC1of2AddressValidator : CValidatorBase { public: - CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") : - m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {} - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); - if (funcId == 0) { - message = m_customMessage + std::string(" invalid opreturn format"); - std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; - return false; - } - - char shouldBeAddr[65], ccAddr[65]; - - //GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey); - Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey); - - if (vout.scriptPubKey.IsPayToCryptoCondition()) { - if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) { - //std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl; - return true; - } - else { - message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)"); - } - } - else { - message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr"); - } - - std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; - return false; - } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return false; } - + CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") : + m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {} + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, std::string& message) const + { + //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + if (funcId == 0) { + message = m_customMessage + std::string(" invalid opreturn format"); + std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; + return false; + } + + char shouldBeAddr[65], ccAddr[65]; + + //GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey); + Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey); + + if (vout.scriptPubKey.IsPayToCryptoCondition()) { + if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) { + //std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl; + return true; + } + else { + message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)"); + } + } + else { + message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr"); + } + + std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; + return false; + } + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return false; } + private: - CScript m_fundingOpretScript; - std::string m_customMessage; + CScript m_fundingOpretScript; + std::string m_customMessage; }; /** -* Validates if this is vout to owner or heir from opret (funding or change) -*/ + * Validates if this is vout to owner or heir from opret (funding or change) + */ template class CMyPubkeyVoutValidator : CValidatorBase { public: - CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals) - : m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { } - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; - - // get both pubkeys: - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); - if (funcId == 0) { - message = std::string("invalid opreturn format"); - return false; - } - - CScript ownerScript; - CScript heirScript; - if (m_checkNormals) { //not used, incorrect check, too strict - ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; - heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; - std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; - } - else { - ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; - heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; - std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; - } - - // recreate scriptPubKey for owner and heir and compare it with that of the vout to check: - if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) { - // this is vout to owner or heir addr: - //std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl; - return true; - } - - std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl; - message = std::string("invalid pubkey"); - return false; - } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } - + CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals) + : m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { } + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, std::string& message) const + { + //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; + + // get both pubkeys: + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + if (funcId == 0) { + message = std::string("invalid opreturn format"); + return false; + } + + CScript ownerScript; + CScript heirScript; + if (m_checkNormals) { //not used, incorrect check, too strict + ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; + heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; + std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; + } + else { + ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; + heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; + std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; + } + + // recreate scriptPubKey for owner and heir and compare it with that of the vout to check: + if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) { + // this is vout to owner or heir addr: + //std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl; + return true; + } + + std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl; + message = std::string("invalid pubkey"); + return false; + } + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; - //uint256 m_lasttxid; - bool m_checkNormals; + CScript m_fundingOpretScript; + //uint256 m_lasttxid; + bool m_checkNormals; }; /** -* Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime) -*/ + * Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime) + */ template class CHeirSpendValidator : CValidatorBase { public: - CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan) - : m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {} - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - // get heir pubkey: - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); - if (funcId == 0) { - message = std::string("invalid opreturn format"); - return false; - } - - int32_t numblocks; - int64_t durationSec = CCduration(numblocks, m_latesttxid); - - // recreate scriptPubKey for heir and compare it with that of the vout: - if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) { - // this is the heir is trying to spend - if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) { - message = "heir is not allowed yet to spend funds"; - std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl; - return false; - } - else { - // heir is allowed to spend - return true; - } - } - - //std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl; - - // this is not heir: - return true; - } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } - + CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan) + : m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {} + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, std::string& message) const + { + //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + // get heir pubkey: + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + if (funcId == 0) { + message = std::string("invalid opreturn format"); + return false; + } + + int32_t numblocks; + int64_t durationSec = CCduration(numblocks, m_latesttxid); + + // recreate scriptPubKey for heir and compare it with that of the vout: + if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) { + // this is the heir is trying to spend + if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) { + message = "heir is not allowed yet to spend funds"; + std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl; + return false; + } + else { + // heir is allowed to spend + return true; + } + } + + //std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl; + + // this is not heir: + return true; + } + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; - uint256 m_latesttxid; - uint8_t m_isHeirSpendingBegan; + CScript m_fundingOpretScript; + uint256 m_latesttxid; + uint8_t m_isHeirSpendingBegan; }; /** -* Validates this opreturn and compares it with the opreturn from the previous tx -*/ + * Validates this opreturn and compares it with the opreturn from the previous tx + */ template class COpRetValidator : CValidatorBase { public: - COpRetValidator(CCcontract_info* cp, CScript opret) - : m_fundingOpretScript(opret), CValidatorBase(cp) {} - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const - { - //std::cerr << "COpRetValidator::validateVout() entered" << std::endl; - - uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; - uint8_t dummyIsHeirSpendingBegan; - - uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); - if (funcId == 0) { - message = std::string("invalid opreturn format"); - return false; - } - - uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true); - if (initialFuncId == 0) { - message = std::string("invalid initial tx opreturn format"); - return false; - } - - // validation rules: - if (!isMyFuncId(funcId)) { - message = std::string("invalid funcid in opret"); - return false; - } - - if (typeid(Helper) == typeid(TokenHelper)) { - if (tokenid != initialTokenid) { - message = std::string("invalid tokenid in opret"); - return false; - } - } - - //std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl; - return true; - } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } - + COpRetValidator(CCcontract_info* cp, CScript opret) + : m_fundingOpretScript(opret), CValidatorBase(cp) {} + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, std::string& message) const + { + //std::cerr << "COpRetValidator::validateVout() entered" << std::endl; + + uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; + uint8_t dummyIsHeirSpendingBegan; + + uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); + if (funcId == 0) { + message = std::string("invalid opreturn format"); + return false; + } + + uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true); + if (initialFuncId == 0) { + message = std::string("invalid initial tx opreturn format"); + return false; + } + + // validation rules: + if (!isMyFuncId(funcId)) { + message = std::string("invalid funcid in opret"); + return false; + } + + if (typeid(Helper) == typeid(TokenHelper)) { + if (tokenid != initialTokenid) { + message = std::string("invalid tokenid in opret"); + return false; + } + } + + //std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl; + return true; + } + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; + CScript m_fundingOpretScript; }; /** -* empty validator always returns true -*/ + * empty validator always returns true + */ template class CNullValidator : CValidatorBase { public: - CNullValidator(CCcontract_info* cp) - : CValidatorBase(cp) { } - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const { return true; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + CNullValidator(CCcontract_info* cp) + : CValidatorBase(cp) { } + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } }; -#endif \ No newline at end of file +#endif diff --git a/src/cc/makecclib b/src/cc/makecclib new file mode 100755 index 000000000..47aec377d --- /dev/null +++ b/src/cc/makecclib @@ -0,0 +1 @@ +gcc -std=c++11 -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../cclib.so cclib.cpp diff --git a/src/cc/marmara.cpp b/src/cc/marmara.cpp index 4eea7395d..795b95186 100644 --- a/src/cc/marmara.cpp +++ b/src/cc/marmara.cpp @@ -35,8 +35,14 @@ vout0 baton to next receiverpk (following the unspent baton back to original is the credit loop) 'S' + vin0 'I' marker + vin1 baton vins CC utxos from credit loop + 'D' default/partial payment + + 'L' lockfunds + */ // start of consensus code @@ -82,13 +88,13 @@ uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32 } if ( vopret.size() > 2 && script[0] == EVAL_MARMARA ) { - if ( script[1] == 'C' || script[1] == 'P' ) + if ( script[1] == 'C' || script[1] == 'P' || script[1] == 'L' ) { if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> unlockht) != 0 ) { return(script[1]); } else fprintf(stderr,"DecodeMaramaraCoinbaseOpRet unmarshal error for %c\n",script[1]); - } else fprintf(stderr,"script[1] is %d != 'C' %d or 'P' %d\n",script[1],'C','P'); + } else fprintf(stderr,"script[1] is %d != 'C' %d or 'P' %d or 'L' %d\n",script[1],'C','P','L'); } else fprintf(stderr,"vopret.size() is %d\n",(int32_t)vopret.size()); return(0); } @@ -155,18 +161,25 @@ int32_t MarmaraGetbatontxid(std::vector &creditloop,uint256 &batontxid, if ( MarmaraGetcreatetxid(createtxid,txid) == 0 ) { txid = createtxid; - fprintf(stderr,"txid.%s -> createtxid %s\n",txid.GetHex().c_str(),createtxid.GetHex().c_str()); + //fprintf(stderr,"txid.%s -> createtxid %s\n",txid.GetHex().c_str(),createtxid.GetHex().c_str()); while ( CCgetspenttxid(spenttxid,vini,height,txid,vout) == 0 ) { creditloop.push_back(txid); - fprintf(stderr,"%d: %s\n",n,txid.GetHex().c_str()); + //fprintf(stderr,"%d: %s\n",n,txid.GetHex().c_str()); n++; - if ( (value= CCgettxout(spenttxid,vout,1)) > 0 ) + if ( (value= CCgettxout(spenttxid,vout,1)) == 10000 ) { batontxid = spenttxid; - fprintf(stderr,"got baton %s %.8f\n",batontxid.GetHex().c_str(),(double)value/COIN); + //fprintf(stderr,"got baton %s %.8f\n",batontxid.GetHex().c_str(),(double)value/COIN); return(n); } + else if ( value > 0 ) + { + batontxid = spenttxid; + fprintf(stderr,"n.%d got false baton %s/v%d %.8f\n",n,batontxid.GetHex().c_str(),vout,(double)value/COIN); + return(n); + } + // get funcid txid = spenttxid; } } @@ -175,9 +188,16 @@ int32_t MarmaraGetbatontxid(std::vector &creditloop,uint256 &batontxid, CScript Marmara_scriptPubKey(int32_t height,CPubKey pk) { - CTxOut ccvout; + CTxOut ccvout; struct CCcontract_info *cp,C; CPubKey Marmarapk; + cp = CCinit(&C,EVAL_MARMARA); + Marmarapk = GetUnspendable(cp,0); if ( height > 0 && (height & 1) == 0 && pk.size() == 33 ) - ccvout = MakeCC1vout(EVAL_MARMARA,0,pk); + { + ccvout = MakeCC1of2vout(EVAL_MARMARA,0,Marmarapk,pk); + //char coinaddr[64]; + //Getscriptaddress(coinaddr,ccvout.scriptPubKey); + //fprintf(stderr,"Marmara_scriptPubKey %s ht.%d -> %s\n",HexStr(pk).c_str(),height,coinaddr); + } return(ccvout.scriptPubKey); } @@ -192,8 +212,9 @@ CScript MarmaraCoinbaseOpret(uint8_t funcid,int32_t height,CPubKey pk) int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx) { - struct CCcontract_info *cp,C; CPubKey pk; int32_t ht,unlockht; CTxOut ccvout; + struct CCcontract_info *cp,C; CPubKey Marmarapk,pk; int32_t ht,unlockht; CTxOut ccvout; cp = CCinit(&C,EVAL_MARMARA); + Marmarapk = GetUnspendable(cp,0); if ( 0 ) { int32_t d,histo[365*2+30]; @@ -221,10 +242,13 @@ int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx) if ( ht == height && MarmaraUnlockht(height) == unlockht ) { //fprintf(stderr,"ht.%d -> unlock.%d\n",ht,unlockht); - ccvout = MakeCC1vout(EVAL_MARMARA,0,pk); + ccvout = MakeCC1of2vout(EVAL_MARMARA,0,Marmarapk,pk); if ( ccvout.scriptPubKey == tx.vout[0].scriptPubKey ) return(0); - fprintf(stderr,"ht.%d mismatched CCvout scriptPubKey\n",height); + char addr0[64],addr1[64]; + Getscriptaddress(addr0,ccvout.scriptPubKey); + Getscriptaddress(addr1,tx.vout[0].scriptPubKey); + fprintf(stderr,"ht.%d mismatched CCvout scriptPubKey %s vs %s pk.%d %s\n",height,addr0,addr1,(int32_t)pk.size(),HexStr(pk).c_str()); } else fprintf(stderr,"ht.%d %d vs %d unlock.%d\n",height,MarmaraUnlockht(height),ht,unlockht); } else fprintf(stderr,"ht.%d error decoding coinbase opret\n",height); } @@ -234,6 +258,8 @@ int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx) bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { std::vector vopret; CTransaction vinTx; uint256 hashBlock; int32_t numvins,numvouts,i,ht,unlockht,vht,vunlockht; uint8_t funcid,vfuncid,*script; CPubKey pk,vpk; + if ( ASSETCHAINS_MARMARA == 0 ) + return eval->Invalid("-ac_marmara must be set for marmara CC"); numvins = tx.vin.size(); numvouts = tx.vout.size(); if ( numvouts < 1 ) @@ -284,9 +310,13 @@ bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t { return(true); } - else if ( funcid == 'S' ) // collect -> automatically spend issuers locked funds, given 'I' + else if ( funcid == 'S' ) // settlement -> automatically spend issuers locked funds, given 'I' { - return(true); // iterate from issuer all remainder after maturity + return(true); + } + else if ( funcid == 'D' ) // insufficient settlement + { + return(true); } // staking only for locked utxo } @@ -298,9 +328,10 @@ bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t int64_t AddMarmaraCoinbases(struct CCcontract_info *cp,CMutableTransaction &mtx,int32_t firstheight,CPubKey poolpk,int32_t maxinputs) { - char coinaddr[64]; CPubKey pk; int64_t nValue,totalinputs = 0; uint256 txid,hashBlock; CTransaction vintx; int32_t unlockht,ht,vout,unlocks,n = 0; + char coinaddr[64]; CPubKey Marmarapk,pk; int64_t nValue,totalinputs = 0; uint256 txid,hashBlock; CTransaction vintx; int32_t unlockht,ht,vout,unlocks,n = 0; std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,poolpk); + Marmarapk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,Marmarapk,poolpk); SetCCunspents(unspentOutputs,coinaddr); unlocks = MarmaraUnlockht(firstheight); //fprintf(stderr,"check coinaddr.(%s)\n",coinaddr); @@ -332,13 +363,258 @@ int64_t AddMarmaraCoinbases(struct CCcontract_info *cp,CMutableTransaction &mtx, return(totalinputs); } -int32_t MarmaraGetCreditloops(int64_t &totalamount,std::vector &issuances,struct CCcontract_info *cp,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,CPubKey refpk,std::string refcurrency) +int64_t AddMarmarainputs(CMutableTransaction &mtx,std::vector &pubkeys,char *coinaddr,int64_t total,int32_t maxinputs) +{ + uint64_t threshold,nValue,totalinputs = 0; uint256 txid,hashBlock; CTransaction tx; int32_t numvouts,ht,unlockht,vout,i,n = 0; uint8_t funcid; CPubKey pk; std::vector vals; + std::vector > unspentOutputs; + SetCCunspents(unspentOutputs,coinaddr); + threshold = total/(maxinputs+1); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( it->second.satoshis < threshold ) + continue; + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + { + if ( (funcid= DecodeMaramaraCoinbaseOpRet(tx.vout[numvouts-1].scriptPubKey,pk,ht,unlockht)) == 'C' || funcid == 'P' || funcid == 'L' ) + { + //char str[64]; fprintf(stderr,"(%s) %s/v%d %.8f ht.%d unlockht.%d\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN,ht,unlockht); + if ( total != 0 && maxinputs != 0 ) + { + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + pubkeys.push_back(pk); + } + totalinputs += it->second.satoshis; + vals.push_back(it->second.satoshis); + n++; + if ( maxinputs != 0 && total == 0 ) + continue; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } else fprintf(stderr,"null funcid\n"); + } + } + if ( maxinputs != 0 && total == 0 ) + { + std::sort(vals.begin(),vals.end()); + totalinputs = 0; + for (i=0; i txfee ) + inputsum = AddNormalinputs2(mtx,val,CC_MAXVINS/2); + //fprintf(stderr,"normal inputs %.8f val %.8f\n",(double)inputsum/COIN,(double)val/COIN); + mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA,amount,Marmarapk,mypk)); + if ( inputsum < amount+txfee ) + { + refunlockht = MarmaraUnlockht(height); + result.push_back(Pair("normalfunds",ValueFromAmount(inputsum))); + result.push_back(Pair("height",height)); + result.push_back(Pair("unlockht",refunlockht)); + remains = (amount + txfee) - inputsum; + std::vector > unspentOutputs; + GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk); + SetCCunspents(unspentOutputs,coinaddr); + threshold = remains / (MARMARA_VINS+1); + CCaddr1of2set(cp,Marmarapk,mypk,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( (nValue= it->second.satoshis) < threshold ) + continue; + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + { + if ( (funcid= DecodeMaramaraCoinbaseOpRet(tx.vout[numvouts-1].scriptPubKey,pk,ht,unlockht)) == 'C' || funcid == 'P' || funcid == 'L' ) + { + if ( unlockht < refunlockht ) + { + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + //fprintf(stderr,"merge CC vout %s/v%d %.8f unlockht.%d < ref.%d\n",txid.GetHex().c_str(),vout,(double)nValue/COIN,unlockht,refunlockht); + inputsum += nValue; + remains -= nValue; + if ( inputsum >= amount + txfee ) + { + //fprintf(stderr,"inputsum %.8f >= amount %.8f, update amount\n",(double)inputsum/COIN,(double)amount/COIN); + amount = inputsum - txfee; + break; + } + } + } + } + } + } + if ( inputsum >= amount+txfee ) + { + if ( inputsum > amount+txfee ) + { + change = (inputsum - amount); + mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraCoinbaseOpret('L',height,mypk)); + if ( rawtx.size() == 0 ) + errorstr = (char *)"couldnt finalize CCtx"; + else + { + result.push_back(Pair("result",(char *)"success")); + result.push_back(Pair("rawtx",rawtx)); + return(result); + } + } else errorstr = (char *)"insufficient funds"; + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",errorstr)); + return(result); +} + +// jl777: decide on what unlockht settlement change should have + +UniValue MarmaraSettlement(uint64_t txfee,uint256 refbatontxid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::vector creditloop; uint256 batontxid,createtxid,refcreatetxid,hashBlock; uint8_t funcid; int32_t numerrs=0,i,n,numvouts,matures,refmatures,height; int64_t amount,refamount,remaining,inputsum,change; CPubKey Marmarapk,mypk,pk; std::string currency,refcurrency,rawtx; CTransaction tx,batontx; char coinaddr[64],myCCaddr[64],destaddr[64],batonCCaddr[64],str[2],txidaddr[64]; std::vector pubkeys; struct CCcontract_info *cp,C; + if ( txfee == 0 ) + txfee = 10000; + cp = CCinit(&C,EVAL_MARMARA); + mypk = pubkey2pk(Mypubkey()); + Marmarapk = GetUnspendable(cp,0); + remaining = change = 0; + height = chainActive.LastTip()->GetHeight(); + if ( (n= MarmaraGetbatontxid(creditloop,batontxid,refbatontxid)) > 0 ) + { + if ( GetTransaction(batontxid,batontx,hashBlock,false) != 0 && (numvouts= batontx.vout.size()) > 1 ) + { + if ( (funcid= MarmaraDecodeLoopOpret(batontx.vout[numvouts-1].scriptPubKey,refcreatetxid,pk,refamount,refmatures,refcurrency)) != 0 ) + { + if ( refcreatetxid != creditloop[0] ) + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"invalid refcreatetxid, setting to creditloop[0]")); + return(result); + } + else if ( chainActive.LastTip()->GetHeight() < refmatures ) + { + fprintf(stderr,"doesnt mature for another %d blocks\n",refmatures - chainActive.LastTip()->GetHeight()); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"cant settle immature creditloop")); + return(result); + } + else if ( (refmatures & 1) == 0 ) + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"cant automatic settle even maturity heights")); + return(result); + } + else if ( n < 1 ) + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"creditloop too short")); + return(result); + } + remaining = refamount; + GetCCaddress(cp,myCCaddr,Mypubkey()); + Getscriptaddress(batonCCaddr,batontx.vout[0].scriptPubKey); + if ( strcmp(myCCaddr,batonCCaddr) == 0 ) + { + mtx.vin.push_back(CTxIn(n == 1 ? batontxid : creditloop[1],1,CScript())); // issuance marker + pubkeys.push_back(Marmarapk); + mtx.vin.push_back(CTxIn(batontxid,0,CScript())); + pubkeys.push_back(mypk); + for (i=1; i 1 ) + { + if ( (funcid= MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,createtxid,pk,amount,matures,currency)) != 0 ) + { + GetCCaddress1of2(cp,coinaddr,Marmarapk,pk); + if ( (inputsum= AddMarmarainputs(mtx,pubkeys,coinaddr,remaining,MARMARA_VINS)) >= remaining ) + { + change = (inputsum - remaining); + mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if ( change > txfee ) + mtx.vout.push_back(MakeCC1of2vout(EVAL_MARMARA,change,Marmarapk,pk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret('S',createtxid,mypk,0,refmatures,currency),pubkeys); + result.push_back(Pair("result",(char *)"success")); + result.push_back(Pair("rawtx",rawtx)); + return(result); + } else remaining -= inputsum; + if ( mtx.vin.size() >= CC_MAXVINS - MARMARA_VINS ) + break; + } else fprintf(stderr,"null funcid for creditloop[%d]\n",i); + } else fprintf(stderr,"couldnt get creditloop[%d]\n",i); + } + if ( refamount - remaining > 2*txfee ) + { + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,createtxid))) << OP_CHECKSIG)); // failure marker + if ( refamount-remaining > 3*txfee ) + mtx.vout.push_back(CTxOut(refamount-remaining-2*txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,MarmaraLoopOpret('D',createtxid,mypk,-remaining,refmatures,currency),pubkeys); + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"insufficient funds")); + result.push_back(Pair("rawtx",rawtx)); + result.push_back(Pair("remaining",ValueFromAmount(remaining))); + } + else + { + // jl777: maybe fund a txfee to report no funds avail + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"no funds available at all")); + } + } + else + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"this node does not have the baton")); + result.push_back(Pair("myCCaddr",myCCaddr)); + result.push_back(Pair("batonCCaddr",batonCCaddr)); + } + } + else + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt get batontxid opret")); + } + } + else + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt find batontxid")); + } + } + else + { + result.push_back(Pair("result",(char *)"error")); + result.push_back(Pair("error",(char *)"couldnt get creditloop")); + } + return(result); +} + +int32_t MarmaraGetCreditloops(int64_t &totalamount,std::vector &issuances,int64_t &totalclosed,std::vector &closed,struct CCcontract_info *cp,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,CPubKey refpk,std::string refcurrency) { char coinaddr[64]; CPubKey Marmarapk,senderpk; int64_t amount; uint256 createtxid,txid,hashBlock; CTransaction tx; int32_t numvouts,vout,matures,n=0; std::string currency; std::vector > unspentOutputs; Marmarapk = GetUnspendable(cp,0); GetCCaddress(cp,coinaddr,Marmarapk); SetCCunspents(unspentOutputs,coinaddr); + // do all txid, conditional on spent/unspent //fprintf(stderr,"check coinaddr.(%s)\n",coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { @@ -364,21 +640,24 @@ int32_t MarmaraGetCreditloops(int64_t &totalamount,std::vector &issuanc return(n); } -UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid) +UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid,bool automaticflag) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey mypk; struct CCcontract_info *cp,C; std::string rawtx; char *errorstr=0; uint256 createtxid; int64_t batonamount; int32_t needbaton = 0; cp = CCinit(&C,EVAL_MARMARA); if ( txfee == 0 ) txfee = 10000; - // check for batonownership by senderpk and parameters match createtxid + if ( automaticflag != 0 && (matures & 1) == 0 ) + matures++; + else if ( automaticflag == 0 && (matures & 1) != 0 ) + matures++; mypk = pubkey2pk(Mypubkey()); memset(&createtxid,0,sizeof(createtxid)); if ( batontxid != zeroid && MarmaraGetcreatetxid(createtxid,batontxid) < 0 ) errorstr = (char *)"cant get createtxid from batontxid"; else if ( currency != "MARMARA" ) errorstr = (char *)"for now, only MARMARA loops are supported"; - else if ( amount < txfee ) + else if ( amount <= txfee ) errorstr = (char *)"amount must be for more than txfee"; else if ( matures <= chainActive.LastTip()->GetHeight() ) errorstr = (char *)"it must mature in the future"; @@ -425,17 +704,14 @@ UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t a cp = CCinit(&C,EVAL_MARMARA); if ( txfee == 0 ) txfee = 10000; - // make sure receiverpk is unique to creditloop // make sure less than maxlength Marmarapk = GetUnspendable(cp,0); mypk = pubkey2pk(Mypubkey()); if ( MarmaraGetcreatetxid(createtxid,approvaltxid) < 0 ) errorstr = (char *)"cant get createtxid from approvaltxid"; - else if ( batontxid == zeroid ) - errorstr = (char *)"null batontxid"; else if ( currency != "MARMARA" ) errorstr = (char *)"for now, only MARMARA loops are supported"; - else if ( amount < txfee ) + else if ( amount <= txfee ) errorstr = (char *)"amount must be for more than txfee"; else if ( matures <= chainActive.LastTip()->GetHeight() ) errorstr = (char *)"it must mature in the future"; @@ -479,69 +755,6 @@ UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t a return(result); } -UniValue MarmaraSettlement(uint64_t txfee,uint256 refbatontxid) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::vector creditloop; uint256 batontxid,createtxid,refcreatetxid,hashBlock; uint8_t funcid; int32_t numerrs=0,i,n,numvouts,matures,refmatures; int64_t amount,refamount; CPubKey Marmarapk,pk; std::string currency,refcurrency; CTransaction tx,batontx; char coinaddr[64],myCCaddr[64],destaddr[64],batonCCaddr[64],str[2]; struct CCcontract_info *cp,C; - if ( txfee == 0 ) - txfee = 10000; - cp = CCinit(&C,EVAL_MARMARA); - Marmarapk = GetUnspendable(cp,0); - if ( (n= MarmaraGetbatontxid(creditloop,batontxid,refbatontxid)) > 0 ) - { - if ( GetTransaction(batontxid,batontx,hashBlock,false) != 0 && (numvouts= batontx.vout.size()) > 1 ) - { - if ( (funcid= MarmaraDecodeLoopOpret(batontx.vout[numvouts-1].scriptPubKey,refcreatetxid,pk,refamount,refmatures,refcurrency)) != 0 ) - { - if ( refcreatetxid != creditloop[0] ) - { - fprintf(stderr,"invalid refcreatetxid, setting to creditloop[0]\n"); - refcreatetxid = creditloop[0]; - numerrs++; - } - GetCCaddress(cp,myCCaddr,Mypubkey()); - Getscriptaddress(batonCCaddr,batontx.vout[0].scriptPubKey); - if ( strcmp(myCCaddr,batonCCaddr) == 0 ) - { - for (i=0; i 1 ) - { - if ( (funcid= MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,createtxid,pk,amount,matures,currency)) != 0 ) - { - GetCCaddress1of2(cp,coinaddr,Marmarapk,pk); - fprintf(stderr,"get locked funds of %s %.8f\n",coinaddr,(double)CCaddress_balance(coinaddr)/COIN); - } else fprintf(stderr,"null funcid for creditloop[%d]\n",i); - } else fprintf(stderr,"couldnt get creditloop[%d]\n",i); - } - } - else - { - result.push_back(Pair("result",(char *)"error")); - result.push_back(Pair("error",(char *)"this node does not have the baton")); - result.push_back(Pair("myCCaddr",myCCaddr)); - result.push_back(Pair("batonCCaddr",batonCCaddr)); - } - } - else - { - result.push_back(Pair("result",(char *)"error")); - result.push_back(Pair("error",(char *)"couldnt get batontxid opret")); - } - } - else - { - result.push_back(Pair("result",(char *)"error")); - result.push_back(Pair("error",(char *)"couldnt find batontxid")); - } - } - else - { - result.push_back(Pair("result",(char *)"error")); - result.push_back(Pair("error",(char *)"couldnt get creditloop")); - } - return(result); -} - UniValue MarmaraCreditloop(uint256 txid) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::vector creditloop; uint256 batontxid,createtxid,refcreatetxid,hashBlock; uint8_t funcid; int32_t numerrs=0,i,n,numvouts,matures,refmatures; int64_t amount,refamount; CPubKey pk; std::string currency,refcurrency; CTransaction tx; char coinaddr[64],myCCaddr[64],destaddr[64],batonCCaddr[64],str[2]; struct CCcontract_info *cp,C; @@ -555,35 +768,69 @@ UniValue MarmaraCreditloop(uint256 txid) result.push_back(Pair("myaddress",coinaddr)); GetCCaddress(cp,myCCaddr,Mypubkey()); result.push_back(Pair("myCCaddress",myCCaddr)); - result.push_back(Pair("batontxid",batontxid.GetHex())); if ( (funcid= MarmaraDecodeLoopOpret(tx.vout[numvouts-1].scriptPubKey,refcreatetxid,pk,refamount,refmatures,refcurrency)) != 0 ) { str[0] = funcid, str[1] = 0; result.push_back(Pair("funcid",str)); - if ( refcreatetxid != creditloop[0] ) - { - fprintf(stderr,"invalid refcreatetxid, setting to creditloop[0]\n"); - refcreatetxid = creditloop[0]; - numerrs++; - } - result.push_back(Pair("createtxid",refcreatetxid.GetHex())); - result.push_back(Pair("amount",ValueFromAmount(refamount))); - result.push_back(Pair("matures",refmatures)); result.push_back(Pair("currency",refcurrency)); - result.push_back(Pair("batonpk",HexStr(pk))); - Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); - result.push_back(Pair("batonaddr",coinaddr)); - GetCCaddress(cp,batonCCaddr,pk); - result.push_back(Pair("batonCCaddr",batonCCaddr)); - Getscriptaddress(coinaddr,tx.vout[0].scriptPubKey); - if ( strcmp(coinaddr,batonCCaddr) != 0 ) + if ( funcid == 'S' ) { - result.push_back(Pair("vout0address",coinaddr)); - numerrs++; + refcreatetxid = creditloop[0]; + result.push_back(Pair("settlement",batontxid.GetHex())); + result.push_back(Pair("createtxid",refcreatetxid.GetHex())); + result.push_back(Pair("remainder",ValueFromAmount(refamount))); + result.push_back(Pair("settled",refmatures)); + result.push_back(Pair("pubkey",HexStr(pk))); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + result.push_back(Pair("coinaddr",coinaddr)); + result.push_back(Pair("collected",ValueFromAmount(tx.vout[0].nValue))); + Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); + if ( strcmp(coinaddr,destaddr) != 0 ) + { + result.push_back(Pair("destaddr",destaddr)); + numerrs++; + } + refamount = -1; + } + else if ( funcid == 'D' ) + { + refcreatetxid = creditloop[0]; + result.push_back(Pair("settlement",batontxid.GetHex())); + result.push_back(Pair("createtxid",refcreatetxid.GetHex())); + result.push_back(Pair("remainder",ValueFromAmount(refamount))); + result.push_back(Pair("settled",refmatures)); + Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); + result.push_back(Pair("txidaddr",destaddr)); + if ( tx.vout.size() > 1 ) + result.push_back(Pair("collected",ValueFromAmount(tx.vout[1].nValue))); + } + else + { + result.push_back(Pair("batontxid",batontxid.GetHex())); + result.push_back(Pair("createtxid",refcreatetxid.GetHex())); + result.push_back(Pair("amount",ValueFromAmount(refamount))); + result.push_back(Pair("matures",refmatures)); + if ( refcreatetxid != creditloop[0] ) + { + fprintf(stderr,"invalid refcreatetxid, setting to creditloop[0]\n"); + refcreatetxid = creditloop[0]; + numerrs++; + } + result.push_back(Pair("batonpk",HexStr(pk))); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); + result.push_back(Pair("batonaddr",coinaddr)); + GetCCaddress(cp,batonCCaddr,pk); + result.push_back(Pair("batonCCaddr",batonCCaddr)); + Getscriptaddress(coinaddr,tx.vout[0].scriptPubKey); + if ( strcmp(coinaddr,batonCCaddr) != 0 ) + { + result.push_back(Pair("vout0address",coinaddr)); + numerrs++; + } + if ( strcmp(myCCaddr,coinaddr) == 0 ) + result.push_back(Pair("ismine",1)); + else result.push_back(Pair("ismine",0)); } - if ( strcmp(myCCaddr,coinaddr) == 0 ) - result.push_back(Pair("ismine",1)); - else result.push_back(Pair("ismine",0)); for (i=0; i 1 ) @@ -617,6 +864,13 @@ UniValue MarmaraCreditloop(uint256 txid) obj.push_back(Pair("vout0address",destaddr)); numerrs++; } + if ( i == 0 && refamount < 0 ) + { + refamount = amount; + refmatures = matures; + result.push_back(Pair("amount",ValueFromAmount(refamount))); + result.push_back(Pair("matures",refmatures)); + } if ( createtxid != refcreatetxid || amount != refamount || matures != refmatures || currency != refcurrency ) { numerrs++; @@ -653,43 +907,6 @@ UniValue MarmaraCreditloop(uint256 txid) } return(result); } - -UniValue MarmaraInfo(CPubKey refpk,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,std::string currency) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,matches; int64_t totalamount=0; std::vector issuances; char coinaddr[64]; - CPubKey Marmarapk; struct CCcontract_info *cp,C; - result.push_back(Pair("result","success")); - Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(Mypubkey())) << OP_CHECKSIG); - result.push_back(Pair("myaddress",coinaddr)); - GetCCaddress(cp,coinaddr,Mypubkey()); - result.push_back(Pair("myCCaddress",coinaddr)); - if ( refpk.size() == 33 ) - result.push_back(Pair("issuer",HexStr(refpk))); - if ( currency.size() == 0 ) - currency = (char *)"MARMARA"; - if ( firstheight <= lastheight ) - firstheight = 0, lastheight = (1 << 30); - if ( minamount <= maxamount ) - minamount = 0, maxamount = (1LL << 60); - result.push_back(Pair("firstheight",firstheight)); - result.push_back(Pair("lastheight",lastheight)); - result.push_back(Pair("minamount",ValueFromAmount(minamount))); - result.push_back(Pair("maxamount",ValueFromAmount(maxamount))); - result.push_back(Pair("currency",currency)); - cp = CCinit(&C,EVAL_MARMARA); - Marmarapk = GetUnspendable(cp,0); - if ( (n= MarmaraGetCreditloops(totalamount,issuances,cp,firstheight,lastheight,minamount,maxamount,refpk,currency)) > 0 ) - { - result.push_back(Pair("n",n)); - matches = (int32_t)issuances.size(); - result.push_back(Pair("matches",matches)); - for (i=0; i pubkeys; + UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); int32_t i,n,matches; int64_t totalclosed=0,totalamount=0; std::vector issuances,closed; char coinaddr[64]; + CPubKey Marmarapk; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_MARMARA); + Marmarapk = GetUnspendable(cp,0); + result.push_back(Pair("result","success")); + Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(Mypubkey())) << OP_CHECKSIG); + result.push_back(Pair("myaddress",coinaddr)); + result.push_back(Pair("normal",ValueFromAmount(CCaddress_balance(coinaddr)))); + + GetCCaddress1of2(cp,coinaddr,Marmarapk,Mypubkey()); + result.push_back(Pair("myCCactivated",coinaddr)); + result.push_back(Pair("activated",ValueFromAmount(CCaddress_balance(coinaddr)))); + result.push_back(Pair("activated16",ValueFromAmount(AddMarmarainputs(mtx,pubkeys,coinaddr,0,MARMARA_VINS)))); + + GetCCaddress(cp,coinaddr,Mypubkey()); + result.push_back(Pair("myCCaddress",coinaddr)); + result.push_back(Pair("CCutxos",ValueFromAmount(CCaddress_balance(coinaddr)))); + + if ( refpk.size() == 33 ) + result.push_back(Pair("issuer",HexStr(refpk))); + if ( currency.size() == 0 ) + currency = (char *)"MARMARA"; + if ( firstheight <= lastheight ) + firstheight = 0, lastheight = (1 << 30); + if ( minamount <= maxamount ) + minamount = 0, maxamount = (1LL << 60); + result.push_back(Pair("firstheight",firstheight)); + result.push_back(Pair("lastheight",lastheight)); + result.push_back(Pair("minamount",ValueFromAmount(minamount))); + result.push_back(Pair("maxamount",ValueFromAmount(maxamount))); + result.push_back(Pair("currency",currency)); + if ( (n= MarmaraGetCreditloops(totalamount,issuances,totalclosed,closed,cp,firstheight,lastheight,minamount,maxamount,refpk,currency)) > 0 ) + { + result.push_back(Pair("n",n)); + matches = (int32_t)issuances.size(); + result.push_back(Pair("pending",matches)); + for (i=0; iGetHeight()); KOMODO_INITDONE = (uint32_t)time(NULL); @@ -942,12 +947,15 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) } } } - if ( ((signedmask & 1) != 0 && numvalid >= KOMODO_MINRATIFY) || bitweight(signedmask) > (numnotaries/3) ) + if ( ASSETCHAINS_SYMBOL[0] != 0 || height < 100000 ) { - memset(&txhash,0,sizeof(txhash)); - komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0,0,0,0,0,0,0,0,0,zero,0); - printf("RATIFIED! >>>>>>>>>> new notaries.%d newheight.%d from height.%d\n",numvalid,(((height+KOMODO_ELECTION_GAP/2)/KOMODO_ELECTION_GAP)+1)*KOMODO_ELECTION_GAP,height); - } else printf("signedmask.%llx numvalid.%d wt.%d numnotaries.%d\n",(long long)signedmask,numvalid,bitweight(signedmask),numnotaries); + if ( ((signedmask & 1) != 0 && numvalid >= KOMODO_MINRATIFY) || bitweight(signedmask) > (numnotaries/3) ) + { + memset(&txhash,0,sizeof(txhash)); + komodo_stateupdate(height,pubkeys,numvalid,0,txhash,0,0,0,0,0,0,0,0,0,0,zero,0); + printf("RATIFIED! >>>>>>>>>> new notaries.%d newheight.%d from height.%d\n",numvalid,(((height+KOMODO_ELECTION_GAP/2)/KOMODO_ELECTION_GAP)+1)*KOMODO_ELECTION_GAP,height); + } else printf("signedmask.%llx numvalid.%d wt.%d numnotaries.%d\n",(long long)signedmask,numvalid,bitweight(signedmask),numnotaries); + } } } } diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index ebc50fa62..10c6ee4b8 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -643,6 +643,11 @@ int32_t komodo_isPoS(CBlock *pblock) { txid = pblock->vtx[n-1].vin[0].prevout.hash; vout = pblock->vtx[n-1].vin[0].prevout.n; + if ( ASSETCHAINS_MARMARA != 0 ) + { + fprintf(stderr,"validate proper signature and unlockht preservation\n"); + // need to verify it was signed by the non-Marmarapk of the 1of2 + } txtime = komodo_txtime(&value,txid,vout,destaddr); if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) ) { @@ -2112,31 +2117,57 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt maxkp = numkp = 0; lasttime = 0; } - BOOST_FOREACH(const COutput& out, vecOutputs) + if ( ASSETCHAINS_MARMARA == 0 ) { - if ( (tipindex= chainActive.Tip()) == 0 || tipindex->GetHeight()+1 > nHeight ) + BOOST_FOREACH(const COutput& out, vecOutputs) { - fprintf(stderr,"chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter); - return(0); - } - counter++; - if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth ) - { - fprintf(stderr,"komodo_staked invalid depth %d\n",(int32_t)out.nDepth); - continue; - } - CAmount nValue = out.tx->vout[out.i].nValue; - if ( nValue < COIN || !out.fSpendable ) - continue; - const CScript& pk = out.tx->vout[out.i].scriptPubKey; - if ( ExtractDestination(pk,address) != 0 ) - { - if ( IsMine(*pwalletMain,address) == 0 ) - continue; - if ( GetTransaction(out.tx->GetHash(),tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 ) + if ( (tipindex= chainActive.Tip()) == 0 || tipindex->GetHeight()+1 > nHeight ) { - array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,out.tx->GetHash(),out.i,(char *)CBitcoinAddress(address).ToString().c_str(),hashbuf,(CScript)pk); - //fprintf(stderr,"addutxo numkp.%d vs max.%d\n",numkp,maxkp); + fprintf(stderr,"chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter); + return(0); + } + counter++; + if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth ) + { + fprintf(stderr,"komodo_staked invalid depth %d\n",(int32_t)out.nDepth); + continue; + } + CAmount nValue = out.tx->vout[out.i].nValue; + if ( nValue < COIN || !out.fSpendable ) + continue; + const CScript& pk = out.tx->vout[out.i].scriptPubKey; + if ( ExtractDestination(pk,address) != 0 ) + { + if ( IsMine(*pwalletMain,address) == 0 ) + continue; + if ( GetTransaction(out.tx->GetHash(),tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 ) + { + array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,out.tx->GetHash(),out.i,(char *)CBitcoinAddress(address).ToString().c_str(),hashbuf,(CScript)pk); + //fprintf(stderr,"addutxo numkp.%d vs max.%d\n",numkp,maxkp); + } + } + } + } + else + { + struct CCcontract_info *cp,C; uint256 txid; int32_t vout; CAmount nValue; char coinaddr[64]; CPubKey mypk,Marmarapk; + std::vector > unspentOutputs; + cp = CCinit(&C,EVAL_MARMARA); + mypk = pubkey2pk(Mypubkey()); + Marmarapk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk); + SetCCunspents(unspentOutputs,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( (nValue= it->second.satoshis) < COIN ) + continue; + if ( GetTransaction(txid,tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + { + const CScript &scriptPubKey = tx.vout[vout].scriptPubKey; + array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,txid,vout,coinaddr,hashbuf,(CScript)scriptPubKey); + fprintf(stderr,"addutxo %.8f numkp.%d vs max.%d\n",(double)nValue/COIN,numkp,maxkp); } } } @@ -2176,7 +2207,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt } else { - fprintf(stderr,"ht.%d error validating winning blocktime %u -> %.8f eligible.%u test prior\n",nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); + //fprintf(stderr,"ht.%d error validating winning blocktime %u -> %.8f eligible.%u test prior\n",nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); continue; } eligible = besttime; @@ -2218,7 +2249,16 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt txNew.vout[0].nValue = *utxovaluep - txfee; txNew.nLockTime = earliest; CTransaction txNewConst(txNew); - signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); + if ( ASSETCHAINS_MARMARA == 0 ) + { + signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); + } + else + { + fprintf(stderr,"add opreturn and CCFinalizetx\n"); + // add opreturn + // signSuccess = CCFinalizetx(...) + } if (!signSuccess) fprintf(stderr,"failed to create signature\n"); else diff --git a/src/komodo_defs.h b/src/komodo_defs.h index bd516be61..91fcadddb 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -30,6 +30,10 @@ #define KOMODO_SAPLING_DEADLINE 1550188800 // Feb 15th, 2019 #define _COINBASE_MATURITY 100 +#define SETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] |= (1 << ((bitoffset) & 7))) +#define GETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] & (1 << ((bitoffset) & 7))) +#define CLEARBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] &= ~(1 << ((bitoffset) & 7))) + extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e19d94915..02ab67407 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1388,7 +1388,7 @@ void komodo_passport_iteration() { static long lastpos[34]; static char userpass[33][1024]; static uint32_t lasttime,callcounter,lastinterest; int32_t maxseconds = 10; - FILE *fp; uint8_t *filedata; long fpos,datalen,lastfpos; int32_t baseid,limit,n,ht,isrealtime,expired,refid,blocks,longest; struct komodo_state *sp,*refsp; char *retstr,fname[512],*base,symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t buf[3],starttime; cJSON *infoobj,*result; uint64_t RTmask = 0; //CBlockIndex *pindex; + FILE *fp; uint8_t *filedata; long fpos,datalen,lastfpos; int32_t baseid,limit,n,ht,isrealtime,expired,refid,blocks,longest; struct komodo_state *sp,*refsp; char *retstr,fname[512],*base,symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t buf[3],starttime; uint64_t RTmask = 0; //CBlockIndex *pindex; expired = 0; while ( KOMODO_INITDONE == 0 ) { diff --git a/src/komodo_globals.h b/src/komodo_globals.h index f434674b7..79b56cbbb 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -48,7 +48,7 @@ unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10; int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS; int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1; -std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,ASSETCHAINS_SELFIMPORT; +std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,ASSETCHAINS_MARMARA; bool VERUS_MINTBLOCKS; @@ -71,6 +71,7 @@ uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0; uint32_t ASSETCHAINS_LASTERA = 1; uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS]; +uint8_t ASSETCHAINS_CCDISABLES[256]; #define _ASSETCHAINS_EQUIHASH 0 uint32_t ASSETCHAINS_NUMALGOS = 2; diff --git a/src/komodo_jumblr.h b/src/komodo_jumblr.h index 5a5ad0885..fca7395e5 100755 --- a/src/komodo_jumblr.h +++ b/src/komodo_jumblr.h @@ -629,7 +629,7 @@ uint64_t jumblr_increment(uint8_t r,int32_t height,uint64_t total,uint64_t bigge void jumblr_iteration() { static int32_t lastheight; static uint32_t lasttime; - char *zaddr,*addr,*retstr,secretaddr[64]; cJSON *array; int32_t i,iter,height,acpublic,counter,chosen_one,n; uint64_t smallest,medium,biggest,amount=0,total=0; double fee; struct jumblr_item *ptr,*tmp; uint16_t r,s; + char *zaddr,*addr,*retstr=0,secretaddr[64]; cJSON *array; int32_t i,iter,height,acpublic,counter,chosen_one,n; uint64_t smallest,medium,biggest,amount=0,total=0; double fee; struct jumblr_item *ptr,*tmp; uint16_t r,s; acpublic = ASSETCHAINS_PUBLIC; if ( ASSETCHAINS_SYMBOL[0] == 0 && GetTime() >= KOMODO_SAPLING_DEADLINE ) acpublic = 1; @@ -648,7 +648,7 @@ void jumblr_iteration() } free_json(array); } - free(retstr); + free(retstr), retstr = 0; } } height = (int32_t)chainActive.LastTip()->GetHeight(); @@ -691,7 +691,7 @@ void jumblr_iteration() if ( amount > 0 && (retstr= jumblr_sendt_to_z(Jumblr_deposit,addr,dstr(amount))) != 0 ) { printf("sendt_to_z.(%s)\n",retstr); - free(retstr); + free(retstr), retstr = 0; } free(zaddr); } else printf("no zaddr from jumblr_zgetnewaddress\n"); @@ -723,7 +723,7 @@ void jumblr_iteration() if ( (retstr= jumblr_sendz_to_z(ptr->dest,addr,dstr(total))) != 0 ) { printf("n.%d counter.%d chosen_one.%d send z_to_z.(%s)\n",n,counter,chosen_one,retstr); - free(retstr); + free(retstr), retstr = 0; } ptr->spent = (uint32_t)time(NULL); free(zaddr); @@ -768,7 +768,7 @@ void jumblr_iteration() if ( (retstr= jumblr_sendz_to_t(ptr->dest,secretaddr,dstr(total))) != 0 ) { printf("%s send z_to_t.(%s)\n",secretaddr,retstr); - free(retstr); + free(retstr), retstr = 0; } else printf("null return from jumblr_sendz_to_t\n"); ptr->spent = (uint32_t)time(NULL); break; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 017a17eff..289a84d7c 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1654,9 +1654,10 @@ extern int64_t MAX_MONEY; void komodo_args(char *argv0) { extern const char *Notaries_elected1[][2]; - std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,baseid,len,n,extralen = 0; + std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz,baseid,len,n,extralen = 0; uint64_t ccenables[256]; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); - + memset(ccenables,0,sizeof(ccenables)); + memset(disablebits,0,sizeof(disablebits)); if ( GetBoolArg("-gen", false) != 0 ) { KOMODO_MININGTHREADS = GetArg("-genproclimit",-1); @@ -1779,6 +1780,33 @@ void komodo_args(char *argv0) ASSETCHAINS_BEAMPORT = GetArg("-ac_beam",0); ASSETCHAINS_CODAPORT = GetArg("-ac_coda",0); ASSETCHAINS_MARMARA = GetArg("-ac_marmara",0); + if ( ASSETCHAINS_CC != 0 ) + { + ASSETCHAINS_CCLIB = GetArg("-ac_cclib",""); + Split(GetArg("-ac_ccenable",""), ccenables, 0); + for (i=nonz=0; i<0x100; i++) + { + if ( ccenables[i] != 0 ) + { + nonz++; + fprintf(stderr,"%d ",(uint8_t)(ccenables[i] & 0xff)); + } + } + fprintf(stderr,"nonz.%d ccenables[]\n",nonz); + if ( nonz > 0 ) + { + for (i=0; i<256; i++) + { + ASSETCHAINS_CCDISABLES[i] = 1; + SETBIT(disablebits,i); + } + for (i=0; i<256; i++) + { + CLEARBIT(disablebits,(ccenables[i] & 0xff)); + ASSETCHAINS_CCDISABLES[ccenables[i] & 0xff] = 0; + } + } + } if ( ASSETCHAINS_BEAMPORT != 0 && ASSETCHAINS_CODAPORT != 0 ) { fprintf(stderr,"can only have one of -ac_beam or -ac_coda\n"); @@ -1863,7 +1891,7 @@ void komodo_args(char *argv0) fprintf(stderr,"-ac_script and -ac_marmara are mutually exclusive\n"); exit(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 ) + 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 ) { 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; @@ -1939,8 +1967,22 @@ void komodo_args(char *argv0) extraptr[extralen++] = 'c'; if ( ASSETCHAINS_MARMARA != 0 ) extraptr[extralen++] = ASSETCHAINS_MARMARA; + if ( nonz > 0 ) + { + memcpy(&extraptr[extralen],disablebits,sizeof(disablebits)); + extralen += sizeof(disablebits); + } + if ( ASSETCHAINS_CCLIB.size() > 1 ) + { + for (i=0; i 0 ) ASSETCHAINS_SEED = 1; diff --git a/src/main.cpp b/src/main.cpp index 929ec9d5e..21b6b4eb0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3497,6 +3497,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CDiskBlockPos pos; if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock(): FindUndoPos failed"); + if ( pindex->pprev == 0 ) + fprintf(stderr,"ConnectBlock: unexpected null pprev\n"); if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); diff --git a/src/miner.cpp b/src/miner.cpp index a44eb380c..c71bc2461 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -158,6 +158,7 @@ int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33); int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); CScript Marmara_scriptPubKey(int32_t height,CPubKey pk); CScript MarmaraCoinbaseOpret(uint8_t funcid,int32_t height,CPubKey pk); +bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake) { @@ -540,11 +541,11 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txNew.vout[0].nValue += 5000; pblock->vtx[0] = txNew; - //fprintf(stderr,"ht.%d cmp.%d [%d %d %d %d %d]\n",nHeight,nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && ASSETCHAINS_COMMISSION != 0 && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0,nHeight > 1,ASSETCHAINS_SYMBOL[0] != 0, (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1), ASSETCHAINS_COMMISSION != 0,(commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0); - // check if coinbase transactions must be time locked at current subsidy and prepend the time lock - // to transaction if so, cast for GTE operator if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) { + char checkaddr[64]; + Getscriptaddress(checkaddr,txNew.vout[0].scriptPubKey); + //`fprintf(stderr,"set mining coinbase -> %s\n",checkaddr); txNew.vout.resize(2); txNew.vout[1].nValue = 0; txNew.vout[1].scriptPubKey = MarmaraCoinbaseOpret('C',nHeight,pk); @@ -1605,11 +1606,11 @@ void static BitcoinMiner() int32_t percPoS,z; bool fNegative,fOverflow; HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); - if ( ASSETCHAINS_STAKED < 100 ) + if ( ASSETCHAINS_STAKED < 100 && KOMODO_MININGTHREADS == 0 ) { for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&HASHTarget_POW)[z]); - fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%%\n",percPoS,(int32_t)ASSETCHAINS_STAKED); + fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%% ht.%d\n",percPoS,(int32_t)ASSETCHAINS_STAKED,Mining_height); } } while (true) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 5761b5bf7..24f0c5f98 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -458,10 +458,15 @@ static const CRPCCommand vRPCCommands[] = { "marmara", "marmarainfo", &marmara_info, true }, { "marmara", "marmaracreditloop", &marmara_creditloop, true }, { "marmara", "marmarasettlement", &marmara_settlement, true }, + { "marmara", "marmaralock", &marmara_lock, true }, // Payments { "payments", "paymentsaddress", &paymentsaddress, true }, + { "CClib", "cclibaddress", &cclibaddress, true }, + { "CClib", "cclibinfo", &cclibinfo, true }, + { "CClib", "cclib", &cclib, true }, + // Gateways { "gateways", "gatewaysaddress", &gatewaysaddress, true }, { "gateways", "gatewayslist", &gatewayslist, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 99b7e8678..0166bca41 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -285,7 +285,11 @@ extern UniValue marmara_transfer(const UniValue& params, bool fHelp); extern UniValue marmara_info(const UniValue& params, bool fHelp); extern UniValue marmara_creditloop(const UniValue& params, bool fHelp); extern UniValue marmara_settlement(const UniValue& params, bool fHelp); +extern UniValue marmara_lock(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); +extern UniValue cclibaddress(const UniValue& params, bool fHelp); +extern UniValue cclibinfo(const UniValue& params, bool fHelp); +extern UniValue cclib(const UniValue& params, bool fHelp); extern UniValue gatewaysaddress(const UniValue& params, bool fHelp); extern UniValue gatewayslist(const UniValue& params, bool fHelp); extern UniValue gatewaysinfo(const UniValue& params, bool fHelp); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index b90ceafb5..685018828 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -45,14 +45,14 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, key = *pprivKey; else if (!keystore || !keystore->GetKey(address, key)) return false; - + uint256 hash; try { hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId); } catch (logic_error ex) { return false; } - + if (scriptCode.IsPayToCryptoCondition()) { CC *cc = (CC *)extraData; @@ -75,9 +75,9 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, return false; } } - + vchSig.push_back((unsigned char)nHashType); - + return true; } @@ -128,7 +128,7 @@ std::vector &GetCryptoConditions() static bool initialized = false; static std::vector vCC = std::vector(); CCcontract_info C; - + if (!initialized) { // this should initialize any desired auto-signed crypto-conditions @@ -140,7 +140,7 @@ bool GetCCByUnspendableAddress(struct CCcontract_info *cp, char *addrstr) { std::vector &vCC = GetCryptoConditions(); bool found = false; - + for (int i = 0; i < vCC.size(); i++) { if (strcmp(addrstr, vCC[i].unspendableCCaddr) == 0) @@ -157,7 +157,7 @@ bool CCinitLite(struct CCcontract_info *cp, uint8_t evalcode) { std::vector &vCC = GetCryptoConditions(); bool found = false; - + for (int i = 0; i < vCC.size(); i++) { if (vCC[i].evalcode == evalcode) @@ -172,7 +172,7 @@ bool CCinitLite(struct CCcontract_info *cp, uint8_t evalcode) bool _Getscriptaddress(char *destaddr, const CScript &scriptPubKey) { - CTxDestination address; + CTxDestination address; txnouttype whichType; std::vector> vvch = std::vector>(); if (Solver(scriptPubKey, whichType, vvch) && vvch[0].size() == 20) @@ -199,10 +199,10 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip vector vPK; vector vParams = vector(); COptCCParams p; - + // get information to sign with CCcontract_info C; - + scriptPubKey.IsPayToCryptoCondition(&subScript, vParams); if (vParams.empty()) { @@ -219,12 +219,12 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { p = COptCCParams(vParams[0]); } - + if (p.IsValid() && p.vKeys.size() >= p.n) { bool is1of2 = (p.m == 1 && p.n == 2); CKey privKey; - + // must be a valid cc eval code if (CCinitLite(&C, p.evalCode)) { @@ -232,7 +232,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip if (!is1of2) { bool havePriv = creator.KeyStore().GetKey(p.vKeys[0].GetID(), privKey); - + // if we don't have the private key, it must be the unspendable address if (!havePriv && (p.vKeys[0] == CPubKey(ParseHex(C.CChexstr)))) { @@ -240,9 +240,9 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip std::vector vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv)); privKey.Set(vch.begin(), vch.end(), false); } - + CC *cc = CCcond1(p.evalCode, p.vKeys[0]); - + if (cc) { vector vch; @@ -254,7 +254,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { fprintf(stderr,"vin has 1of1 CC signing error with address.(%s)\n", p.vKeys[0].GetID().ToString().c_str()); } - + cc_free(cc); return ret.size() != 0; } @@ -266,7 +266,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { if (creator.IsKeystoreValid() && creator.KeyStore().GetKey(pk.GetID(), privKey) && privKey.IsValid()) break; - + if (pk == CPubKey(ParseHex(C.CChexstr))) { privKey = CKey(); @@ -275,12 +275,12 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip break; } } - + if (!privKey.IsValid()) return false; - + CC *cc = CCcond1of2(p.evalCode, p.vKeys[0], p.vKeys[1]); - + if (cc) { vector vch; @@ -292,7 +292,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { fprintf(stderr,"vin has 1of2 CC signing error with addresses.(%s)\n(%s)\n", p.vKeys[0].GetID().ToString().c_str(), p.vKeys[1].GetID().ToString().c_str()); } - + cc_free(cc); return ret.size() != 0; } @@ -314,9 +314,9 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP CScript scriptRet; uint160 h160; ret.clear(); - + vector vSolutions; - + if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) { // if this is a CLTV script, solve for the destination after CLTV @@ -324,10 +324,10 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP { uint8_t pushOp = scriptPubKey[0]; uint32_t scriptStart = pushOp + 3; - + // check post CLTV script CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); - + // check again with only postfix subscript if (!Solver(postfix, whichTypeRet, vSolutions)) return false; @@ -335,44 +335,44 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP else return false; } - + CKeyID keyID; - + switch (whichTypeRet) { - case TX_NONSTANDARD: - case TX_NULL_DATA: - return false; - case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId)) + case TX_NONSTANDARD: + case TX_NULL_DATA: return false; - else - { - CPubKey vch; - creator.KeyStore().GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); - } - return true; - case TX_SCRIPTHASH: - if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { - ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId); + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId)) + return false; + else + { + CPubKey vch; + creator.KeyStore().GetPubKey(keyID, vch); + ret.push_back(ToByteVector(vch)); + } return true; - } - return false; - - case TX_CRYPTOCONDITION: - return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId); - - case TX_MULTISIG: - ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, ret, consensusBranchId)); - - default: - return false; + case TX_SCRIPTHASH: + if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; + + case TX_CRYPTOCONDITION: + return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId); + + case TX_MULTISIG: + ret.push_back(valtype()); // workaround CHECKMULTISIG bug + return (SignN(vSolutions, creator, scriptPubKey, ret, consensusBranchId)); + + default: + return false; } } @@ -399,7 +399,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu txnouttype whichType; solved = SignStep(creator, script, result, whichType, consensusBranchId); CScript subscript; - + if (solved && whichType == TX_SCRIPTHASH) { // Solver returns the subscript that needs to be evaluated; @@ -409,9 +409,9 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu solved = solved && SignStep(creator, script, result, whichType, consensusBranchId) && whichType != TX_SCRIPTHASH; result.push_back(std::vector(subscript.begin(), subscript.end())); } - + sigdata.scriptSig = PushAll(result); - + // Test solution return solved && VerifyScript(sigdata.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker(), consensusBranchId); } @@ -431,19 +431,19 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur } bool SignSignature( - const CKeyStore &keystore, - const CScript& fromPubKey, - CMutableTransaction& txTo, - unsigned int nIn, - const CAmount& amount, - int nHashType, - uint32_t consensusBranchId) + const CKeyStore &keystore, + const CScript& fromPubKey, + CMutableTransaction& txTo, + unsigned int nIn, + const CAmount& amount, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); - + CTransaction txToConst(txTo); TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType); - + SignatureData sigdata; bool ret = ProduceSignature(creator, fromPubKey, sigdata, consensusBranchId); UpdateTransaction(txTo, nIn, sigdata); @@ -451,24 +451,24 @@ bool SignSignature( } bool SignSignature( - const CKeyStore &keystore, - const CTransaction& txFrom, - CMutableTransaction& txTo, - unsigned int nIn, - int nHashType, - uint32_t consensusBranchId) + const CKeyStore &keystore, + const CTransaction& txFrom, + CMutableTransaction& txTo, + unsigned int nIn, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; assert(txin.prevout.n < txFrom.vout.size()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; - + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType, consensusBranchId); } static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const vector& vSolutions, - const vector& sigs1, const vector& sigs2, uint32_t consensusBranchId) + const vector& vSolutions, + const vector& sigs1, const vector& sigs2, uint32_t consensusBranchId) { // Combine all the signatures we've got: set allsigs; @@ -482,7 +482,7 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi if (!v.empty()) allsigs.insert(v); } - + // Build a map of pubkey -> signature by matching sigs to pubkeys: assert(vSolutions.size() > 1); unsigned int nSigsRequired = vSolutions.front()[0]; @@ -495,7 +495,7 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi const valtype& pubkey = vSolutions[i+1]; if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - + if (checker.CheckSig(sig, pubkey, scriptPubKey, consensusBranchId)) { sigs[pubkey] = sig; @@ -517,108 +517,108 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi // Fill any missing with OP_0: for (unsigned int i = nSigsHave; i < nSigsRequired; i++) result.push_back(valtype()); - + return result; } namespace { -struct Stacks -{ - std::vector script; - - Stacks() {} - explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_) {} - explicit Stacks(const SignatureData& data, uint32_t consensusBranchId) { - EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), consensusBranchId); - } - - SignatureData Output() const { - SignatureData result; - result.scriptSig = PushAll(script); - return result; - } -}; + struct Stacks + { + std::vector script; + + Stacks() {} + explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_) {} + explicit Stacks(const SignatureData& data, uint32_t consensusBranchId) { + EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), consensusBranchId); + } + + SignatureData Output() const { + SignatureData result; + result.scriptSig = PushAll(script); + return result; + } + }; } static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const txnouttype txType, const vector& vSolutions, - Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId) + const txnouttype txType, const vector& vSolutions, + Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId) { switch (txType) { - case TX_NONSTANDARD: - case TX_NULL_DATA: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.script.size() >= sigs2.script.size()) - return sigs1; - return sigs2; - case TX_PUBKEY: - case TX_PUBKEYHASH: - case TX_CRYPTOCONDITION: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.script.empty() || sigs1.script[0].empty()) + case TX_NONSTANDARD: + case TX_NULL_DATA: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.script.size() >= sigs2.script.size()) + return sigs1; return sigs2; - return sigs1; - case TX_SCRIPTHASH: - if (sigs1.script.empty() || sigs1.script.back().empty()) - return sigs2; - else if (sigs2.script.empty() || sigs2.script.back().empty()) + case TX_PUBKEY: + case TX_PUBKEYHASH: + case TX_CRYPTOCONDITION: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.script.empty() || sigs1.script[0].empty()) + return sigs2; return sigs1; - else - { - // Recur to combine: - valtype spk = sigs1.script.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - vector > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.script.pop_back(); - sigs2.script.pop_back(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, consensusBranchId); - result.script.push_back(spk); - return result; - } - case TX_MULTISIG: - return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, consensusBranchId)); - default: - return Stacks(); + case TX_SCRIPTHASH: + if (sigs1.script.empty() || sigs1.script.back().empty()) + return sigs2; + else if (sigs2.script.empty() || sigs2.script.back().empty()) + return sigs1; + else + { + // Recur to combine: + valtype spk = sigs1.script.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.script.pop_back(); + sigs2.script.pop_back(); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, consensusBranchId); + result.script.push_back(spk); + return result; + } + case TX_MULTISIG: + return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, consensusBranchId)); + default: + return Stacks(); } } SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const SignatureData& scriptSig1, const SignatureData& scriptSig2, - uint32_t consensusBranchId) + const SignatureData& scriptSig1, const SignatureData& scriptSig2, + uint32_t consensusBranchId) { txnouttype txType; vector > vSolutions; Solver(scriptPubKey, txType, vSolutions); - + return CombineSignatures( - scriptPubKey, checker, txType, vSolutions, - Stacks(scriptSig1, consensusBranchId), - Stacks(scriptSig2, consensusBranchId), - consensusBranchId).Output(); + scriptPubKey, checker, txType, vSolutions, + Stacks(scriptSig1, consensusBranchId), + Stacks(scriptSig2, consensusBranchId), + consensusBranchId).Output(); } namespace { -/** Dummy signature checker which accepts all signatures. */ -class DummySignatureChecker : public BaseSignatureChecker -{ -public: - DummySignatureChecker() {} - - bool CheckSig( - const std::vector& scriptSig, - const std::vector& vchPubKey, - const CScript& scriptCode, - uint32_t consensusBranchId) const + /** Dummy signature checker which accepts all signatures. */ + class DummySignatureChecker : public BaseSignatureChecker { - return true; - } -}; -const DummySignatureChecker dummyChecker; + public: + DummySignatureChecker() {} + + bool CheckSig( + const std::vector& scriptSig, + const std::vector& vchPubKey, + const CScript& scriptCode, + uint32_t consensusBranchId) const + { + return true; + } + }; + const DummySignatureChecker dummyChecker; } const BaseSignatureChecker& DummySignatureCreator::Checker() const @@ -627,12 +627,12 @@ const BaseSignatureChecker& DummySignatureCreator::Checker() const } bool DummySignatureCreator::CreateSig( - std::vector& vchSig, - const CKeyID& keyid, - const CScript& scriptCode, - uint32_t consensusBranchId, - CKey *key, - void *extraData) const + std::vector& vchSig, + const CKeyID& keyid, + const CScript& scriptCode, + uint32_t consensusBranchId, + CKey *key, + void *extraData) const { // Create a dummy signature that is a valid DER-encoding vchSig.assign(72, '\000'); @@ -647,3 +647,4 @@ bool DummySignatureCreator::CreateSig( vchSig[6 + 33 + 32] = SIGHASH_ALL; return true; } + diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 538155331..174f447f0 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5094,8 +5094,8 @@ int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) if (!EnsureWalletIsAvailable(0)) return 0; - const CKeyStore& keystore = *pwalletMain; assert(pwalletMain != NULL); + const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); utxovalue = 0; memset(&utxotxid,0,sizeof(utxotxid)); @@ -5327,6 +5327,43 @@ UniValue channelsaddress(const UniValue& params, bool fHelp) return(result); } +UniValue cclibaddress(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; std::vector pubkey; + cp = CCinit(&C,EVAL_FIRSTUSER); + if ( fHelp || params.size() > 1 ) + throw runtime_error("cclibaddress [pubkey]\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + if ( params.size() == 1 ) + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp,(char *)"CClib",pubkey)); +} + +UniValue cclibinfo(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_FIRSTUSER); + if ( fHelp || params.size() > 0 ) + throw runtime_error("cclibinfo\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + return(CClib_info(cp)); +} + +UniValue cclib(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *method; cJSON *jsonparams; + cp = CCinit(&C,EVAL_FIRSTUSER); + if ( fHelp || params.size() > 2 ) + throw runtime_error("cclib method [JSON params]\n"); + if ( ensure_CCrequirements() < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + method = (char *)params[0].get_str().c_str(); + jsonparams = cJSON_Parse(params[1].get_str().c_str()); + return(CClib(cp,method,jsonparams)); +} + UniValue oraclesaddress(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; std::vector pubkey; @@ -5558,8 +5595,9 @@ UniValue marmara_receive(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 batontxid; std::vector senderpub; int64_t amount; int32_t matures; std::string currency; if ( fHelp || (params.size() != 5 && params.size() != 4) ) { + // automatic flag -> lsb of matures // 1st marmarareceive 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 1440 - // after marmarareceive 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 3903 bf6b4d42aa3ce974c853d73b06c78597dd3b5fb493d5d0d944f72c2017f561ad + // after marmarareceive 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 1168 d72d87aa0d50436de695c93e2bf3d7273c63c92ef6307913aa01a6ee6a16548b throw runtime_error("marmarareceive senderpk amount currency matures batontxid\n"); } if ( ensure_CCrequirements() < 0 ) @@ -5578,7 +5616,7 @@ UniValue marmara_receive(const UniValue& params, bool fHelp) matures = atol(params[3].get_str().c_str()); batontxid = Parseuint256((char *)params[4].get_str().c_str()); } else matures = atol(params[3].get_str().c_str()) + chainActive.LastTip()->GetHeight() + 1; - return(MarmaraReceive(0,pubkey2pk(senderpub),amount,currency,matures,batontxid)); + return(MarmaraReceive(0,pubkey2pk(senderpub),amount,currency,matures,batontxid,true)); } UniValue marmara_issue(const UniValue& params, bool fHelp) @@ -5586,7 +5624,9 @@ UniValue marmara_issue(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 approvaltxid; std::vector receiverpub; int64_t amount; int32_t matures; std::string currency; if ( fHelp || params.size() != 5 ) { - // marmaraissue 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 3903 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be + // marmaraissue 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 1168 32da4cb3e886ee42de90b4a15042d71169077306badf909099c5c5c692df3f27 + // marmaraissue 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 700 MARMARA 2629 11fe8bf1de80c2ef69124d08907f259aef7f41e3a632ca2d48ad072a8c8f3078 -> 335df3a5dd6b92a3d020c9465d4d76e0d8242126106b83756dcecbad9813fdf3 + throw runtime_error("marmaraissue receiverpk amount currency matures approvaltxid\n"); } if ( ensure_CCrequirements() < 0 ) @@ -5609,7 +5649,7 @@ UniValue marmara_transfer(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 approvaltxid,batontxid; std::vector receiverpub; int64_t amount; int32_t matures; std::string currency; std::vector creditloop; if ( fHelp || params.size() != 5 ) { - // marmaratransfer 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 3903 748a4c80e6f6b725340fb0f52738f38a11c422d59b3034c8366b3d7b33c99a1e + // marmaratransfer 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 1168 1506c774e4b2804a6e25260920840f4cfca8d1fb400e69fe6b74b8e593dbedc5 throw runtime_error("marmaratransfer receiverpk amount currency matures approvaltxid\n"); } if ( ensure_CCrequirements() < 0 ) @@ -5679,7 +5719,7 @@ UniValue marmara_settlement(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1 ) { // marmarasettlement 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be - // marmarasettlement cc23bf81733556dc06db2fd9c9f4178cad44bdc237d6e62101cf0cdafb5195f7 + // marmarasettlement ff3e259869196f3da9b5ea3f9e088a76c4fc063cf36ab586b652e121d441a603 throw runtime_error("marmarasettlement batontxid\n"); } if ( ensure_CCrequirements() < 0 ) @@ -5689,6 +5729,20 @@ UniValue marmara_settlement(const UniValue& params, bool fHelp) return(result); } +UniValue marmara_lock(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); int64_t amount; int32_t height; + if ( fHelp || params.size() > 2 || params.size() == 0 ) + { + throw runtime_error("marmaralock amount unlockht\n"); + } + amount = atof(params[0].get_str().c_str()) * COIN + 0.00000000499999; + if ( params.size() == 2 ) + height = atol(params[1].get_str().c_str()); + else height = chainActive.LastTip()->GetHeight() + 1; + return(MarmaraLock(0,amount,height)); +} + UniValue channelslist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) diff --git a/zcutil/build-mac.sh b/zcutil/build-mac.sh index 138508cb5..a3619111f 100755 --- a/zcutil/build-mac.sh +++ b/zcutil/build-mac.sh @@ -42,6 +42,21 @@ PREFIX="$(pwd)/depends/$TRIPLET" make "$@" -C ./depends/ V=1 NO_QT=1 NO_PROTON=1 +#BUILD CCLIB + +WD=$PWD +cd src/cc +echo $PWD + +if make "$@"; then + echo CCLIB BUILD SUCCESSFUL +else + echo CCLIB BUILD FAILED + exit 1 +fi + +cd $WD + ./autogen.sh CPPFLAGS="-I$PREFIX/include -arch x86_64" LDFLAGS="-L$PREFIX/lib -arch x86_64 -Wl,-no_pie" \ CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Werror -g -Wl,-undefined -Wl,dynamic_lookup' \ diff --git a/zcutil/build.sh b/zcutil/build.sh index 08517475c..68239e16e 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -101,6 +101,21 @@ eval "$MAKE" --version as --version ld -v +#BUILD CCLIB + +WD=$PWD +cd src/cc +echo $PWD + +if make "$@"; then + echo CCLIB BUILD SUCCESSFUL +else + echo CCLIB BUILD FAILED + exit 1 +fi + +cd $WD + HOST="$HOST" BUILD="$BUILD" NO_PROTON="$PROTON_ARG" "$MAKE" "$@" -C ./depends/ V=1 ./autogen.sh