Files
hush3/src/komodo_nSPV_fullnode.h
2019-07-06 00:19:12 -11:00

510 lines
20 KiB
C++

/******************************************************************************
* 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<uint8_t> 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
ptr->utxos[n].txid = it->first.txhash;
ptr->utxos[n].vout = (int32_t)it->first.index;
ptr->utxos[n].satoshis = it->second.satoshis;
ptr->utxos[n].height = it->second.blockHeight;
if ( ASSETCHAINS_SYMBOL[0] == 0 && it->second.satoshis >= 10*COIN )
{
ptr->utxos[n].extradata = komodo_accrued_interest(&txheight,&locktime,ptr->utxos[n].txid,ptr->utxos[n].vout,ptr->utxos[n].height,ptr->utxos[n].satoshis,tipheight);
interest += ptr->utxos[n].extradata;
}
total += it->second.satoshis;
n++;
}
if ( len < maxlen )
{
len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->utxos)*ptr->numutxos - sizeof(ptr->utxos));
fprintf(stderr,"getaddressutxos for %s -> n.%d:%d total %.8f interest %.8f len.%d\n",coinaddr,n,ptr->numutxos,dstr(total),dstr(interest),len);
if ( n == ptr->numutxos )
{
ptr->total = total;
ptr->interest = interest;
return(len);
}
}
}
if ( ptr->utxos != 0 )
free(ptr->utxos);
memset(ptr,0,sizeof(*ptr));
return(0);
}
uint8_t *NSPV_getrawtx(uint256 &hashBlock,uint16_t *txlenp,uint256 txid)
{
CTransaction tx; uint8_t *rawtx = 0;
*txlenp = 0;
{
LOCK(cs_main);
if (!GetTransaction(txid, tx, hashBlock, false))
return(0);
string strHex = EncodeHexTx(tx);
*txlenp = (int32_t)strHex.size() >> 1;
if ( *txlenp > 0 )
{
rawtx = (uint8_t *)calloc(1,*txlenp);
decode_hex(rawtx,*txlenp,(char *)strHex.c_str());
}
}
return(rawtx);
}
int32_t NSPV_sendrawtransaction(struct NSPV_broadcastresp *ptr,uint8_t *data,int32_t n)
{
CTransaction tx; std::vector<uint8_t> rawdata;
ptr->retcode = 0;
rawdata.resize(n);
memcpy(&rawdata[0],data,n);
if ( DecodeHexTx(tx,HexStr(rawdata)) != 0 )
{
LOCK(cs_main);
ptr->txid = tx.GetHash();
//fprintf(stderr,"addmempool transaction %s\n",ptr->txid.GetHex().c_str());
if ( myAddtomempool(tx) != 0 )
ptr->retcode = 1;
else ptr->retcode = 0;
//fprintf(stderr,"relay transaction %s retcode.%d\n",ptr->txid.GetHex().c_str(),ptr->retcode);
RelayTransaction(tx);
} else ptr->retcode = -1;
return(sizeof(*ptr));
}
int32_t NSPV_gettxproof(struct NSPV_txproof *ptr,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<uint256> setTxids;
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION);
setTxids.insert(txid);
CMerkleBlock mb(block, setTxids);
ssMB << mb;
std::vector<uint8_t> 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; i<ptr->common.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<uint8_t> request) // received a request
{
int32_t len,slen,ind; std::vector<uint8_t> 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);
}
}
}
}
else if ( request[0] == NSPV_BROADCAST )
{
if ( timestamp > pfrom->prevtimes[ind] )
{
struct NSPV_broadcastresp B; uint16_t n,offset; uint256 txid;
if ( len > 1+sizeof(txid)+sizeof(n) )
{
iguana_rwbignum(0,&request[1],sizeof(txid),(uint8_t *)&txid);
iguana_rwnum(0,&request[1+sizeof(txid)],sizeof(n),&n);
memset(&B,0,sizeof(B));
offset = 1 + sizeof(txid) + sizeof(n);
if ( request.size() == offset+n && (slen= NSPV_sendrawtransaction(&B,&request[offset],n)) > 0 )
{
response.resize(1 + slen);
response[0] = NSPV_BROADCASTRESP;
if ( NSPV_rwbroadcastresp(1,&response[1],&B) == slen )
{
pfrom->PushMessage("nSPV",response);
pfrom->prevtimes[ind] = timestamp;
}
NSPV_broadcast_purge(&B);
}
}
}
}
}
}
#endif // KOMODO_NSPVFULLNODE_H