From 4d095fce5d93da908a68de7775a0d436f675f46c Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Jul 2019 03:25:02 -1100 Subject: [PATCH] NSPV_gettransaction and split out into 4 files --- src/komodo_nSPV.h | 1067 +---------------------------------- src/komodo_nSPV_fullnode.h | 464 +++++++++++++++ src/komodo_nSPV_superlite.h | 413 ++++++++++++++ src/komodo_nSPV_wallet.h | 277 +++++++++ src/main.cpp | 5 +- 5 files changed, 1160 insertions(+), 1066 deletions(-) create mode 100644 src/komodo_nSPV_fullnode.h create mode 100644 src/komodo_nSPV_superlite.h create mode 100644 src/komodo_nSPV_wallet.h diff --git a/src/komodo_nSPV.h b/src/komodo_nSPV.h index 01c5b8bab..91c6ba7f4 100644 --- a/src/komodo_nSPV.h +++ b/src/komodo_nSPV.h @@ -23,6 +23,8 @@ #ifndef KOMODO_NSPV_H #define KOMODO_NSPV_H +// nSPV defines and struct definitions with serialization and purge functions + #define NSPV_INFO 0x00 #define NSPV_INFORESP 0x01 #define NSPV_UTXOS 0x02 @@ -359,1069 +361,4 @@ void NSPV_spentinfo_purge(struct NSPV_spentinfo *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 - -#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); -} - -uint256 NSPV_extract_desttxid(int32_t *heightp,char *symbol,std::vector opret) -{ - uint256 desttxid; int32_t i; - //for (i=0; i<32; i++) - // fprintf(stderr,"%02x",opret[i]); - //fprintf(stderr," blockhash, "); - //for (i=0; i<4; i++) - // fprintf(stderr,"%02x",opret[32+i]); - //fprintf(stderr," height, "); - iguana_rwnum(0,&opret[32],sizeof(*heightp),heightp); - //for (i=0; i<32; i++) - // fprintf(stderr,"%02x",opret[36+i]); - //fprintf(stderr," desttxid\n"); - for (i=0; i<32; i++) - ((uint8_t *)&desttxid)[i] = opret[4 + 32 + i]; - return(desttxid); -} - -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; Notarisation nota; char *symbol; - 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; - desttxids[0] = NSPV_extract_desttxid(&ntzheights[0],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; - desttxids[1] = NSPV_extract_desttxid(&ntzheights[1],symbol,E_MARSHAL(ss << nota.second)); - } - 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_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(); - 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)); - 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->prevtxlen,ptr->prevtxid); - ptr->nexttxid = NSPV_getnotarization_txid(&ptr->nexttxidht,nextht); - ptr->nextntz = NSPV_getrawtx(hashBlock,&ptr->nexttxlen,ptr->nexttxid); - //fprintf(stderr,"prevtxlen.%d nexttxlen.%d size %ld -> %ld\n",ptr->prevtxlen,ptr->nexttxlen,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->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,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; 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); - //fprintf(stderr,"got txid ht.%d\n",txid.GetHex().c_str(),height); - 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. VERY simplistic "single threaded" networking model. for production GUI best to multithread, etc. -#define NSPV_POLLITERS 15 -#define NSPV_POLLMICROS 100000 -#define NSPV_MAXVINS 64 - -CAmount AmountFromValue(const UniValue& value); -int32_t bitcoin_base58decode(uint8_t *data,char *coinaddr); - -uint32_t NSPV_lastinfo,NSPV_logintime; -char NSPV_wifstr[64],NSPV_pubkeystr[67]; -std::string NSPV_address; -CKey NSPV_key; -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; - -CKey *NSPV_defaultkey() -{ - return(&NSPV_key); -} - -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->prevtimes[ind] > timestamp ) - ptr->prevtimes[ind] = 0; - 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); - } else fprintf(stderr,"no pnodes\n"); - 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(struct NSPV_inforesp *ptr) -{ - UniValue result(UniValue::VOBJ); - result.push_back(Pair("result","success")); - result.push_back(Pair("height",(int64_t)ptr->height)); - result.push_back(Pair("chaintip",ptr->blockhash.GetHex())); - result.push_back(Pair("notarization",NSPV_ntz_json(&ptr->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("address",ptr->coinaddr)); - 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("prevtxlen",(int64_t)ptr->prevtxlen)); - result.push_back(Pair("nexttxid",ptr->nexttxid.GetHex())); - result.push_back(Pair("nexttxidht",(int64_t)ptr->nexttxidht)); - result.push_back(Pair("nexttxlen",(int64_t)ptr->prevtxlen)); - 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")); - NSPV_key = DecodeSecret(wifstr); - CPubKey pubkey = NSPV_key.GetPubKey(); - CKeyID vchAddress = pubkey.GetID(); - NSPV_address = EncodeDestination(vchAddress); - result.push_back(Pair("address",NSPV_address)); - result.push_back(Pair("pubkey",HexStr(pubkey))); - strcpy(NSPV_pubkeystr,HexStr(pubkey).c_str()); - 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_getinfo_json() -{ - uint8_t msg[64]; int32_t i,iters,len = 0; struct NSPV_inforesp I; - NSPV_inforesp_purge(&NSPV_inforesult); - msg[len++] = NSPV_INFO; - for (iters=0; iters<3; iters++) - { - //fprintf(stderr,"issue getinfo\n"); - if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) - { - for (i=0; i>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= 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= 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= 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); - //fprintf(stderr,"req txproof %s at height.%d\n",txid.GetHex().c_str(),height); - if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) - { - for (i=0; i>1) != 0 ) - { - for (i=0; i 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; - } - } -} - -// nSPV wallet - -int32_t NSPV_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct NSPV_utxoresp utxos[],int32_t numunspents,int64_t value) -{ - int32_t i,abovei,belowi; int64_t above,below,gap,atx_value; - abovei = belowi = -1; - for (above=below=i=0; i value ) - { - gap = (atx_value - value); - if ( above == 0 || gap < above ) - { - above = gap; - abovei = i; - } - } - else - { - gap = (value - atx_value); - if ( below == 0 || gap < below ) - { - below = gap; - belowi = i; - } - } - //printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below)); - } - *aboveip = abovei; - *abovep = above; - *belowip = belowi; - *belowp = below; - //printf("above.%d below.%d\n",abovei,belowi); - if ( abovei >= 0 && belowi >= 0 ) - { - if ( above < (below >> 1) ) - return(abovei); - else return(belowi); - } - else if ( abovei >= 0 ) - return(abovei); - else return(belowi); -} - -int64_t NSPV_addinputs(CMutableTransaction &mtx,int64_t total,int32_t maxinputs,struct NSPV_utxoresp *ptr,int32_t num) -{ - int32_t abovei,belowi,ind,vout,i,n = 0; int64_t threshold,above,below; int64_t remains,totalinputs = 0; CTransaction tx; struct NSPV_utxoresp *utxos,*up; - utxos = (struct NSPV_utxoresp *)calloc(num,sizeof(*utxos)); - if ( maxinputs > NSPV_MAXVINS ) - maxinputs = NSPV_MAXVINS; - if ( maxinputs > 0 ) - threshold = total/maxinputs; - else threshold = total; - for (i=0; i threshold ) - utxos[n++] = ptr[i]; - } - remains = total; - fprintf(stderr,"n.%d for total %.8f\n",n,(double)total/COIN); - for (i=0; i0; i++) - { - below = above = 0; - abovei = belowi = -1; - if ( NSPV_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 ) - { - fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN); - free(utxos); - return(0); - } - if ( belowi < 0 || abovei >= 0 ) - ind = abovei; - else ind = belowi; - if ( ind < 0 ) - { - fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind); - free(utxos); - return(0); - } - fprintf(stderr,"i.%d ind.%d abovei.%d belowi.%d n.%d\n",i,ind,abovei,belowi,n); - up = &utxos[ind]; - mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript())); - totalinputs += up->satoshis; - remains -= up->satoshis; - utxos[ind] = utxos[--n]; - memset(&utxos[n],0,sizeof(utxos[n])); - fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs); - if ( totalinputs >= total || (i+1) >= maxinputs ) - break; - } - free(utxos); - fprintf(stderr,"totalinputs %.8f vs total %.8f\n",(double)totalinputs/COIN,(double)total/COIN); - if ( totalinputs >= total ) - return(totalinputs); - return(0); -} - -std::string NSPV_signtx(CMutableTransaction &mtx,uint64_t txfee,CScript opret) -{ - auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - CTransaction vintx; std::string hex; uint256 hashBlock; int64_t change,totaloutputs=0,totalinputs=0; int32_t i,utxovout,n; - n = mtx.vout.size(); - for (i=0; i= totaloutputs+2*txfee ) - { - change = totalinputs - (totaloutputs+txfee); - mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(NSPV_pubkeystr) << OP_CHECKSIG)); - } - if ( opret.size() > 0 ) - mtx.vout.push_back(CTxOut(0,opret)); - PrecomputedTransactionData txdata(mtx); - n = mtx.vin.size(); - fprintf(stderr,"sign inputs %.8f -> outputs %.8f change %.8f\n",(double)totalinputs/COIN,(double)totaloutputs/COIN,(double)change/COIN); - for (i=0; i data; CScript opret; std::string hex; - data.resize(20); - memcpy(&data[0],&rmd160[1],20); - if ( NSPV_addinputs(mtx,satoshis+txfee,64,NSPV_utxosresult.utxos,NSPV_utxosresult.numutxos) > 0 ) - { - fprintf(stderr,"vout\n"); - mtx.vout.push_back(CTxOut(satoshis,CScript() << OP_DUP << OP_HASH160 << ParseHex(HexStr(data)) << OP_EQUALVERIFY << OP_CHECKSIG)); - fprintf(stderr,"signtx\n"); - hex = NSPV_signtx(mtx,txfee,opret); - result.push_back(Pair("result","success")); - result.push_back(Pair("hex",hex)); - // prove all the vins - return(result); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt create tx")); - return(result); - } -} - -// polling loop - -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)); - memset(&NSPV_key,0,sizeof(NSPV_key)); - NSPV_logintime = 0; - } - if ( (pto->nServices & NODE_NSPV) == 0 ) - return; - if ( KOMODO_NSPV != 0 ) - { - if ( timestamp > NSPV_lastinfo + ASSETCHAINS_BLOCKTIME/2 && timestamp > pto->prevtimes[NSPV_INFO>>1] + 2*ASSETCHAINS_BLOCKTIME/3 ) - { - len = 0; - msg[len++] = NSPV_INFO; - NSPV_req(pto,msg,len,NODE_NSPV,NSPV_INFO>>1); - } - } -} - #endif // KOMODO_NSPV_H diff --git a/src/komodo_nSPV_fullnode.h b/src/komodo_nSPV_fullnode.h new file mode 100644 index 000000000..f493512a6 --- /dev/null +++ b/src/komodo_nSPV_fullnode.h @@ -0,0 +1,464 @@ + +/****************************************************************************** + * 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 + +// 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 + +#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); +} + +uint256 NSPV_extract_desttxid(int32_t *heightp,char *symbol,std::vector opret) +{ + uint256 desttxid; int32_t i; + //for (i=0; i<32; i++) + // fprintf(stderr,"%02x",opret[i]); + //fprintf(stderr," blockhash, "); + //for (i=0; i<4; i++) + // fprintf(stderr,"%02x",opret[32+i]); + //fprintf(stderr," height, "); + iguana_rwnum(0,&opret[32],sizeof(*heightp),heightp); + //for (i=0; i<32; i++) + // fprintf(stderr,"%02x",opret[36+i]); + //fprintf(stderr," desttxid\n"); + for (i=0; i<32; i++) + ((uint8_t *)&desttxid)[i] = opret[4 + 32 + i]; + return(desttxid); +} + +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; Notarisation nota; char *symbol; + 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; + desttxids[0] = NSPV_extract_desttxid(&ntzheights[0],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; + desttxids[1] = NSPV_extract_desttxid(&ntzheights[1],symbol,E_MARSHAL(ss << nota.second)); + } + 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_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(); + 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)); + 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->prevtxlen,ptr->prevtxid); + ptr->nexttxid = NSPV_getnotarization_txid(&ptr->nexttxidht,nextht); + ptr->nextntz = NSPV_getrawtx(hashBlock,&ptr->nexttxlen,ptr->nexttxid); + //fprintf(stderr,"prevtxlen.%d nexttxlen.%d size %ld -> %ld\n",ptr->prevtxlen,ptr->nexttxlen,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->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,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; 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); + //fprintf(stderr,"got txid ht.%d\n",txid.GetHex().c_str(),height); + 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); + } + } + } + } + } +} + +#endif // KOMODO_NSPVFULLNODE_H diff --git a/src/komodo_nSPV_superlite.h b/src/komodo_nSPV_superlite.h new file mode 100644 index 000000000..aeca64a9d --- /dev/null +++ b/src/komodo_nSPV_superlite.h @@ -0,0 +1,413 @@ + +/****************************************************************************** + * 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_NSPVSUPERLITE_H +#define KOMODO_NSPVSUPERLITE_H + +// nSPV client. VERY simplistic "single threaded" networking model. for production GUI best to multithread, etc. +// no caching, no optimizations, no reducing the number of ntzsproofs needed by detecting overlaps, etc. +// advantage is that it is simpler to implement and understand to create a design for a more performant version + +#define NSPV_POLLITERS 15 +#define NSPV_POLLMICROS 100000 +#define NSPV_MAXVINS 64 + +CAmount AmountFromValue(const UniValue& value); +int32_t bitcoin_base58decode(uint8_t *data,char *coinaddr); + +uint32_t NSPV_lastinfo,NSPV_logintime; +char NSPV_wifstr[64],NSPV_pubkeystr[67]; +std::string NSPV_address; +CKey NSPV_key; +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; + +CKey *NSPV_defaultkey() +{ + return(&NSPV_key); +} + +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->prevtimes[ind] > timestamp ) + ptr->prevtimes[ind] = 0; + 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); + } else fprintf(stderr,"no pnodes\n"); + 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(struct NSPV_inforesp *ptr) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + result.push_back(Pair("height",(int64_t)ptr->height)); + result.push_back(Pair("chaintip",ptr->blockhash.GetHex())); + result.push_back(Pair("notarization",NSPV_ntz_json(&ptr->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("address",ptr->coinaddr)); + 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("prevtxlen",(int64_t)ptr->prevtxlen)); + result.push_back(Pair("nexttxid",ptr->nexttxid.GetHex())); + result.push_back(Pair("nexttxidht",(int64_t)ptr->nexttxidht)); + result.push_back(Pair("nexttxlen",(int64_t)ptr->prevtxlen)); + 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")); + NSPV_key = DecodeSecret(wifstr); + CPubKey pubkey = NSPV_key.GetPubKey(); + CKeyID vchAddress = pubkey.GetID(); + NSPV_address = EncodeDestination(vchAddress); + result.push_back(Pair("address",NSPV_address)); + result.push_back(Pair("pubkey",HexStr(pubkey))); + strcpy(NSPV_pubkeystr,HexStr(pubkey).c_str()); + 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_getinfo_json() +{ + uint8_t msg[64]; int32_t i,iters,len = 0; struct NSPV_inforesp I; + NSPV_inforesp_purge(&NSPV_inforesult); + msg[len++] = NSPV_INFO; + for (iters=0; iters<3; iters++) + { + //fprintf(stderr,"issue getinfo\n"); + if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) + { + for (i=0; i>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= 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= 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= 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); + //fprintf(stderr,"req txproof %s at height.%d\n",txid.GetHex().c_str(),height); + if ( NSPV_req(0,msg,len,NODE_NSPV,msg[0]>>1) != 0 ) + { + for (i=0; i>1) != 0 ) + { + for (i=0; i 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; + } + } +} + +#endif // KOMODO_NSPVSUPERLITE_H diff --git a/src/komodo_nSPV_wallet.h b/src/komodo_nSPV_wallet.h new file mode 100644 index 000000000..1eb173dd5 --- /dev/null +++ b/src/komodo_nSPV_wallet.h @@ -0,0 +1,277 @@ + +/****************************************************************************** + * 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_NSPVWALLET_H +#define KOMODO_NSPVWALLET_H + +// nSPV wallet uses superlite functions (and some komodod built in functions) to implement nSPV_send + +int32_t NSPV_gettransaction(uint256 txid,int32_t height,CTransaction &tx) +{ + char *txstr; int32_t retval = 0; + NSPV_txproof(txid,height); + if ( NSPV_txproofresult.txid != txid || NSPV_txproofresult.height != height ) + return(-1); + txstr = (char *)malloc(NSPV_txproofresult.txlen*2 + 1); + init_hexbytes_noT(txstr,NSPV_txproofresult.tx,NSPV_txproofresult.txlen); + if ( !DecodeHexTx(tx,txstr) ) + retval = -1; + else + { + // need to validate txproof + // get the notarizations bracket of height + // get hdrsproof of the prev,next + // verify the hdrs link back from next to prev + // merkle prove txproof to the merkleroot in the corresponding hdr + } + free(txstr); + return(retval); +} + +int32_t NSPV_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct NSPV_utxoresp utxos[],int32_t numunspents,int64_t value) +{ + int32_t i,abovei,belowi; int64_t above,below,gap,atx_value; + abovei = belowi = -1; + for (above=below=i=0; i value ) + { + gap = (atx_value - value); + if ( above == 0 || gap < above ) + { + above = gap; + abovei = i; + } + } + else + { + gap = (value - atx_value); + if ( below == 0 || gap < below ) + { + below = gap; + belowi = i; + } + } + //printf("value %.8f gap %.8f abovei.%d %.8f belowi.%d %.8f\n",dstr(value),dstr(gap),abovei,dstr(above),belowi,dstr(below)); + } + *aboveip = abovei; + *abovep = above; + *belowip = belowi; + *belowp = below; + //printf("above.%d below.%d\n",abovei,belowi); + if ( abovei >= 0 && belowi >= 0 ) + { + if ( above < (below >> 1) ) + return(abovei); + else return(belowi); + } + else if ( abovei >= 0 ) + return(abovei); + else return(belowi); +} + +int64_t NSPV_addinputs(struct NSPV_utxoresp *used,CMutableTransaction &mtx,int64_t total,int32_t maxinputs,struct NSPV_utxoresp *ptr,int32_t num) +{ + int32_t abovei,belowi,ind,vout,i,n = 0; int64_t threshold,above,below; int64_t remains,totalinputs = 0; CTransaction tx; struct NSPV_utxoresp utxos[NSPV_MAXVINS],*up; + memset(utxos,0,sizeof(utxos)); + if ( maxinputs > NSPV_MAXVINS ) + maxinputs = NSPV_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + for (i=0; i threshold ) + utxos[n++] = ptr[i]; + } + remains = total; + //fprintf(stderr,"n.%d for total %.8f\n",n,(double)total/COIN); + for (i=0; i0; i++) + { + below = above = 0; + abovei = belowi = -1; + if ( NSPV_vinselect(&abovei,&above,&belowi,&below,utxos,n,remains) < 0 ) + { + fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f\n",i,n,(double)remains/COIN,(double)total/COIN); + free(utxos); + return(0); + } + if ( belowi < 0 || abovei >= 0 ) + ind = abovei; + else ind = belowi; + if ( ind < 0 ) + { + fprintf(stderr,"error finding unspent i.%d of %d, %.8f vs %.8f, abovei.%d belowi.%d ind.%d\n",i,n,(double)remains/COIN,(double)total/COIN,abovei,belowi,ind); + free(utxos); + return(0); + } + //fprintf(stderr,"i.%d ind.%d abovei.%d belowi.%d n.%d\n",i,ind,abovei,belowi,n); + up = &utxos[ind]; + mtx.vin.push_back(CTxIn(up->txid,up->vout,CScript())); + used[i] = *up; + totalinputs += up->satoshis; + remains -= up->satoshis; + utxos[ind] = utxos[--n]; + memset(&utxos[n],0,sizeof(utxos[n])); + fprintf(stderr,"totalinputs %.8f vs total %.8f i.%d vs max.%d\n",(double)totalinputs/COIN,(double)total/COIN,i,maxinputs); + if ( totalinputs >= total || (i+1) >= maxinputs ) + break; + } + fprintf(stderr,"totalinputs %.8f vs total %.8f\n",(double)totalinputs/COIN,(double)total/COIN); + if ( totalinputs >= total ) + return(totalinputs); + return(0); +} + +std::string NSPV_signtx(CMutableTransaction &mtx,uint64_t txfee,CScript opret,struct NSPV_utxoresp used[]) +{ + auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + CTransaction vintx; std::string hex; uint256 hashBlock; int64_t change,totaloutputs=0,totalinputs=0; int32_t i,utxovout,n; + n = mtx.vout.size(); + for (i=0; i= totaloutputs+2*txfee ) + { + change = totalinputs - (totaloutputs+txfee); + mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(NSPV_pubkeystr) << OP_CHECKSIG)); + } + if ( opret.size() > 0 ) + mtx.vout.push_back(CTxOut(0,opret)); + fprintf(stderr,"sign %d inputs %.8f -> %d outputs %.8f change %.8f\n",(int32_t)mtx.vin.size(),(double)totalinputs/COIN,(int32_t)mtx.vout.size(),(double)totaloutputs/COIN,(double)change/COIN); + return(EncodeHexTx(mtx)); +} + +UniValue NSPV_send(char *srcaddr,char *destaddr,int64_t satoshis) // what its all about! +{ + UniValue result(UniValue::VOBJ); uint8_t rmd160[128]; int64_t txfee = 10000; + if ( strcmp(srcaddr,NSPV_address.c_str()) != 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid address")); + result.push_back(Pair("mismatched",srcaddr)); + return(result); + } + else if ( bitcoin_base58decode(rmd160,destaddr) != 25 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid destaddr")); + return(result); + } + if ( NSPV_inforesult.height == 0 ) + NSPV_getinfo_json(); + if ( NSPV_inforesult.height == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt getinfo")); + return(result); + } + if ( strcmp(NSPV_utxosresult.coinaddr,srcaddr) != 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) + NSPV_addressutxos(srcaddr); + if ( strcmp(NSPV_utxosresult.coinaddr,srcaddr) != 0 || NSPV_utxosresult.nodeheight < NSPV_inforesult.height ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt get addressutxos")); + return(result); + } + if ( NSPV_utxosresult.total < satoshis+txfee ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + result.push_back(Pair("balance",(double)NSPV_utxosresult.total/COIN)); + result.push_back(Pair("amount",(double)satoshis/COIN)); + return(result); + } + printf("%s numutxos.%d balance %.8f\n",NSPV_utxosresult.coinaddr,NSPV_utxosresult.numutxos,(double)NSPV_utxosresult.total/COIN); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + std::vector data; CScript opret; std::string hex; + struct NSPV_utxoresp used[NSPV_MAXVINS]; + memset(used,0,sizeof(used)); + data.resize(20); + memcpy(&data[0],&rmd160[1],20); + if ( NSPV_addinputs(used,mtx,satoshis+txfee,64,NSPV_utxosresult.utxos,NSPV_utxosresult.numutxos) > 0 ) + { + fprintf(stderr,"vout\n"); + mtx.vout.push_back(CTxOut(satoshis,CScript() << OP_DUP << OP_HASH160 << ParseHex(HexStr(data)) << OP_EQUALVERIFY << OP_CHECKSIG)); + fprintf(stderr,"signtx\n"); + hex = NSPV_signtx(mtx,txfee,opret,used); + result.push_back(Pair("result","success")); + result.push_back(Pair("hex",hex)); + // prove all the vins + return(result); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt create tx")); + return(result); + } +} + +// polling loop (really this belongs in its own file, but it is so small, it ended up here) + +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)); + memset(&NSPV_key,0,sizeof(NSPV_key)); + NSPV_logintime = 0; + } + if ( (pto->nServices & NODE_NSPV) == 0 ) + return; + if ( KOMODO_NSPV != 0 ) + { + if ( timestamp > NSPV_lastinfo + ASSETCHAINS_BLOCKTIME/2 && timestamp > pto->prevtimes[NSPV_INFO>>1] + 2*ASSETCHAINS_BLOCKTIME/3 ) + { + len = 0; + msg[len++] = NSPV_INFO; + NSPV_req(pto,msg,len,NODE_NSPV,NSPV_INFO>>1); + } + } +} + +#endif // KOMODO_NSPVWALLET_H diff --git a/src/main.cpp b/src/main.cpp index 27646ec0b..d6b3e0bb9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7046,7 +7046,10 @@ void static ProcessGetData(CNode* pfrom) } } -#include "komodo_nSPV.h" +#include "komodo_nSPV.h" // shared defines, structs, serdes, purge functions +#include "komodo_nSPV_fullnode.h" // nSPV fullnode handling of the getnSPV request messages +#include "komodo_nSPV_superlite.h" // nSPV superlite client, issuing requests and handling nSPV responses +#include "komodo_nSPV_wallet.h" // nSPV_send and support functions, really all the rest is to support this bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) {