// Copyright (c) 2016-2024 The Hush developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html /****************************************************************************** * 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. * * * ******************************************************************************/ #ifndef HUSH_NSPVFULLNODE_H #define HUSH_NSPVFULLNODE_H // NSPV_get... functions need to return the exact serialized length, which is the size of the structure minus size of pointers, plus size of allocated data #include "notarizationdb.h" #include "rpc/server.h" static std::map nspv_remote_commands = {{"channelsopen", true},{"channelspayment", true},{"channelsclose", true},{"channelsrefund", true}, {"channelslist", true},{"channelsinfo", true},{"oraclescreate", true},{"oraclesfund", true},{"oraclesregister", true},{"oraclessubscribe", true}, {"oraclesdata", true},{"oraclesinfo", false},{"oracleslist", false},{"gatewaysbind", true},{"gatewaysdeposit", true},{"gatewaysclaim", true},{"gatewayswithdraw", true}, {"gatewayspartialsign", true},{"gatewayscompletesigning", true},{"gatewaysmarkdone", true},{"gatewayspendingdeposits", true},{"gatewayspendingwithdraws", true}, {"gatewaysprocessed", true},{"gatewaysinfo", false},{"gatewayslist", false},{"faucetfund", true},{"faucetget", true}}; struct NSPV_ntzargs { uint256 txid,desttxid,blockhash; int32_t txidht,ntzheight; }; int32_t NSPV_notarization_find(struct NSPV_ntzargs *args,int32_t height,int32_t dir) { int32_t ntzheight = 0; uint256 hashBlock; CTransaction tx; Notarization nota; char *symbol; std::vector opret; symbol = (SMART_CHAIN_SYMBOL[0] == 0) ? (char *)"HUSH3" : SMART_CHAIN_SYMBOL; memset(args,0,sizeof(*args)); if ( dir > 0 ) height += 10; if ( (args->txidht= ScanNotarizationsDB(height,symbol,1440,nota)) == 0 ) return(-1); args->txid = nota.first; if ( !GetTransaction(args->txid,tx,hashBlock,false) || tx.vout.size() < 2 ) return(-2); GetOpReturnData(tx.vout[1].scriptPubKey,opret); if ( opret.size() >= 32*2+4 ) args->desttxid = NSPV_opretextract(&args->ntzheight,&args->blockhash,symbol,opret,args->txid); return(args->ntzheight); } int32_t NSPV_notarized_bracket(struct NSPV_ntzargs *prev,struct NSPV_ntzargs *next,int32_t height) { uint256 bhash; int32_t txidht,ntzht,nextht,i=0; memset(prev,0,sizeof(*prev)); memset(next,0,sizeof(*next)); if ( (ntzht= NSPV_notarization_find(prev,height,-1)) < 0 || ntzht > height || ntzht == 0 ) return(-1); txidht = height+1; while ( (ntzht= NSPV_notarization_find(next,txidht,1)) < height ) { nextht = next->txidht + 10*i; //fprintf(stderr,"found forward ntz, but ntzht.%d vs height.%d, txidht.%d -> nextht.%d\n",next->ntzheight,height,txidht,nextht); memset(next,0,sizeof(*next)); txidht = nextht; if ( ntzht <= 0 ) break; if ( i++ > 10 ) break; } return(0); } int32_t NSPV_ntzextract(struct NSPV_ntz *ptr,uint256 ntztxid,int32_t txidht,uint256 desttxid,int32_t ntzheight) { CBlockIndex *pindex; ptr->blockhash = *chainActive[ntzheight]->phashBlock; ptr->height = ntzheight; ptr->txidheight = txidht; ptr->othertxid = desttxid; ptr->txid = ntztxid; if ( (pindex= hush_chainactive(ptr->txidheight)) != 0 ) ptr->timestamp = pindex->nTime; return(0); } int32_t NSPV_getntzsresp(struct NSPV_ntzsresp *ptr,int32_t origreqheight) { struct NSPV_ntzargs prev,next; int32_t reqheight = origreqheight; if ( reqheight < chainActive.LastTip()->GetHeight() ) reqheight++; if ( NSPV_notarized_bracket(&prev,&next,reqheight) == 0 ) { if ( prev.ntzheight != 0 ) { ptr->reqheight = origreqheight; if ( NSPV_ntzextract(&ptr->prevntz,prev.txid,prev.txidht,prev.desttxid,prev.ntzheight) < 0 ) return(-1); } if ( next.ntzheight != 0 ) { if ( NSPV_ntzextract(&ptr->nextntz,next.txid,next.txidht,next.desttxid,next.ntzheight) < 0 ) return(-1); } } return(sizeof(*ptr)); } int32_t NSPV_setequihdr(struct NSPV_equihdr *hdr,int32_t height) { CBlockIndex *pindex; if ( (pindex= hush_chainactive(height)) != 0 ) { hdr->nVersion = pindex->nVersion; if ( pindex->pprev == 0 ) return(-1); hdr->hashPrevBlock = pindex->pprev->GetBlockHash(); hdr->hashMerkleRoot = pindex->hashMerkleRoot; hdr->hashFinalSaplingRoot = pindex->hashFinalSaplingRoot; hdr->nTime = pindex->nTime; hdr->nBits = pindex->nBits; hdr->nNonce = pindex->nNonce; memcpy(hdr->nSolution,&pindex->GetBlockHeader().nSolution[0],sizeof(hdr->nSolution)); return(sizeof(*hdr)); } return(-1); } int32_t NSPV_getinfo(struct NSPV_inforesp *ptr,int32_t reqheight) { int32_t prevMoMheight,len = 0; CBlockIndex *pindex, *pindex2; struct NSPV_ntzsresp pair; if ( (pindex= chainActive.LastTip()) != 0 ) { ptr->height = pindex->GetHeight(); ptr->blockhash = pindex->GetBlockHash(); memset(&pair,0,sizeof(pair)); if ( NSPV_getntzsresp(&pair,ptr->height-1) < 0 ) return(-1); ptr->notarization = pair.prevntz; if ( (pindex2= hush_chainactive(ptr->notarization.txidheight)) != 0 ) ptr->notarization.timestamp = pindex->nTime; //fprintf(stderr, "timestamp.%i\n", ptr->notarization.timestamp ); if ( reqheight == 0 ) reqheight = ptr->height; ptr->hdrheight = reqheight; ptr->version = NSPV_PROTOCOL_VERSION; if ( NSPV_setequihdr(&ptr->H,reqheight) < 0 ) return(-1); return(sizeof(*ptr)); } else return(-1); } int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter) { int64_t total = 0,interest=0; uint32_t locktime; int32_t ind=0,tipheight,maxlen,txheight,n = 0,len = 0; std::vector > unspentOutputs; //SetCCunspents(unspentOutputs,coinaddr,isCC); maxlen = MAX_BLOCK_SIZE(tipheight) - 512; maxlen /= sizeof(*ptr->utxos); strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); ptr->CCflag = isCC; ptr->filter = filter; if ( skipcount < 0 ) skipcount = 0; if ( (ptr->numutxos= (int32_t)unspentOutputs.size()) >= 0 && ptr->numutxos < maxlen ) { tipheight = chainActive.LastTip()->GetHeight(); ptr->nodeheight = tipheight; if ( skipcount >= ptr->numutxos ) skipcount = ptr->numutxos-1; ptr->skipcount = skipcount; if ( ptr->numutxos-skipcount > 0 ) { ptr->utxos = (struct NSPV_utxoresp *)calloc(ptr->numutxos-skipcount,sizeof(*ptr->utxos)); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { // if gettxout is != null to handle mempool { if ( n >= skipcount && myIsutxo_spentinmempool(ignoretxid,ignorevin,it->first.txhash,(int32_t)it->first.index) == 0 ) { ptr->utxos[ind].txid = it->first.txhash; ptr->utxos[ind].vout = (int32_t)it->first.index; ptr->utxos[ind].satoshis = it->second.satoshis; ptr->utxos[ind].height = it->second.blockHeight; ind++; total += it->second.satoshis; } n++; } } } ptr->numutxos = ind; if ( len < maxlen ) { len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->utxos)*ptr->numutxos - sizeof(ptr->utxos)); //fprintf(stderr,"getaddressutxos for %s -> n.%d:%d total %.8f interest %.8f len.%d\n",coinaddr,n,ptr->numutxos,dstr(total),dstr(interest),len); ptr->total = total; ptr->interest = interest; return(len); } } if ( ptr->utxos != 0 ) free(ptr->utxos); memset(ptr,0,sizeof(*ptr)); return(0); } class BaseCCChecker { public: /// base check function /// @param vouts vouts where checked vout and opret are /// @param nvout vout index to check /// @param evalcode which must be in the opret /// @param funcids allowed funcids in string /// @param filtertxid txid that should be in the opret (after funcid) virtual bool checkCC(uint256 txid, const std::vector &vouts, int32_t nvout, uint8_t evalcode, std::string funcids, uint256 filtertxid) = 0; }; /// default cc vout checker for use in NSPV_getccmoduleutxos /// checks if a vout is cc, has required evalcode, allowed funcids and txid /// check both cc opret and last vout opret /// maybe customized in via inheritance class DefaultCCChecker : public BaseCCChecker { private: public: DefaultCCChecker() { } virtual bool checkCC(uint256 txid, const std::vector &vouts, int32_t nvout, uint8_t evalcode, std::string funcids, uint256 filtertxid) { CScript opret, dummy; std::vector< vscript_t > vParams; vscript_t vopret; if (nvout < vouts.size()) { // first check if it is cc vout if (vouts[nvout].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams)) { // try to find cc opret if (vParams.size() > 0) { COptCCParams p(vParams[0]); // parse vout data if (p.vData.size() > 0) { vopret = p.vData[0]; // get opret data } } // if no cc opret check last vout opret if (vopret.size() == 0) { GetOpReturnData(vouts.back().scriptPubKey, vopret); } if (vopret.size() > 2) { uint8_t opretEvalcode, opretFuncid; uint256 opretTxid; bool isEof = true; bool isCreateTx = false; // parse opret first 3 fields: bool parseOk = E_UNMARSHAL(vopret, ss >> opretEvalcode; ss >> opretFuncid; if (funcids.size() > 0 && opretFuncid == funcids[0]) // this means that we check txid only for second+ funcid in array (considering that the first funcid is the creation txid itself like tokens) { isCreateTx = true; } else { ss >> opretTxid; isCreateTx = false; } isEof = ss.eof(); ); opretTxid = revuint256(opretTxid); std::cerr << __func__ << " " << "opretEvalcode=" << opretEvalcode << " opretFuncid=" << (char)opretFuncid << " isCreateTx=" << isCreateTx << " opretTxid=" << opretTxid.GetHex() << std::endl; if( parseOk /*parseOk=true if eof reached*/|| !isEof /*more data means okay*/) { if (evalcode == opretEvalcode && std::find(funcids.begin(), funcids.end(), (char)opretFuncid) != funcids.end() && (isCreateTx && filtertxid == txid || !isCreateTx && filtertxid == opretTxid)) { return true; } } } } } return false; } }; static class DefaultCCChecker defaultCCChecker; // table of pluggable cc vout checkers for usage in NSPV_getccmoduleutxos // if the checker is not in the table for a evalcode then defaultCCChecker is used static std::map ccCheckerTable = { }; int32_t NSPV_getaddresstxids(struct NSPV_txidsresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter) { int32_t maxlen,txheight,ind=0,n = 0,len = 0; CTransaction tx; uint256 hashBlock; std::vector > txids; //SetCCtxids(txids,coinaddr,isCC); ptr->nodeheight = chainActive.LastTip()->GetHeight(); maxlen = MAX_BLOCK_SIZE(ptr->nodeheight) - 512; maxlen /= sizeof(*ptr->txids); strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); ptr->CCflag = isCC; ptr->filter = filter; if ( skipcount < 0 ) skipcount = 0; if ( (ptr->numtxids= (int32_t)txids.size()) >= 0 && ptr->numtxids < maxlen ) { if ( skipcount >= ptr->numtxids ) skipcount = ptr->numtxids-1; ptr->skipcount = skipcount; if ( ptr->numtxids-skipcount > 0 ) { ptr->txids = (struct NSPV_txidresp *)calloc(ptr->numtxids-skipcount,sizeof(*ptr->txids)); for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) { if ( n >= skipcount ) { ptr->txids[ind].txid = it->first.txhash; ptr->txids[ind].vout = (int32_t)it->first.index; ptr->txids[ind].satoshis = (int64_t)it->second; ptr->txids[ind].height = (int64_t)it->first.blockHeight; ind++; } n++; } } ptr->numtxids = ind; len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->txids)*ptr->numtxids - sizeof(ptr->txids)); return(len); } if ( ptr->txids != 0 ) free(ptr->txids); memset(ptr,0,sizeof(*ptr)); return(0); } int32_t NSPV_mempoolfuncs(bits256 *satoshisp,int32_t *vindexp,std::vector &txids,char *coinaddr,bool isCC,uint8_t funcid,uint256 txid,int32_t vout) { int32_t num = 0,vini = 0,vouti = 0; uint8_t evalcode=0,func=0; std::vector vopret; char destaddr[64]; *vindexp = -1; memset(satoshisp,0,sizeof(*satoshisp)); /* if ( funcid == NSPV_CC_TXIDS) { std::vector > tmp_txids; uint256 tmp_txid,hashBlock; int32_t n=0,skipcount=vout>>16; uint8_t eval=(vout>>8)&0xFF, func=vout&0xFF; CTransaction tx; SetCCtxids(tmp_txids,coinaddr,isCC); if ( skipcount < 0 ) skipcount = 0; if ( skipcount >= tmp_txids.size() ) skipcount = tmp_txids.size()-1; if ( tmp_txids.size()-skipcount > 0 ) { for (std::vector >::const_iterator it=tmp_txids.begin(); it!=tmp_txids.end(); it++) { if (txid!=zeroid || func!=0) { myGetTransaction(it->first.txhash,tx,hashBlock); std::vector> oprets; uint256 tokenid,txid; std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; if (DecodeTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { vopret=vOpretExtra; } else GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey, vopret); script = (uint8_t *)vopret.data(); if ( vopret.size() > 2 && script[0]==eval ) { switch (eval) { case EVAL_CHANNELS:EVAL_PEGS:EVAL_ORACLES:EVAL_GAMES:EVAL_IMPORTGATEWAY:EVAL_ROGUE: E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> tmp_txid;); if (e!=eval || (txid!=zeroid && txid!=tmp_txid) || (func!=0 && f!=func)) continue; break; case EVAL_TOKENS:EVAL_DICE:EVAL_DILITHIUM:EVAL_FAUCET:EVAL_LOTO:EVAL_PAYMENTS:EVAL_REWARDS: E_UNMARSHAL(vopret,ss >> e; ss >> f;); if (e!=eval || (func!=0 && f!=func)) continue; break; default: break; } } } if ( n >= skipcount ) txids.push_back(it->first.txhash); n++; } return (n-skipcount); } return (0); } */ if ( mempool.size() == 0 ) return(0); /* if ( funcid == NSPV_MEMPOOL_CCEVALCODE ) { isCC = true; evalcode = vout & 0xff; func = (vout >> 8) & 0xff; } */ LOCK(mempool.cs); BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) { const CTransaction &tx = e.GetTx(); const uint256 &hash = tx.GetHash(); if ( funcid == NSPV_MEMPOOL_ALL ) { txids.push_back(hash); num++; continue; } else if ( funcid == NSPV_MEMPOOL_INMEMPOOL ) { if ( hash == txid ) { txids.push_back(hash); return(++num); } continue; } else if ( funcid == NSPV_MEMPOOL_CCEVALCODE ) { if ( tx.vout.size() > 1 ) { CScript scriptPubKey = tx.vout[tx.vout.size()-1].scriptPubKey; if ( GetOpReturnData(scriptPubKey,vopret) != 0 ) { if ( vopret[0] == evalcode && vopret[1] == func ) { txids.push_back(hash); num++; } } } continue; } if ( funcid == NSPV_MEMPOOL_ISSPENT ) { BOOST_FOREACH(const CTxIn &txin,tx.vin) { //fprintf(stderr,"%s/v%d ",uint256_str(str,txin.prevout.hash),txin.prevout.n); if ( txin.prevout.n == vout && txin.prevout.hash == txid ) { txids.push_back(hash); *vindexp = vini; return(++num); } vini++; } } else if ( funcid == NSPV_MEMPOOL_ADDRESS ) { BOOST_FOREACH(const CTxOut &txout,tx.vout) { if ( txout.scriptPubKey.IsPayToCryptoCondition() == isCC ) { Getscriptaddress(destaddr,txout.scriptPubKey); if ( strcmp(destaddr,coinaddr) == 0 ) { txids.push_back(hash); *vindexp = vouti; if ( num < 4 ) satoshisp->ulongs[num] = txout.nValue; num++; } } vouti++; } } //fprintf(stderr,"are vins for %s\n",uint256_str(str,hash)); } return(num); } int32_t NSPV_mempooltxids(struct NSPV_mempoolresp *ptr,char *coinaddr,uint8_t isCC,uint8_t funcid,uint256 txid,int32_t vout) { std::vector txids; bits256 satoshis; uint256 tmp,tmpdest; int32_t i,len = 0; ptr->nodeheight = chainActive.LastTip()->GetHeight(); strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); ptr->CCflag = isCC; ptr->txid = txid; ptr->vout = vout; ptr->funcid = funcid; if ( NSPV_mempoolfuncs(&satoshis,&ptr->vindex,txids,coinaddr,isCC,funcid,txid,vout) >= 0 ) { if ( (ptr->numtxids= (int32_t)txids.size()) >= 0 ) { if ( ptr->numtxids > 0 ) { ptr->txids = (uint256 *)calloc(ptr->numtxids,sizeof(*ptr->txids)); for (i=0; inumtxids; i++) { tmp = txids[i]; dragon_rwbignum(0,(uint8_t *)&tmp,sizeof(*ptr->txids),(uint8_t *)&ptr->txids[i]); } } if ( funcid == NSPV_MEMPOOL_ADDRESS ) { memcpy(&tmp,&satoshis,sizeof(tmp)); dragon_rwbignum(0,(uint8_t *)&tmp,sizeof(ptr->txid),(uint8_t *)&ptr->txid); } len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->txids)*ptr->numtxids - sizeof(ptr->txids)); return(len); } } if ( ptr->txids != 0 ) free(ptr->txids); memset(ptr,0,sizeof(*ptr)); return(0); } int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n) { std::vector txids; int32_t i,len = 0; UniValue result; std::string response; UniValue request(UniValue::VOBJ),rpc_result(UniValue::VOBJ); JSONRequest jreq; CPubKey mypk; try { request.read(json,n); jreq.parse(request); strcpy(ptr->method,jreq.strMethod.c_str()); len+=sizeof(ptr->method); std::map::iterator it = nspv_remote_commands.find(jreq.strMethod); if (it==nspv_remote_commands.end()) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not supported!"); const CRPCCommand *cmd=tableRPC[jreq.strMethod]; if (!cmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); if (it->second) { if (!request.exists("mypk")) throw JSONRPCError(RPC_PARSE_ERROR, "No pubkey supplied in remote rpc request, necessary for this type of rpc"); std::string str=request["mypk"].get_str(); mypk=pubkey2pk(ParseHex(str)); if (!mypk.IsValid()) throw JSONRPCError(RPC_PARSE_ERROR, "Not valid pubkey passed in remote rpc call"); } if ((result = cmd->actor(jreq.params,false,mypk)).isObject() || result.isArray()) { rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); response=rpc_result.write(); memcpy(ptr->json,response.c_str(),response.size()); len+=response.size(); return (len); } else throw JSONRPCError(RPC_MISC_ERROR, "Error in executing RPC on remote node"); } catch (const UniValue& objError) { rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); response=rpc_result.write(); } catch (const runtime_error& e) { rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); response=rpc_result.write(); } catch (const std::exception& e) { rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); response=rpc_result.write(); } memcpy(ptr->json,response.c_str(),response.size()); len+=response.size(); return (len); } uint8_t *NSPV_getrawtx(CTransaction &tx,uint256 &hashBlock,int32_t *txlenp,uint256 txid) { uint8_t *rawtx = 0; *txlenp = 0; { if (!GetTransaction(txid, tx, hashBlock, false)) { //fprintf(stderr,"error getting transaction %s\n",txid.GetHex().c_str()); return(0); } string strHex = EncodeHexTx(tx); *txlenp = (int32_t)strHex.size() >> 1; if ( *txlenp > 0 ) { rawtx = (uint8_t *)calloc(1,*txlenp); decode_hex(rawtx,*txlenp,(char *)strHex.c_str()); } } return(rawtx); } int32_t NSPV_sendrawtransaction(struct NSPV_broadcastresp *ptr,uint8_t *data,int32_t n) { CTransaction tx; ptr->retcode = 0; if ( NSPV_txextract(tx,data,n) == 0 ) { //LOCK(cs_main); ptr->txid = tx.GetHash(); //fprintf(stderr,"try to addmempool transaction %s\n",ptr->txid.GetHex().c_str()); if ( myAddtomempool(tx) != 0 ) { ptr->retcode = 1; //int32_t i; //for (i=0; itxid.GetHex().c_str(),ptr->retcode); RelayTransaction(tx); } else ptr->retcode = -3; } else ptr->retcode = -1; return(sizeof(*ptr)); } int32_t NSPV_gettxproof(struct NSPV_txproof *ptr,int32_t vout,uint256 txid,int32_t height) { int32_t flag = 0,len = 0; CTransaction _tx; uint256 hashBlock; CBlock block; CBlockIndex *pindex; ptr->height = -1; if ( (ptr->tx= NSPV_getrawtx(_tx,hashBlock,&ptr->txlen,txid)) != 0 ) { ptr->txid = txid; ptr->vout = vout; ptr->hashblock = hashBlock; if ( height == 0 ) ptr->height = hush_blockheight(hashBlock); else { ptr->height = height; if ( (pindex= hush_chainactive(height)) != 0 && hush_blockload(block,pindex) == 0 ) { BOOST_FOREACH(const CTransaction&tx, block.vtx) { if ( tx.GetHash() == txid ) { flag = 1; break; } } if ( flag != 0 ) { set setTxids; CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); setTxids.insert(txid); CMerkleBlock mb(block, setTxids); ssMB << mb; std::vector proof(ssMB.begin(), ssMB.end()); ptr->txprooflen = (int32_t)proof.size(); //fprintf(stderr,"%s txproof.(%s)\n",txid.GetHex().c_str(),HexStr(proof).c_str()); if ( ptr->txprooflen > 0 ) { ptr->txproof = (uint8_t *)calloc(1,ptr->txprooflen); memcpy(ptr->txproof,&proof[0],ptr->txprooflen); } //fprintf(stderr,"gettxproof slen.%d\n",(int32_t)(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen)); } } } // ptr->unspentvalue = CCgettxout(txid,vout,1,1); } return(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen); } int32_t NSPV_getntzsproofresp(struct NSPV_ntzsproofresp *ptr,uint256 prevntztxid,uint256 nextntztxid) { int32_t i; uint256 hashBlock,bhash0,bhash1,desttxid0,desttxid1; CTransaction tx; ptr->prevtxid = prevntztxid; ptr->prevntz = NSPV_getrawtx(tx,hashBlock,&ptr->prevtxlen,ptr->prevtxid); ptr->prevtxidht = hush_blockheight(hashBlock); if ( NSPV_notarizationextract(0,&ptr->common.prevht,&bhash0,&desttxid0,tx) < 0 ) return(-2); else if ( hush_blockheight(bhash0) != ptr->common.prevht ) return(-3); ptr->nexttxid = nextntztxid; ptr->nextntz = NSPV_getrawtx(tx,hashBlock,&ptr->nexttxlen,ptr->nexttxid); ptr->nexttxidht = hush_blockheight(hashBlock); if ( NSPV_notarizationextract(0,&ptr->common.nextht,&bhash1,&desttxid1,tx) < 0 ) return(-5); else if ( hush_blockheight(bhash1) != ptr->common.nextht ) return(-6); else if ( ptr->common.prevht > ptr->common.nextht || (ptr->common.nextht - ptr->common.prevht) > 1440 ) { fprintf(stderr,"illegal prevht.%d nextht.%d\n",ptr->common.prevht,ptr->common.nextht); return(-7); } //fprintf(stderr,"%s -> prevht.%d, %s -> nexht.%d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht); ptr->common.numhdrs = (ptr->common.nextht - ptr->common.prevht + 1); ptr->common.hdrs = (struct NSPV_equihdr *)calloc(ptr->common.numhdrs,sizeof(*ptr->common.hdrs)); //fprintf(stderr,"prev.%d next.%d allocate numhdrs.%d\n",prevht,nextht,ptr->common.numhdrs); for (i=0; icommon.numhdrs; i++) { //hashBlock = NSPV_hdrhash(&ptr->common.hdrs[i]); //fprintf(stderr,"hdr[%d] %s\n",prevht+i,hashBlock.GetHex().c_str()); if ( NSPV_setequihdr(&ptr->common.hdrs[i],ptr->common.prevht+i) < 0 ) { fprintf(stderr,"error setting hdr.%d\n",ptr->common.prevht+i); free(ptr->common.hdrs); ptr->common.hdrs = 0; return(-1); } } //fprintf(stderr,"sizeof ptr %ld, common.%ld lens.%d %d\n",sizeof(*ptr),sizeof(ptr->common),ptr->prevtxlen,ptr->nexttxlen); return(sizeof(*ptr) + sizeof(*ptr->common.hdrs)*ptr->common.numhdrs - sizeof(ptr->common.hdrs) - sizeof(ptr->prevntz) - sizeof(ptr->nextntz) + ptr->prevtxlen + ptr->nexttxlen); } int32_t NSPV_getspentinfo(struct NSPV_spentinfo *ptr,uint256 txid,int32_t vout) { int32_t len = 0; ptr->txid = txid; ptr->vout = vout; ptr->spentvini = -1; len = (int32_t)(sizeof(*ptr) - sizeof(ptr->spent.tx) - sizeof(ptr->spent.txproof)); return(len); } void hush_nSPVreq(CNode *pfrom,std::vector request) // received a request { int32_t len,slen,ind,reqheight,n; std::vector response; uint32_t timestamp = (uint32_t)time(NULL); if ( (len= request.size()) > 0 ) { if ( (ind= request[0]>>1) >= sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes) ) ind = (int32_t)(sizeof(pfrom->prevtimes)/sizeof(*pfrom->prevtimes)) - 1; if ( pfrom->prevtimes[ind] > timestamp ) pfrom->prevtimes[ind] = 0; if ( request[0] == NSPV_INFO ) // info { //fprintf(stderr,"check info %u vs %u, ind.%d\n",timestamp,pfrom->prevtimes[ind],ind); if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_inforesp I; if ( len == 1+sizeof(reqheight) ) dragon_rwnum(0,&request[1],sizeof(reqheight),&reqheight); else reqheight = 0; //fprintf(stderr,"request height.%d\n",reqheight); memset(&I,0,sizeof(I)); if ( (slen= NSPV_getinfo(&I,reqheight)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_INFORESP; //fprintf(stderr,"slen.%d version.%d\n",slen,I.version); if ( NSPV_rwinforesp(1,&response[1],&I) == slen ) { //fprintf(stderr,"send info resp to id %d\n",(int32_t)pfrom->id); pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_inforesp_purge(&I); } } } else if ( request[0] == NSPV_UTXOS ) { //fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len); if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_utxosresp U; if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) ) { int32_t skipcount = 0; char coinaddr[64]; uint8_t filter; uint8_t isCC = 0; memcpy(coinaddr,&request[2],request[1]); coinaddr[request[1]] = 0; if ( request[1] == len-3 ) isCC = (request[len-1] != 0); else if ( request[1] == len-7 ) { isCC = (request[len-5] != 0); dragon_rwnum(0,&request[len-4],sizeof(skipcount),&skipcount); } else { isCC = (request[len-9] != 0); dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount); dragon_rwnum(0,&request[len-4],sizeof(filter),&filter); } if ( 0 && isCC != 0 ) fprintf(stderr,"utxos %s isCC.%d skipcount.%d filter.%x\n",coinaddr,isCC,skipcount,filter); memset(&U,0,sizeof(U)); if ( (slen= NSPV_getaddressutxos(&U,coinaddr,isCC,skipcount,filter)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_UTXOSRESP; if ( NSPV_rwutxosresp(1,&response[1],&U) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_utxosresp_purge(&U); } } } } else if ( request[0] == NSPV_TXIDS ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_txidsresp T; if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) ) { int32_t skipcount = 0; char coinaddr[64]; uint32_t filter; uint8_t isCC = 0; memcpy(coinaddr,&request[2],request[1]); coinaddr[request[1]] = 0; if ( request[1] == len-3 ) isCC = (request[len-1] != 0); else if ( request[1] == len-7 ) { isCC = (request[len-5] != 0); dragon_rwnum(0,&request[len-4],sizeof(skipcount),&skipcount); } else { isCC = (request[len-9] != 0); dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount); dragon_rwnum(0,&request[len-4],sizeof(filter),&filter); } if ( 0 && isCC != 0 ) fprintf(stderr,"txids %s isCC.%d skipcount.%d filter.%d\n",coinaddr,isCC,skipcount,filter); memset(&T,0,sizeof(T)); if ( (slen= NSPV_getaddresstxids(&T,coinaddr,isCC,skipcount,filter)) > 0 ) { //fprintf(stderr,"slen.%d\n",slen); response.resize(1 + slen); response[0] = NSPV_TXIDSRESP; if ( NSPV_rwtxidsresp(1,&response[1],&T) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_txidsresp_purge(&T); } } else fprintf(stderr,"len.%d req1.%d\n",len,request[1]); } } else if ( request[0] == NSPV_MEMPOOL ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_mempoolresp M; char coinaddr[64]; if ( len < sizeof(M)+64 ) { int32_t vout; uint256 txid; uint8_t funcid,isCC = 0; n = 1; n += dragon_rwnum(0,&request[n],sizeof(isCC),&isCC); n += dragon_rwnum(0,&request[n],sizeof(funcid),&funcid); n += dragon_rwnum(0,&request[n],sizeof(vout),&vout); n += dragon_rwbignum(0,&request[n],sizeof(txid),(uint8_t *)&txid); slen = request[n++]; if ( slen < 63 ) { memcpy(coinaddr,&request[n],slen), n += slen; coinaddr[slen] = 0; if ( isCC != 0 ) fprintf(stderr,"(%s) isCC.%d funcid.%d %s/v%d len.%d slen.%d\n",coinaddr,isCC,funcid,txid.GetHex().c_str(),vout,len,slen); memset(&M,0,sizeof(M)); if ( (slen= NSPV_mempooltxids(&M,coinaddr,isCC,funcid,txid,vout)) > 0 ) { //fprintf(stderr,"NSPV_mempooltxids slen.%d\n",slen); response.resize(1 + slen); response[0] = NSPV_MEMPOOLRESP; if ( NSPV_rwmempoolresp(1,&response[1],&M) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_mempoolresp_purge(&M); } } } else fprintf(stderr,"len.%d req1.%d\n",len,request[1]); } } else if ( request[0] == NSPV_NTZS ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_ntzsresp N; int32_t height; if ( len == 1+sizeof(height) ) { dragon_rwnum(0,&request[1],sizeof(height),&height); memset(&N,0,sizeof(N)); if ( (slen= NSPV_getntzsresp(&N,height)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_NTZSRESP; if ( NSPV_rwntzsresp(1,&response[1],&N) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_ntzsresp_purge(&N); } } } } else if ( request[0] == NSPV_NTZSPROOF ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_ntzsproofresp P; uint256 prevntz,nextntz; if ( len == 1+sizeof(prevntz)+sizeof(nextntz) ) { dragon_rwbignum(0,&request[1],sizeof(prevntz),(uint8_t *)&prevntz); dragon_rwbignum(0,&request[1+sizeof(prevntz)],sizeof(nextntz),(uint8_t *)&nextntz); memset(&P,0,sizeof(P)); if ( (slen= NSPV_getntzsproofresp(&P,prevntz,nextntz)) > 0 ) { // fprintf(stderr,"slen.%d msg prev.%s next.%s\n",slen,prevntz.GetHex().c_str(),nextntz.GetHex().c_str()); response.resize(1 + slen); response[0] = NSPV_NTZSPROOFRESP; if ( NSPV_rwntzsproofresp(1,&response[1],&P) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_ntzsproofresp_purge(&P); } else fprintf(stderr,"err.%d\n",slen); } } } else if ( request[0] == NSPV_TXPROOF ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_txproof P; uint256 txid; int32_t height,vout; if ( len == 1+sizeof(txid)+sizeof(height)+sizeof(vout) ) { dragon_rwnum(0,&request[1],sizeof(height),&height); dragon_rwnum(0,&request[1+sizeof(height)],sizeof(vout),&vout); dragon_rwbignum(0,&request[1+sizeof(height)+sizeof(vout)],sizeof(txid),(uint8_t *)&txid); //fprintf(stderr,"got txid %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height); memset(&P,0,sizeof(P)); if ( (slen= NSPV_gettxproof(&P,vout,txid,height)) > 0 ) { //fprintf(stderr,"slen.%d\n",slen); response.resize(1 + slen); response[0] = NSPV_TXPROOFRESP; if ( NSPV_rwtxproof(1,&response[1],&P) == slen ) { //fprintf(stderr,"send response\n"); pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_txproof_purge(&P); } else fprintf(stderr,"gettxproof error.%d\n",slen); } else fprintf(stderr,"txproof reqlen.%d\n",len); } } else if ( request[0] == NSPV_SPENTINFO ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_spentinfo S; int32_t vout; uint256 txid; if ( len == 1+sizeof(txid)+sizeof(vout) ) { dragon_rwnum(0,&request[1],sizeof(vout),&vout); dragon_rwbignum(0,&request[1+sizeof(vout)],sizeof(txid),(uint8_t *)&txid); memset(&S,0,sizeof(S)); if ( (slen= NSPV_getspentinfo(&S,txid,vout)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_SPENTINFORESP; if ( NSPV_rwspentinfo(1,&response[1],&S) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_spentinfo_purge(&S); } } } } else if ( request[0] == NSPV_BROADCAST ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_broadcastresp B; uint32_t n,offset; uint256 txid; if ( len > 1+sizeof(txid)+sizeof(n) ) { dragon_rwbignum(0,&request[1],sizeof(txid),(uint8_t *)&txid); dragon_rwnum(0,&request[1+sizeof(txid)],sizeof(n),&n); memset(&B,0,sizeof(B)); offset = 1 + sizeof(txid) + sizeof(n); if ( n < MAX_TX_SIZE_AFTER_SAPLING && request.size() == offset+n && (slen= NSPV_sendrawtransaction(&B,&request[offset],n)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_BROADCASTRESP; if ( NSPV_rwbroadcastresp(1,&response[1],&B) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_broadcast_purge(&B); } } } } else if ( request[0] == NSPV_REMOTERPC ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_remoterpcresp R; int32_t p; p = 1; p+=dragon_rwnum(0,&request[p],sizeof(slen),&slen); memset(&R,0,sizeof(R)); if (request.size() == p+slen && (slen=NSPV_remoterpc(&R,(char *)&request[p],slen))>0 ) { response.resize(1 + slen); response[0] = NSPV_REMOTERPCRESP; NSPV_rwremoterpcresp(1,&response[1],&R,slen); pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; NSPV_remoterpc_purge(&R); } } } } } #endif // HUSH_NSPVFULLNODE_H