/****************************************************************************** * 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. * * * ******************************************************************************/ // todo: // make sure no files are updated (this is to allow nSPV=1 and later nSPV=0 without affecting database) // validate proofs // make sure to sanity check all vector lengths on receipt // determine if it makes sense to be scanning mempool for the utxo/spentinfo requests #ifndef KOMODO_NSPV_H #define KOMODO_NSPV_H #define NSPV_INFO 0x00 #define NSPV_INFORESP 0x01 #define NSPV_UTXOS 0x02 #define NSPV_UTXOSRESP 0x03 #define NSPV_NTZS 0x04 #define NSPV_NTZSRESP 0x05 #define NSPV_NTZSPROOF 0x06 #define NSPV_NTZSPROOFRESP 0x07 #define NSPV_TXPROOF 0x08 #define NSPV_TXPROOFRESP 0x09 #define NSPV_SPENTINFO 0x0a #define NSPV_SPENTINFORESP 0x0b int32_t iguana_rwbuf(int32_t rwflag,uint8_t *serialized,uint16_t len,uint8_t *buf) { if ( rwflag != 0 ) memcpy(serialized,buf,len); else memcpy(buf,serialized,len); return(len); } struct NSPV_equihdr { int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; uint256 hashFinalSaplingRoot; uint32_t nTime; uint32_t nBits; uint256 nNonce; uint8_t nSolution[1344]; }; int32_t NSPV_rwequihdr(int32_t rwflag,uint8_t *serialized,struct NSPV_equihdr *ptr) { int32_t len = 0; len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nVersion),&ptr->nVersion); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashPrevBlock),(uint8_t *)&ptr->hashPrevBlock); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashMerkleRoot),(uint8_t *)&ptr->hashMerkleRoot); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->hashFinalSaplingRoot),(uint8_t *)&ptr->hashFinalSaplingRoot); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nTime),&ptr->nTime); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nBits),&ptr->nBits); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nNonce),(uint8_t *)&ptr->nNonce); len += iguana_rwbuf(rwflag,&serialized[len],sizeof(ptr->nSolution),ptr->nSolution); return(len); } int32_t iguana_rwequihdrvec(int32_t rwflag,uint8_t *serialized,uint16_t *vecsizep,struct NSPV_equihdr **ptrp) { int32_t i,vsize,len = 0; len += iguana_rwnum(rwflag,&serialized[len],sizeof(*vecsizep),vecsizep); if ( (vsize= *vecsizep) != 0 ) { //fprintf(stderr,"vsize.%d ptrp.%p alloc %ld\n",vsize,*ptrp,sizeof(struct NSPV_equihdr)*vsize); if ( *ptrp == 0 ) *ptrp = (struct NSPV_equihdr *)calloc(sizeof(struct NSPV_equihdr),vsize); // relies on uint16_t being "small" to prevent mem exhaustion for (i=0; itxid),(uint8_t *)&ptr->txid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->satoshis),&ptr->satoshis); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->extradata),&ptr->extradata); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); return(len); } struct NSPV_utxosresp { struct NSPV_utxoresp *utxos; int64_t total,interest; int32_t nodeheight; uint16_t numutxos,pad16; }; int32_t NSPV_rwutxosresp(int32_t rwflag,uint8_t *serialized,struct NSPV_utxosresp *ptr) // check mempool { int32_t i,len = 0; len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->numutxos),&ptr->numutxos); if ( ptr->numutxos != 0 ) { if ( ptr->utxos == 0 ) ptr->utxos = (struct NSPV_utxoresp *)calloc(sizeof(*ptr->utxos),ptr->numutxos); // relies on uint16_t being "small" to prevent mem exhaustion for (i=0; inumutxos; i++) len += NSPV_rwutxoresp(rwflag,&serialized[len],&ptr->utxos[i]); fprintf(stderr,"parsed numutxos.%d\n",ptr->numutxos); } len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->total),&ptr->total); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->interest),&ptr->interest); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nodeheight),&ptr->nodeheight); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad16),&ptr->pad16); return(len); } void NSPV_utxosresp_purge(struct NSPV_utxosresp *ptr) { if ( ptr != 0 ) { if ( ptr->utxos != 0 ) free(ptr->utxos); memset(ptr,0,sizeof(*ptr)); } } struct NSPV_ntz { uint256 blockhash,txid,othertxid; int32_t height,txidheight; }; int32_t NSPV_rwntz(int32_t rwflag,uint8_t *serialized,struct NSPV_ntz *ptr) { int32_t len = 0; len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->othertxid),(uint8_t *)&ptr->othertxid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->txidheight),&ptr->txidheight); return(len); } struct NSPV_ntzsresp { struct NSPV_ntz prevntz,nextntz; }; int32_t NSPV_rwntzsresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsresp *ptr) { int32_t len = 0; len += NSPV_rwntz(rwflag,&serialized[len],&ptr->prevntz); len += NSPV_rwntz(rwflag,&serialized[len],&ptr->nextntz); return(len); } void NSPV_ntzsresp_purge(struct NSPV_ntzsresp *ptr) { if ( ptr != 0 ) memset(ptr,0,sizeof(*ptr)); } struct NSPV_inforesp { struct NSPV_ntz notarization; uint256 blockhash; int32_t height,pad32; }; int32_t NSPV_rwinforesp(int32_t rwflag,uint8_t *serialized,struct NSPV_inforesp *ptr) { int32_t len = 0; len += NSPV_rwntz(rwflag,&serialized[len],&ptr->notarization); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->blockhash),(uint8_t *)&ptr->blockhash); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); return(len); } void NSPV_inforesp_purge(struct NSPV_inforesp *ptr) { if ( ptr != 0 ) memset(ptr,0,sizeof(*ptr)); } struct NSPV_txproof { uint256 txid; int32_t height; uint16_t txlen,txprooflen; uint8_t *tx,*txproof; }; int32_t NSPV_rwtxproof(int32_t rwflag,uint8_t *serialized,struct NSPV_txproof *ptr) { int32_t len = 0; len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->height),&ptr->height); len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->txlen,&ptr->tx); len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->txprooflen,&ptr->txproof); return(len); } void NSPV_txproof_purge(struct NSPV_txproof *ptr) { if ( ptr != 0 ) { if ( ptr->tx != 0 ) free(ptr->tx); if ( ptr->txproof != 0 ) free(ptr->txproof); memset(ptr,0,sizeof(*ptr)); } } struct NSPV_utxo { struct NSPV_txproof T; int64_t satoshis,extradata; int32_t vout,prevht,nextht,pad32; }; int32_t NSPV_rwutxo(int32_t rwflag,uint8_t *serialized,struct NSPV_utxo *ptr) { int32_t len = 0; len += NSPV_rwtxproof(rwflag,&serialized[len],&ptr->T); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->satoshis),&ptr->satoshis); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->extradata),&ptr->extradata); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevht),&ptr->prevht); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nextht),&ptr->nextht); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); return(len); } struct NSPV_ntzproofshared { struct NSPV_equihdr *hdrs; int32_t prevht,nextht,pad32; uint16_t numhdrs,pad16; }; int32_t NSPV_rwntzproofshared(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzproofshared *ptr) { int32_t len = 0; len += iguana_rwequihdrvec(rwflag,&serialized[len],&ptr->numhdrs,&ptr->hdrs); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevht),&ptr->prevht); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nextht),&ptr->nextht); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad16),&ptr->pad16); return(len); } struct NSPV_ntzsproofresp { struct NSPV_ntzproofshared common; uint256 prevtxid,nexttxid; int32_t pad32,prevtxidht,nexttxidht; uint16_t prevlen,nextlen; uint8_t *prevntz,*nextntz; }; int32_t NSPV_rwntzsproofresp(int32_t rwflag,uint8_t *serialized,struct NSPV_ntzsproofresp *ptr) { int32_t len = 0; len += NSPV_rwntzproofshared(rwflag,&serialized[len],&ptr->common); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->prevtxid),(uint8_t *)&ptr->prevtxid); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->nexttxid),(uint8_t *)&ptr->nexttxid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->pad32),&ptr->pad32); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->prevtxidht),&ptr->prevtxidht); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->nexttxidht),&ptr->nexttxidht); len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->prevlen,&ptr->prevntz); len += iguana_rwuint8vec(rwflag,&serialized[len],&ptr->nextlen,&ptr->nextntz); return(len); } void NSPV_ntzsproofresp_purge(struct NSPV_ntzsproofresp *ptr) { if ( ptr != 0 ) { if ( ptr->common.hdrs != 0 ) free(ptr->common.hdrs); if ( ptr->prevntz != 0 ) free(ptr->prevntz); if ( ptr->nextntz != 0 ) free(ptr->nextntz); memset(ptr,0,sizeof(*ptr)); } } struct NSPV_MMRproof { struct NSPV_ntzproofshared common; // tbd }; struct NSPV_spentinfo { struct NSPV_txproof spent; uint256 txid; int32_t vout,spentvini; }; int32_t NSPV_rwspentinfo(int32_t rwflag,uint8_t *serialized,struct NSPV_spentinfo *ptr) // check mempool { int32_t len = 0; len += NSPV_rwtxproof(rwflag,&serialized[len],&ptr->spent); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(ptr->txid),(uint8_t *)&ptr->txid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->vout),&ptr->vout); len += iguana_rwnum(rwflag,&serialized[len],sizeof(ptr->spentvini),&ptr->spentvini); return(len); } void NSPV_spentinfo_purge(struct NSPV_spentinfo *ptr) { if ( ptr != 0 ) { NSPV_txproof_purge(&ptr->spent); memset(ptr,0,sizeof(*ptr)); } } // on fullnode: // 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 uint256 NSPV_getnotarization_txid(int32_t *ntzheightp,int32_t height) { uint256 ntztxid; if ( ntzheightp == 0 ) // search consecutive blocks { *ntzheightp = height + 1; // iterate } else { // check *ntzheightp } // find notarization for height, return its txid and set *ntzheightp fprintf(stderr,"implement NSPV_getnotarization_txid\n"); return(ntztxid); } int32_t NSPV_getinfo(struct NSPV_inforesp *ptr) { int32_t prevMoMheight,len = 0; CBlockIndex *pindex; if ( (pindex= chainActive.LastTip()) != 0 ) { ptr->height = pindex->GetHeight(); ptr->blockhash = pindex->GetBlockHash(); ptr->notarization.height = komodo_notarized_height(&prevMoMheight,&ptr->notarization.blockhash,&ptr->notarization.othertxid); ptr->notarization.txidheight = 0; ptr->notarization.txid = NSPV_getnotarization_txid(&ptr->notarization.txidheight,ptr->notarization.height); 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); 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); } int32_t NSPV_npextract(struct NSPV_ntz *ptr,struct notarized_checkpoint *np) { int32_t ntzheight; ptr->blockhash = np->notarized_hash; ptr->height = np->notarized_height; ptr->txidheight = np->nHeight; ptr->othertxid = np->notarized_desttxid; ntzheight = ptr->txidheight; ptr->txid = NSPV_getnotarization_txid(&ntzheight,ptr->height); if ( ntzheight != ptr->txidheight ) { fprintf(stderr,"NSPV_npextract ntzheight.%d != ptr->txidheight.%d\n",ntzheight,ptr->txidheight); return(-1); } else return(0); } int32_t NSPV_getntzsresp(struct NSPV_ntzsresp *ptr,int32_t height) { struct notarized_checkpoint *nps[2]; if ( komodo_notarized_bracket(nps,height) == 0 ) { if ( nps[0] != 0 ) { if ( NSPV_npextract(&ptr->prevntz,nps[0]) < 0 ) return(-1); } if ( nps[1] != 0 ) { if ( NSPV_npextract(&ptr->nextntz,nps[1]) < 0 ) return(-1); } } return(sizeof(*ptr)); } 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; rawtx = (uint8_t *)calloc(1,*txlenp); decode_hex(rawtx,*txlenp,(char *)strHex.c_str()); } return(rawtx); } int32_t NSPV_gettxproof(struct NSPV_txproof *ptr,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->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(); ptr->txproof = (uint8_t *)calloc(1,ptr->txprooflen); memcpy(ptr->txproof,&proof[0],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,int32_t prevht,int32_t nextht) { int32_t i; uint256 hashBlock; if ( prevht > nextht || (nextht-prevht) > 1440 ) { fprintf(stderr,"illegal prevht.%d nextht.%d\n",prevht,nextht); return(-1); } ptr->common.prevht = prevht; ptr->common.nextht = nextht; ptr->common.numhdrs = (nextht - 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++) { if ( NSPV_setequihdr(&ptr->common.hdrs[i],prevht+i) < 0 ) { fprintf(stderr,"error setting hdr.%d\n",prevht+i); free(ptr->common.hdrs); ptr->common.hdrs = 0; return(-1); } } ptr->prevtxid = NSPV_getnotarization_txid(&ptr->prevtxidht,prevht); ptr->prevntz = NSPV_getrawtx(hashBlock,&ptr->prevlen,ptr->prevtxid); ptr->nexttxid = NSPV_getnotarization_txid(&ptr->nexttxidht,nextht); ptr->nextntz = NSPV_getrawtx(hashBlock,&ptr->nextlen,ptr->nexttxid); //fprintf(stderr,"prevlen.%d nextlen.%d size %ld -> %ld\n",ptr->prevlen,ptr->nextlen,sizeof(*ptr),sizeof(*ptr) - sizeof(ptr->common.hdrs) - sizeof(ptr->prevntz) - sizeof(ptr->nextntz) + ptr->prevlen + ptr->nextlen); return(sizeof(*ptr) + sizeof(*ptr->common.hdrs)*ptr->common.numhdrs - sizeof(ptr->common.hdrs) - sizeof(ptr->prevntz) - sizeof(ptr->nextntz) + ptr->prevlen + ptr->nextlen); } 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,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 ( 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] + ASSETCHAINS_BLOCKTIME/2 ) { struct NSPV_inforesp I; memset(&I,0,sizeof(I)); if ( (slen= NSPV_getinfo(&I)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_INFORESP; NSPV_rwinforesp(1,&response[1],&I); pfrom->PushMessage("nSPV",response); pfrom->prevtimes[ind] = timestamp; NSPV_inforesp_purge(&I); } } } else if ( request[0] == NSPV_UTXOS ) { 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 ) { //printf("getaddressutxos.(%s) slen.%d\n",coinaddr,slen); 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; int32_t prevht,nextht; if ( len == 1+sizeof(prevht)+sizeof(nextht) ) { iguana_rwnum(0,&request[1],sizeof(prevht),&prevht); iguana_rwnum(0,&request[1+sizeof(prevht)],sizeof(nextht),&nextht); if ( prevht != 0 && nextht != 0 && nextht >= prevht ) { memset(&P,0,sizeof(P)); if ( (slen= NSPV_getntzsproofresp(&P,prevht,nextht)) > 0 ) { response.resize(1 + slen); response[0] = NSPV_NTZSPROOFRESP; P.common.numhdrs = (nextht - prevht + 1); 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; if ( len == 1+sizeof(txid)+sizeof(height) ) { iguana_rwnum(0,&request[1],sizeof(height),&height); iguana_rwbignum(0,&request[1+sizeof(height)],sizeof(txid),(uint8_t *)&txid); memset(&P,0,sizeof(P)); if ( (slen= NSPV_gettxproof(&P,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); } } } } } } // nSPV client CAmount AmountFromValue(const UniValue& value); int32_t bitcoin_base58decode(uint8_t *data,char *coinaddr); uint32_t NSPV_lastinfo,NSPV_logintime; char NSPV_wifstr[64]; std::string NSPV_address; struct NSPV_inforesp NSPV_inforesult; struct NSPV_utxosresp NSPV_utxosresult; struct NSPV_spentinfo NSPV_spentresult; struct NSPV_ntzsresp NSPV_ntzsresult; struct NSPV_ntzsproofresp NSPV_ntzsproofresult; struct NSPV_txproof NSPV_txproofresult; struct NSPV_utxo *NSPV_utxos; CNode *NSPV_req(CNode *pnode,uint8_t *msg,int32_t len,uint64_t mask,int32_t ind) { int32_t flag = 0; uint32_t timestamp = (uint32_t)time(NULL); if ( pnode == 0 ) { LOCK(cs_vNodes); BOOST_FOREACH(CNode *ptr,vNodes) { if ( ptr->hSocket == INVALID_SOCKET ) continue; if ( (ptr->nServices & mask) == mask && timestamp > ptr->prevtimes[ind] ) { flag = 1; pnode = ptr; break; } // else fprintf(stderr,"nServices %llx vs mask %llx, t%u vs %u, ind.%d\n",(long long)ptr->nServices,(long long)mask,timestamp,ptr->prevtimes[ind],ind); } } else flag = 1; if ( pnode != 0 ) { std::vector request; request.resize(len); memcpy(&request[0],msg,len); //fprintf(stderr,"pushmessage [%d] len.%d\n",msg[0],len); pnode->PushMessage("getnSPV",request); pnode->prevtimes[ind] = timestamp; return(pnode); } return(0); } UniValue NSPV_txproof_json(struct NSPV_txproof *ptr) { UniValue result(UniValue::VOBJ); result.push_back(Pair("txid",ptr->txid.GetHex())); result.push_back(Pair("height",(int64_t)ptr->height)); result.push_back(Pair("txlen",(int64_t)ptr->txlen)); result.push_back(Pair("txprooflen",(int64_t)ptr->txprooflen)); return(result); } UniValue NSPV_spentinfo_json(struct NSPV_spentinfo *ptr) { UniValue result(UniValue::VOBJ); result.push_back(Pair("result","success")); result.push_back(Pair("txid",ptr->txid.GetHex())); result.push_back(Pair("vout",(int64_t)ptr->vout)); result.push_back(Pair("spentheight",(int64_t)ptr->spent.height)); result.push_back(Pair("spenttxid",ptr->spent.txid.GetHex())); result.push_back(Pair("spentvini",(int64_t)ptr->spentvini)); result.push_back(Pair("spenttxlen",(int64_t)ptr->spent.txlen)); result.push_back(Pair("spenttxprooflen",(int64_t)ptr->spent.txprooflen)); return(result); } UniValue NSPV_ntz_json(struct NSPV_ntz *ptr) { UniValue result(UniValue::VOBJ); result.push_back(Pair("notarized_height",(int64_t)ptr->height)); result.push_back(Pair("notarized_blockhash",ptr->blockhash.GetHex())); result.push_back(Pair("notarization_txid",ptr->txid.GetHex())); result.push_back(Pair("notarization_txidheight",(int64_t)ptr->txidheight)); result.push_back(Pair("notarization_desttxid",ptr->othertxid.GetHex())); return(result); } UniValue NSPV_getinfo_json() { UniValue result(UniValue::VOBJ); result.push_back(Pair("result","success")); result.push_back(Pair("height",(int64_t)NSPV_inforesult.height)); result.push_back(Pair("chaintip",NSPV_inforesult.blockhash.GetHex())); result.push_back(Pair("notarization",NSPV_ntz_json(&NSPV_inforesult.notarization))); return(result); } UniValue NSPV_utxoresp_json(struct NSPV_utxoresp *utxos,int32_t numutxos) { UniValue array(UniValue::VARR); int32_t i; for (i=0; iutxos,ptr->numutxos))); result.push_back(Pair("height",(int64_t)ptr->nodeheight)); result.push_back(Pair("numutxos",(int64_t)ptr->numutxos)); result.push_back(Pair("balance",(double)ptr->total/COIN)); if ( ASSETCHAINS_SYMBOL[0] == 0 ) result.push_back(Pair("interest",(double)ptr->interest/COIN)); return(result); } UniValue NSPV_ntzs_json(struct NSPV_ntzsresp *ptr) { UniValue result(UniValue::VOBJ); result.push_back(Pair("result","success")); result.push_back(Pair("prev",NSPV_ntz_json(&ptr->prevntz))); result.push_back(Pair("next",NSPV_ntz_json(&ptr->nextntz))); return(result); } UniValue NSPV_headers_json(struct NSPV_equihdr *hdrs,int32_t numhdrs) { UniValue array(UniValue::VARR); int32_t i; for (i=0; icommon.prevht)); result.push_back(Pair("nextht",(int64_t)ptr->common.nextht)); result.push_back(Pair("prevtxid",ptr->prevtxid.GetHex())); result.push_back(Pair("prevtxidht",(int64_t)ptr->prevtxidht)); result.push_back(Pair("prevlen",(int64_t)ptr->prevlen)); result.push_back(Pair("nexttxid",ptr->nexttxid.GetHex())); result.push_back(Pair("nexttxidht",(int64_t)ptr->nexttxidht)); result.push_back(Pair("nextlen",(int64_t)ptr->prevlen)); result.push_back(Pair("numhdrs",(int64_t)ptr->common.numhdrs)); result.push_back(Pair("headers",NSPV_headers_json(ptr->common.hdrs,ptr->common.numhdrs))); return(result); } UniValue NSPV_login(char *wifstr) { UniValue result(UniValue::VOBJ); char coinaddr[64]; uint8_t data[128]; int32_t len,valid = 0; len = bitcoin_base58decode(data,wifstr); if ( strlen(wifstr) < 64 && (len == 38 && data[len-5] == 1) || (len == 37 && data[len-5] != 1) ) valid = 1; if ( valid == 0 || data[0] != 188 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid wif")); result.push_back(Pair("len",(int64_t)len)); result.push_back(Pair("prefix",(int64_t)data[0])); return(result); } memset(NSPV_wifstr,0,sizeof(NSPV_wifstr)); strncpy(NSPV_wifstr,wifstr,sizeof(NSPV_wifstr)-1); NSPV_logintime = (uint32_t)time(NULL); result.push_back(Pair("result","success")); result.push_back(Pair("status","wif will expire in 60 seconds")); CKey key = DecodeSecret(wifstr); CPubKey pubkey = key.GetPubKey(); //assert(key.VerifyPubKey(pubkey)); CKeyID vchAddress = pubkey.GetID(); NSPV_address = EncodeDestination(vchAddress); result.push_back(Pair("address",NSPV_address)); result.push_back(Pair("pubkey",HexStr(pubkey))); result.push_back(Pair("wifprefix",(int64_t)data[0])); result.push_back(Pair("compressed",(int64_t)(data[len-5] == 1))); memset(data,0,sizeof(data)); return(result); } UniValue NSPV_addressutxos(char *coinaddr) { UniValue result(UniValue::VOBJ); uint8_t msg[64]; int32_t i,slen,len = 0; if ( bitcoin_base58decode(msg,coinaddr) != 25 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid address")); return(result); } if ( NSPV_inforesult.height == 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) { if ( NSPV_inforesult.height == 0 ) { msg[0] = NSPV_INFO; NSPV_req(0,msg,1,NODE_NSPV,msg[0]>>1); } slen = (int32_t)strlen(coinaddr); msg[len++] = NSPV_UTXOS; msg[len++] = slen; memcpy(&msg[len],coinaddr,slen), len += slen; msg[len] = 0; if ( NSPV_req(0,msg,len,NODE_ADDRINDEX,msg[0]>>1) != 0 ) { for (i=0; i<10; i++) { usleep(100000); if ( NSPV_inforesult.height != 0 && NSPV_utxosresult.nodeheight >= NSPV_inforesult.height ) return(NSPV_utxosresp_json(&NSPV_utxosresult)); } } } result.push_back(Pair("result","error")); result.push_back(Pair("error","no utxos result")); return(result); } UniValue NSPV_notarizations(int32_t height) { uint8_t msg[64]; int32_t i,len = 0; struct NSPV_ntzsresp N; if ( NSPV_ntzsresult.prevntz.height <= height && NSPV_ntzsresult.nextntz.height >= height ) return(NSPV_ntzs_json(&NSPV_ntzsresult)); msg[len++] = NSPV_NTZS; len += iguana_rwnum(1,&msg[len],sizeof(height),&height); if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) { for (i=0; i<10; i++) { usleep(100000); if ( NSPV_ntzsresult.prevntz.height <= height && NSPV_ntzsresult.nextntz.height >= height ) return(NSPV_ntzs_json(&NSPV_ntzsresult)); } } memset(&N,0,sizeof(N)); return(NSPV_ntzs_json(&N)); } UniValue NSPV_hdrsproof(int32_t prevheight,int32_t nextheight) { uint8_t msg[64]; int32_t i,len = 0; struct NSPV_ntzsproofresp H; if ( NSPV_ntzsproofresult.common.prevht == prevheight && NSPV_ntzsproofresult.common.nextht >= nextheight ) return(NSPV_ntzsproof_json(&NSPV_ntzsproofresult)); msg[len++] = NSPV_NTZSPROOF; len += iguana_rwnum(1,&msg[len],sizeof(prevheight),&prevheight); len += iguana_rwnum(1,&msg[len],sizeof(nextheight),&nextheight); if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) { for (i=0; i<10; i++) { usleep(100000); if ( NSPV_ntzsproofresult.common.prevht == prevheight && NSPV_ntzsproofresult.common.nextht >= nextheight ) return(NSPV_ntzsproof_json(&NSPV_ntzsproofresult)); } } memset(&H,0,sizeof(H)); return(NSPV_ntzsproof_json(&H)); } UniValue NSPV_txproof(uint256 txid,int32_t height) { uint8_t msg[64]; int32_t i,len = 0; struct NSPV_txproof P; if ( NSPV_txproofresult.txid == txid && NSPV_txproofresult.height == height ) return(NSPV_txproof_json(&NSPV_txproofresult)); msg[len++] = NSPV_TXPROOF; len += iguana_rwnum(1,&msg[len],sizeof(height),&height); len += iguana_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid); if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) { for (i=0; i<10; i++) { usleep(100000); if ( NSPV_txproofresult.txid == txid && NSPV_txproofresult.height == height ) return(NSPV_txproof_json(&NSPV_txproofresult)); } } memset(&P,0,sizeof(P)); return(NSPV_txproof_json(&P)); } UniValue NSPV_spentinfo(uint256 txid,int32_t vout) { uint8_t msg[64]; int32_t i,len = 0; struct NSPV_spentinfo I; if ( NSPV_spentresult.txid == txid && NSPV_spentresult.vout == vout ) return(NSPV_spentinfo_json(&NSPV_spentresult)); msg[len++] = NSPV_SPENTINFO; len += iguana_rwnum(1,&msg[len],sizeof(vout),&vout); len += iguana_rwbignum(1,&msg[len],sizeof(txid),(uint8_t *)&txid); if ( NSPV_req(0,msg,len,NODE_SPENTINDEX,msg[0]>>1) != 0 ) { for (i=0; i<10; i++) { usleep(100000); if ( NSPV_spentresult.txid == txid && NSPV_spentresult.vout == vout ) return(NSPV_spentinfo_json(&NSPV_spentresult)); } } memset(&I,0,sizeof(I)); return(NSPV_spentinfo_json(&I)); } void komodo_nSPVresp(CNode *pfrom,std::vector response) // received a response { int32_t len; uint32_t timestamp = (uint32_t)time(NULL); if ( (len= response.size()) > 0 ) { switch ( response[0] ) { case NSPV_INFORESP: NSPV_inforesp_purge(&NSPV_inforesult); NSPV_rwinforesp(0,&response[1],&NSPV_inforesult); fprintf(stderr,"got info response %u size.%d height.%d\n",timestamp,(int32_t)response.size(),NSPV_inforesult.height); // update current height and ntrz status break; case NSPV_UTXOSRESP: NSPV_utxosresp_purge(&NSPV_utxosresult); NSPV_rwutxosresp(0,&response[1],&NSPV_utxosresult); fprintf(stderr,"got utxos response %u size.%d\n",timestamp,(int32_t)response.size()); // update utxos list break; case NSPV_NTZSRESP: NSPV_ntzsresp_purge(&NSPV_ntzsresult); NSPV_rwntzsresp(0,&response[1],&NSPV_ntzsresult); fprintf(stderr,"got ntzs response %u size.%d\n",timestamp,(int32_t)response.size()); // update utxos[i] break; case NSPV_NTZSPROOFRESP: NSPV_ntzsproofresp_purge(&NSPV_ntzsproofresult); NSPV_rwntzsproofresp(0,&response[1],&NSPV_ntzsproofresult); fprintf(stderr,"got ntzproof response %u size.%d prev.%d next.%d\n",timestamp,(int32_t)response.size(),NSPV_ntzsproofresult.common.prevht,NSPV_ntzsproofresult.common.nextht); // update utxos[i] break; case NSPV_TXPROOFRESP: NSPV_txproof_purge(&NSPV_txproofresult); NSPV_rwtxproof(0,&response[1],&NSPV_txproofresult); fprintf(stderr,"got txproof response %u size.%d\n",timestamp,(int32_t)response.size()); // update utxos[i] break; case NSPV_SPENTINFORESP: NSPV_spentinfo_purge(&NSPV_spentresult); NSPV_rwspentinfo(0,&response[1],&NSPV_spentresult); fprintf(stderr,"got spentinfo response %u size.%d\n",timestamp,(int32_t)response.size()); // update utxos[i] break; default: fprintf(stderr,"unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp); break; } } } void komodo_nSPV(CNode *pto) // polling loop from SendMessages { uint8_t msg[256]; int32_t i,len=0; uint32_t timestamp = (uint32_t)time(NULL); if ( NSPV_logintime != 0 && timestamp > NSPV_logintime+60 ) { fprintf(stderr,"scrub wif from NSPV memory\n"); memset(NSPV_wifstr,0,sizeof(NSPV_wifstr)); NSPV_logintime = 0; } if ( (pto->nServices & NODE_NSPV) == 0 ) return; /*if ( timestamp > pto->lastntzs || timestamp > pto->lastproof ) { for (i=0; i pto->lastntzs ) { request[0] = NSPV_NTZS; pto->lastntzs = timestamp; pto->PushMessage("getnSPV",request); return; } else if ( timestamp > pto->lastproof ) { if ( NSPV_utxos[i].T.txlen == 0 ) { request[0] = NSPV_TXPROOF; pto->lastproof = timestamp; pto->PushMessage("getnSPV",request); } else // need space for the headers... { request[0] = NSPV_NTZPROOF; pto->lastproof = timestamp; pto->PushMessage("getnSPV",request); } return; } } } }*/ if ( timestamp > NSPV_lastinfo + ASSETCHAINS_BLOCKTIME/2 && timestamp > pto->prevtimes[NSPV_INFO>>1] + ASSETCHAINS_BLOCKTIME/2 ) { if ( (rand() % 100) < 10 ) // randomize which peer to query { msg[len++] = NSPV_INFO; NSPV_req(pto,msg,len,NODE_NSPV,NSPV_INFO>>1); } } } #endif // KOMODO_NSPV_H