/****************************************************************************** * 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 KOMODO_NSPVFULLNODE_H #define KOMODO_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 "notarisationdb.h" /*uint256 NSPV_getnotarization_txid(int32_t *ntzheightp,int32_t height) { uint256 txid; Notarisation nota; char *symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; memset(&txid,0,sizeof(txid)); *ntzheightp = 0; int32_t matchedHeight = ScanNotarisationsDB2(height,symbol,1440,nota); if ( matchedHeight != 0 ) { *ntzheightp = matchedHeight; txid = nota.first; } return(txid); }*/ int32_t komodo_notarized_bracket(uint256 txids[2],int32_t txidhts[2],uint256 desttxids[2],int32_t ntzheights[2],int32_t height) { int32_t txidht; CTransaction tx; Notarisation nota; char *symbol; std::vector opret; uint256 bhash0,bhash1,hashBlock; symbol = (ASSETCHAINS_SYMBOL[0] == 0) ? (char *)"KMD" : ASSETCHAINS_SYMBOL; memset(txids,0,sizeof(*txids)*2); memset(desttxids,0,sizeof(*desttxids)*2); memset(ntzheights,0,sizeof(*ntzheights)*2); memset(txidhts,0,sizeof(*txidhts)*2); if ( (txidht= ScanNotarisationsDB(height,symbol,1440,nota)) == 0 ) return(-1); txids[0] = nota.first; txidhts[0] = txidht; if ( !GetTransaction(txids[0],tx,hashBlock,false) || tx.vout.size() < 2 ) return(-2); GetOpReturnData(tx.vout[1].scriptPubKey,opret); //if ( opret.size() >= 32*2+4 ) desttxids[0] = NSPV_opretextract(&ntzheights[0],&bhash0,symbol,opret,txids[0]); /*desttxids[0] = NSPV_opretextract(&ntzheights[0],&bhash0,symbol,E_MARSHAL(ss << nota.second)); //if ( height != 2668 ) // fprintf(stderr,"scan.%d -> %s txidht.%d ntzht.%d\n",height,desttxids[0].GetHex().c_str(),txidht,ntzheights[0]); if ( ntzheights[0] == height-1 ) // offset the +1 from caller { txids[1] = txids[0]; txidhts[1] = txidhts[0]; ntzheights[1] = ntzheights[0]; desttxids[1] = desttxids[0]; return(0); }*/ if ( (txidht= ScanNotarisationsDB2(height,symbol,1440,nota)) != 0 ) { txids[1] = nota.first; txidhts[1] = txidht; if ( !GetTransaction(txids[1],tx,hashBlock,false) || tx.vout.size() < 2 ) return(-2); GetOpReturnData(tx.vout[1].scriptPubKey,opret); //if ( opret.size() >= 32*2+4 ) desttxids[1] = NSPV_opretextract(&ntzheights[1],&bhash1,symbol,opret,txids[1]); //desttxids[1] = NSPV_opretextract(&ntzheights[1],&bhash1,symbol,E_MARSHAL(ss << nota.second)); } //fprintf(stderr,"prev.(%s -> ht.%d %s) next.(%s -> ht.%d %s)\n",txids[0].GetHex().c_str(),ntzheights[0],bhash0.GetHex().c_str(),txids[1].GetHex().c_str(),ntzheights[1],bhash1.GetHex().c_str()); return(0); } int32_t NSPV_ntzextract(struct NSPV_ntz *ptr,uint256 ntztxid,int32_t txidht,uint256 desttxid,int32_t ntzheight) { ptr->blockhash = *chainActive[ntzheight]->phashBlock; ptr->height = ntzheight; ptr->txidheight = txidht; ptr->othertxid = desttxid; ptr->txid = ntztxid; return(0); } int32_t NSPV_getntzsresp(struct NSPV_ntzsresp *ptr,int32_t height) { uint256 txids[2],desttxids[2]; int32_t ntzheights[2],txidhts[2]; if ( height < chainActive.LastTip()->GetHeight() ) height++; if ( komodo_notarized_bracket(txids,txidhts,desttxids,ntzheights,height) == 0 ) { if ( ntzheights[0] != 0 ) { if ( NSPV_ntzextract(&ptr->prevntz,txids[0],txidhts[0],desttxids[0],ntzheights[0]) < 0 ) return(-1); } if ( ntzheights[1] != 0 ) { if ( NSPV_ntzextract(&ptr->nextntz,txids[1],txidhts[1],desttxids[1],ntzheights[1]) < 0 ) return(-1); } } return(sizeof(*ptr)); } int32_t NSPV_getinfo(struct NSPV_inforesp *ptr) { int32_t prevMoMheight,len = 0; CBlockIndex *pindex; struct NSPV_ntzsresp pair; if ( (pindex= chainActive.LastTip()) != 0 ) { ptr->height = pindex->GetHeight(); ptr->blockhash = pindex->GetBlockHash(); if ( NSPV_getntzsresp(&pair,ptr->height-1) < 0 ) return(-1); ptr->notarization = pair.prevntz; return(sizeof(*ptr)); } else return(-1); } int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr) // check mempool { int64_t total = 0,interest=0; uint32_t locktime; int32_t tipheight,maxlen,txheight,n = 0,len = 0; std::vector > unspentOutputs; SetCCunspents(unspentOutputs,coinaddr,false); maxlen = MAX_BLOCK_SIZE(tipheight) - 512; maxlen /= sizeof(*ptr->utxos); strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1); if ( (ptr->numutxos= (int32_t)unspentOutputs.size()) > 0 && ptr->numutxos < maxlen ) { tipheight = chainActive.LastTip()->GetHeight(); ptr->nodeheight = tipheight; ptr->utxos = (struct NSPV_utxoresp *)calloc(ptr->numutxos,sizeof(*ptr->utxos)); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { ptr->utxos[n].txid = it->first.txhash; ptr->utxos[n].vout = (int32_t)it->first.index; ptr->utxos[n].satoshis = it->second.satoshis; ptr->utxos[n].height = it->second.blockHeight; if ( ASSETCHAINS_SYMBOL[0] == 0 && it->second.satoshis >= 10*COIN ) { ptr->utxos[n].extradata = komodo_accrued_interest(&txheight,&locktime,ptr->utxos[n].txid,ptr->utxos[n].vout,ptr->utxos[n].height,ptr->utxos[n].satoshis,tipheight); interest += ptr->utxos[n].extradata; } total += it->second.satoshis; n++; } 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); if ( n == ptr->numutxos ) { ptr->total = total; ptr->interest = interest; return(len); } } } if ( ptr->utxos != 0 ) free(ptr->utxos); memset(ptr,0,sizeof(*ptr)); return(0); } uint8_t *NSPV_getrawtx(uint256 &hashBlock,uint16_t *txlenp,uint256 txid) { CTransaction tx; uint8_t *rawtx = 0; *txlenp = 0; { LOCK(cs_main); if (!GetTransaction(txid, tx, hashBlock, false)) 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,"addmempool transaction %s\n",ptr->txid.GetHex().c_str()); if ( myAddtomempool(tx) != 0 ) ptr->retcode = 1; else ptr->retcode = 0; //fprintf(stderr,"relay transaction %s retcode.%d\n",ptr->txid.GetHex().c_str(),ptr->retcode); RelayTransaction(tx); } 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; uint256 hashBlock; CBlock block; CBlockIndex *pindex; if ( (ptr->tx= NSPV_getrawtx(hashBlock,&ptr->txlen,txid)) == 0 ) return(-1); ptr->txid = txid; ptr->vout = vout; ptr->height = height; if ( (pindex= komodo_chainactive(height)) != 0 && komodo_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(); if ( ptr->txprooflen > 0 ) { ptr->txproof = (uint8_t *)calloc(1,ptr->txprooflen); memcpy(ptr->txproof,&proof[0],ptr->txprooflen); } //int64_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag); ptr->unspentvalue = CCgettxout(txid,vout,1,1); //fprintf(stderr,"gettxproof slen.%d\n",(int32_t)(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen)); return(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen); } } return(-1); } int32_t NSPV_setequihdr(struct NSPV_equihdr *hdr,int32_t height) { CBlockIndex *pindex; if ( (pindex= komodo_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->nSolution[0],sizeof(hdr->nSolution)); return(sizeof(*hdr)); } return(-1); } 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(hashBlock,&ptr->prevtxlen,ptr->prevtxid); ptr->prevtxidht = komodo_blockheight(hashBlock); if ( NSPV_txextract(tx,ptr->prevntz,ptr->prevtxlen) < 0 ) return(-1); else if ( NSPV_notarizationextract(&ptr->common.prevht,&bhash0,&desttxid0,tx) < 0 ) return(-2); else if ( komodo_blockheight(bhash0) != ptr->common.prevht ) return(-3); ptr->nexttxid = nextntztxid; ptr->nextntz = NSPV_getrawtx(hashBlock,&ptr->nexttxlen,ptr->nexttxid); ptr->nexttxidht = komodo_blockheight(hashBlock); if ( NSPV_txextract(tx,ptr->prevntz,ptr->prevtxlen) < 0 ) return(-4); else if ( NSPV_notarizationextract(&ptr->common.nextht,&bhash1,&desttxid1,tx) < 0 ) return(-5); else if ( komodo_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); } 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); } } 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)); if ( CCgetspenttxid(ptr->spent.txid,ptr->spentvini,ptr->spent.height,txid,vout) == 0 ) { if ( NSPV_gettxproof(&ptr->spent,0,ptr->spent.txid,ptr->spent.height) > 0 ) len += ptr->spent.txlen + ptr->spent.txprooflen; else { NSPV_txproof_purge(&ptr->spent); return(-1); } } return(len); } void komodo_nSPVreq(CNode *pfrom,std::vector request) // received a request { int32_t len,slen,ind; 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; memset(&I,0,sizeof(I)); if ( (slen= NSPV_getinfo(&I)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_INFORESP; if ( NSPV_rwinforesp(1,&response[1],&I) == slen ) { 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; char coinaddr[64]; if ( len < 64 && request[1] == len-2 ) { memcpy(coinaddr,&request[2],request[1]); coinaddr[request[1]] = 0; memset(&U,0,sizeof(U)); if ( (slen= NSPV_getaddressutxos(&U,coinaddr)) > 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_NTZS ) { if ( timestamp > pfrom->prevtimes[ind] ) { struct NSPV_ntzsresp N; int32_t height; if ( len == 1+sizeof(height) ) { iguana_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) ) { iguana_rwbignum(0,&request[1],sizeof(prevntz),(uint8_t *)&prevntz); iguana_rwbignum(0,&request[1+sizeof(prevntz)],sizeof(nextntz),(uint8_t *)&nextntz); memset(&P,0,sizeof(P)); if ( (slen= NSPV_getntzsproofresp(&P,prevntz,nextntz)) > 0 ) { 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 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) ) { iguana_rwnum(0,&request[1],sizeof(height),&height); iguana_rwnum(0,&request[1+sizeof(height)],sizeof(vout),&vout); iguana_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 ) { response.resize(1 + slen); response[0] = NSPV_TXPROOFRESP; if ( NSPV_rwtxproof(1,&response[1],&P) == slen ) { pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; } NSPV_txproof_purge(&P); } } } } 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) ) { iguana_rwnum(0,&request[1],sizeof(vout),&vout); iguana_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; uint16_t n,offset; uint256 txid; if ( len > 1+sizeof(txid)+sizeof(n) ) { iguana_rwbignum(0,&request[1],sizeof(txid),(uint8_t *)&txid); iguana_rwnum(0,&request[1+sizeof(txid)],sizeof(n),&n); memset(&B,0,sizeof(B)); offset = 1 + sizeof(txid) + sizeof(n); if ( 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); } } } } } } #endif // KOMODO_NSPVFULLNODE_H