Hush Full Node software. We were censored from Github, this is where all development happens now.
https://hush.is
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1226 lines
52 KiB
1226 lines
52 KiB
// Copyright (c) 2016-2020 The Hush developers
|
|
// Distributed under the GPLv3 software license, see the accompanying
|
|
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
/******************************************************************************
|
|
* Copyright © 2014-2019 The SuperNET Developers. *
|
|
* *
|
|
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
|
* the top-level directory of this distribution for the individual copyright *
|
|
* holder information and the developer policies on copyright and licensing. *
|
|
* *
|
|
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
|
* SuperNET software, including this file may be copied, modified, propagated *
|
|
* or distributed except according to the terms contained in the LICENSE file *
|
|
* *
|
|
* Removal or modification of this copyright notice is prohibited. *
|
|
* *
|
|
******************************************************************************/
|
|
#ifndef HUSH_NSPVFULLNODE_H
|
|
#define HUSH_NSPVFULLNODE_H
|
|
|
|
// NSPV_get... functions need to return the exact serialized length, which is the size of the structure minus size of pointers, plus size of allocated data
|
|
|
|
#include "notarizationdb.h"
|
|
#include "rpc/server.h"
|
|
|
|
static std::map<std::string,bool> nspv_remote_commands = {{"channelsopen", true},{"channelspayment", true},{"channelsclose", true},{"channelsrefund", true},
|
|
{"channelslist", true},{"channelsinfo", true},{"oraclescreate", true},{"oraclesfund", true},{"oraclesregister", true},{"oraclessubscribe", true},
|
|
{"oraclesdata", true},{"oraclesinfo", false},{"oracleslist", false},{"gatewaysbind", true},{"gatewaysdeposit", true},{"gatewaysclaim", true},{"gatewayswithdraw", true},
|
|
{"gatewayspartialsign", true},{"gatewayscompletesigning", true},{"gatewaysmarkdone", true},{"gatewayspendingdeposits", true},{"gatewayspendingwithdraws", true},
|
|
{"gatewaysprocessed", true},{"gatewaysinfo", false},{"gatewayslist", false},{"faucetfund", true},{"faucetget", true}};
|
|
|
|
struct NSPV_ntzargs
|
|
{
|
|
uint256 txid,desttxid,blockhash;
|
|
int32_t txidht,ntzheight;
|
|
};
|
|
|
|
int32_t NSPV_notarization_find(struct NSPV_ntzargs *args,int32_t height,int32_t dir)
|
|
{
|
|
int32_t ntzheight = 0; uint256 hashBlock; CTransaction tx; Notarization nota; char *symbol; std::vector<uint8_t> opret;
|
|
symbol = (SMART_CHAIN_SYMBOL[0] == 0) ? (char *)"KMD" : SMART_CHAIN_SYMBOL;
|
|
memset(args,0,sizeof(*args));
|
|
if ( dir > 0 )
|
|
height += 10;
|
|
if ( (args->txidht= ScanNotarizationsDB(height,symbol,1440,nota)) == 0 )
|
|
return(-1);
|
|
args->txid = nota.first;
|
|
if ( !GetTransaction(args->txid,tx,hashBlock,false) || tx.vout.size() < 2 )
|
|
return(-2);
|
|
GetOpReturnData(tx.vout[1].scriptPubKey,opret);
|
|
if ( opret.size() >= 32*2+4 )
|
|
args->desttxid = NSPV_opretextract(&args->ntzheight,&args->blockhash,symbol,opret,args->txid);
|
|
return(args->ntzheight);
|
|
}
|
|
|
|
int32_t NSPV_notarized_bracket(struct NSPV_ntzargs *prev,struct NSPV_ntzargs *next,int32_t height)
|
|
{
|
|
uint256 bhash; int32_t txidht,ntzht,nextht,i=0;
|
|
memset(prev,0,sizeof(*prev));
|
|
memset(next,0,sizeof(*next));
|
|
if ( (ntzht= NSPV_notarization_find(prev,height,-1)) < 0 || ntzht > height || ntzht == 0 )
|
|
return(-1);
|
|
txidht = height+1;
|
|
while ( (ntzht= NSPV_notarization_find(next,txidht,1)) < height )
|
|
{
|
|
nextht = next->txidht + 10*i;
|
|
//fprintf(stderr,"found forward ntz, but ntzht.%d vs height.%d, txidht.%d -> nextht.%d\n",next->ntzheight,height,txidht,nextht);
|
|
memset(next,0,sizeof(*next));
|
|
txidht = nextht;
|
|
if ( ntzht <= 0 )
|
|
break;
|
|
if ( i++ > 10 )
|
|
break;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int32_t NSPV_ntzextract(struct NSPV_ntz *ptr,uint256 ntztxid,int32_t txidht,uint256 desttxid,int32_t ntzheight)
|
|
{
|
|
CBlockIndex *pindex;
|
|
ptr->blockhash = *chainActive[ntzheight]->phashBlock;
|
|
ptr->height = ntzheight;
|
|
ptr->txidheight = txidht;
|
|
ptr->othertxid = desttxid;
|
|
ptr->txid = ntztxid;
|
|
if ( (pindex= hush_chainactive(ptr->txidheight)) != 0 )
|
|
ptr->timestamp = pindex->nTime;
|
|
return(0);
|
|
}
|
|
|
|
int32_t NSPV_getntzsresp(struct NSPV_ntzsresp *ptr,int32_t origreqheight)
|
|
{
|
|
struct NSPV_ntzargs prev,next; int32_t reqheight = origreqheight;
|
|
if ( reqheight < chainActive.LastTip()->GetHeight() )
|
|
reqheight++;
|
|
if ( NSPV_notarized_bracket(&prev,&next,reqheight) == 0 )
|
|
{
|
|
if ( prev.ntzheight != 0 )
|
|
{
|
|
ptr->reqheight = origreqheight;
|
|
if ( NSPV_ntzextract(&ptr->prevntz,prev.txid,prev.txidht,prev.desttxid,prev.ntzheight) < 0 )
|
|
return(-1);
|
|
}
|
|
if ( next.ntzheight != 0 )
|
|
{
|
|
if ( NSPV_ntzextract(&ptr->nextntz,next.txid,next.txidht,next.desttxid,next.ntzheight) < 0 )
|
|
return(-1);
|
|
}
|
|
}
|
|
return(sizeof(*ptr));
|
|
}
|
|
|
|
int32_t NSPV_setequihdr(struct NSPV_equihdr *hdr,int32_t height)
|
|
{
|
|
CBlockIndex *pindex;
|
|
if ( (pindex= hush_chainactive(height)) != 0 )
|
|
{
|
|
hdr->nVersion = pindex->nVersion;
|
|
if ( pindex->pprev == 0 )
|
|
return(-1);
|
|
hdr->hashPrevBlock = pindex->pprev->GetBlockHash();
|
|
hdr->hashMerkleRoot = pindex->hashMerkleRoot;
|
|
hdr->hashFinalSaplingRoot = pindex->hashFinalSaplingRoot;
|
|
hdr->nTime = pindex->nTime;
|
|
hdr->nBits = pindex->nBits;
|
|
hdr->nNonce = pindex->nNonce;
|
|
memcpy(hdr->nSolution,&pindex->nSolution[0],sizeof(hdr->nSolution));
|
|
return(sizeof(*hdr));
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
int32_t NSPV_getinfo(struct NSPV_inforesp *ptr,int32_t reqheight)
|
|
{
|
|
int32_t prevMoMheight,len = 0; CBlockIndex *pindex, *pindex2; struct NSPV_ntzsresp pair;
|
|
if ( (pindex= chainActive.LastTip()) != 0 )
|
|
{
|
|
ptr->height = pindex->GetHeight();
|
|
ptr->blockhash = pindex->GetBlockHash();
|
|
memset(&pair,0,sizeof(pair));
|
|
if ( NSPV_getntzsresp(&pair,ptr->height-1) < 0 )
|
|
return(-1);
|
|
ptr->notarization = pair.prevntz;
|
|
if ( (pindex2= hush_chainactive(ptr->notarization.txidheight)) != 0 )
|
|
ptr->notarization.timestamp = pindex->nTime;
|
|
//fprintf(stderr, "timestamp.%i\n", ptr->notarization.timestamp );
|
|
if ( reqheight == 0 )
|
|
reqheight = ptr->height;
|
|
ptr->hdrheight = reqheight;
|
|
ptr->version = NSPV_PROTOCOL_VERSION;
|
|
if ( NSPV_setequihdr(&ptr->H,reqheight) < 0 )
|
|
return(-1);
|
|
return(sizeof(*ptr));
|
|
} else return(-1);
|
|
}
|
|
|
|
int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter)
|
|
{
|
|
int64_t total = 0,interest=0; uint32_t locktime; int32_t ind=0,tipheight,maxlen,txheight,n = 0,len = 0;
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
|
SetCCunspents(unspentOutputs,coinaddr,isCC);
|
|
maxlen = MAX_BLOCK_SIZE(tipheight) - 512;
|
|
maxlen /= sizeof(*ptr->utxos);
|
|
strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1);
|
|
ptr->CCflag = isCC;
|
|
ptr->filter = filter;
|
|
if ( skipcount < 0 )
|
|
skipcount = 0;
|
|
if ( (ptr->numutxos= (int32_t)unspentOutputs.size()) >= 0 && ptr->numutxos < maxlen )
|
|
{
|
|
tipheight = chainActive.LastTip()->GetHeight();
|
|
ptr->nodeheight = tipheight;
|
|
if ( skipcount >= ptr->numutxos )
|
|
skipcount = ptr->numutxos-1;
|
|
ptr->skipcount = skipcount;
|
|
if ( ptr->numutxos-skipcount > 0 )
|
|
{
|
|
ptr->utxos = (struct NSPV_utxoresp *)calloc(ptr->numutxos-skipcount,sizeof(*ptr->utxos));
|
|
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
|
{
|
|
// if gettxout is != null to handle mempool
|
|
{
|
|
if ( n >= skipcount && myIsutxo_spentinmempool(ignoretxid,ignorevin,it->first.txhash,(int32_t)it->first.index) == 0 )
|
|
{
|
|
ptr->utxos[ind].txid = it->first.txhash;
|
|
ptr->utxos[ind].vout = (int32_t)it->first.index;
|
|
ptr->utxos[ind].satoshis = it->second.satoshis;
|
|
ptr->utxos[ind].height = it->second.blockHeight;
|
|
ind++;
|
|
total += it->second.satoshis;
|
|
}
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
ptr->numutxos = ind;
|
|
if ( len < maxlen )
|
|
{
|
|
len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->utxos)*ptr->numutxos - sizeof(ptr->utxos));
|
|
//fprintf(stderr,"getaddressutxos for %s -> n.%d:%d total %.8f interest %.8f len.%d\n",coinaddr,n,ptr->numutxos,dstr(total),dstr(interest),len);
|
|
ptr->total = total;
|
|
ptr->interest = interest;
|
|
return(len);
|
|
}
|
|
}
|
|
if ( ptr->utxos != 0 )
|
|
free(ptr->utxos);
|
|
memset(ptr,0,sizeof(*ptr));
|
|
return(0);
|
|
}
|
|
|
|
class BaseCCChecker {
|
|
public:
|
|
/// base check function
|
|
/// @param vouts vouts where checked vout and opret are
|
|
/// @param nvout vout index to check
|
|
/// @param evalcode which must be in the opret
|
|
/// @param funcids allowed funcids in string
|
|
/// @param filtertxid txid that should be in the opret (after funcid)
|
|
virtual bool checkCC(uint256 txid, const std::vector<CTxOut> &vouts, int32_t nvout, uint8_t evalcode, std::string funcids, uint256 filtertxid) = 0;
|
|
};
|
|
|
|
/// default cc vout checker for use in NSPV_getccmoduleutxos
|
|
/// checks if a vout is cc, has required evalcode, allowed funcids and txid
|
|
/// check both cc opret and last vout opret
|
|
/// maybe customized in via inheritance
|
|
class DefaultCCChecker : public BaseCCChecker {
|
|
|
|
private:
|
|
|
|
public:
|
|
DefaultCCChecker() { }
|
|
virtual bool checkCC(uint256 txid, const std::vector<CTxOut> &vouts, int32_t nvout, uint8_t evalcode, std::string funcids, uint256 filtertxid)
|
|
{
|
|
CScript opret, dummy;
|
|
std::vector< vscript_t > vParams;
|
|
vscript_t vopret;
|
|
|
|
if (nvout < vouts.size())
|
|
{
|
|
// first check if it is cc vout
|
|
if (vouts[nvout].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams))
|
|
{
|
|
// try to find cc opret
|
|
if (vParams.size() > 0)
|
|
{
|
|
COptCCParams p(vParams[0]); // parse vout data
|
|
if (p.vData.size() > 0)
|
|
{
|
|
vopret = p.vData[0]; // get opret data
|
|
}
|
|
}
|
|
// if no cc opret check last vout opret
|
|
if (vopret.size() == 0)
|
|
{
|
|
GetOpReturnData(vouts.back().scriptPubKey, vopret);
|
|
}
|
|
if (vopret.size() > 2)
|
|
{
|
|
uint8_t opretEvalcode, opretFuncid;
|
|
uint256 opretTxid;
|
|
bool isEof = true;
|
|
bool isCreateTx = false;
|
|
|
|
// parse opret first 3 fields:
|
|
bool parseOk = E_UNMARSHAL(vopret,
|
|
ss >> opretEvalcode;
|
|
ss >> opretFuncid;
|
|
if (funcids.size() > 0 && opretFuncid == funcids[0]) // this means that we check txid only for second+ funcid in array (considering that the first funcid is the creation txid itself like tokens)
|
|
{
|
|
isCreateTx = true;
|
|
}
|
|
else
|
|
{
|
|
ss >> opretTxid;
|
|
isCreateTx = false;
|
|
}
|
|
isEof = ss.eof(); );
|
|
|
|
opretTxid = revuint256(opretTxid);
|
|
std::cerr << __func__ << " " << "opretEvalcode=" << opretEvalcode << " opretFuncid=" << (char)opretFuncid << " isCreateTx=" << isCreateTx << " opretTxid=" << opretTxid.GetHex() << std::endl;
|
|
if( parseOk /*parseOk=true if eof reached*/|| !isEof /*more data means okay*/)
|
|
{
|
|
if (evalcode == opretEvalcode && std::find(funcids.begin(), funcids.end(), (char)opretFuncid) != funcids.end() &&
|
|
(isCreateTx && filtertxid == txid || !isCreateTx && filtertxid == opretTxid))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
static class DefaultCCChecker defaultCCChecker;
|
|
|
|
// table of pluggable cc vout checkers for usage in NSPV_getccmoduleutxos
|
|
// if the checker is not in the table for a evalcode then defaultCCChecker is used
|
|
static std::map<uint8_t, class BaseCCChecker*> ccCheckerTable =
|
|
{
|
|
};
|
|
|
|
// implements SPV server's part, gets cc module utxos, filtered by evalcode, funcid and txid on opret, for the specified amount
|
|
// if the amount param is 0 returns total available filtere utxo amount and returns no utxos
|
|
// first char funcid in the string param is considered as the creation tx funcid so filtertxid is compared to the creation txid itself
|
|
// for other funcids filtertxid is compared to the txid in opreturn
|
|
int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_t amount, uint8_t evalcode, std::string funcids, uint256 filtertxid)
|
|
{
|
|
int64_t total = 0, totaladded = 0;
|
|
uint32_t locktime;
|
|
int32_t tipheight=0, len, maxlen;
|
|
int32_t maxinputs = CC_MAXVINS;
|
|
|
|
std::vector<struct CC_utxo> utxoSelected;
|
|
utxoSelected.reserve(CC_MAXVINS);
|
|
|
|
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
|
SetCCunspents(unspentOutputs, coinaddr, true);
|
|
|
|
maxlen = MAX_BLOCK_SIZE(tipheight) - 512;
|
|
//maxlen /= sizeof(*ptr->utxos); // TODO why was this? we need maxlen in bytes, don't we?
|
|
|
|
//ptr->numutxos = (uint16_t)unspentOutputs.size();
|
|
//if (ptr->numutxos >= 0 && ptr->numutxos < maxlen)
|
|
//{
|
|
ptr->utxos = NULL;
|
|
ptr->numutxos = 0;
|
|
strncpy(ptr->coinaddr, coinaddr, sizeof(ptr->coinaddr) - 1);
|
|
ptr->CCflag = 1;
|
|
tipheight = chainActive.LastTip()->GetHeight();
|
|
ptr->nodeheight = tipheight; // will be checked in libnspv
|
|
//}
|
|
|
|
// select all appropriate utxos:
|
|
std::cerr << __func__ << " " << "searching addr=" << coinaddr << std::endl;
|
|
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
|
|
{
|
|
if (myIsutxo_spentinmempool(ignoretxid, ignorevin, it->first.txhash, (int32_t)it->first.index) == 0)
|
|
{
|
|
//const CCoins *pcoins = pcoinsTip->AccessCoins(it->first.txhash); <-- no opret in coins
|
|
CTransaction tx;
|
|
uint256 hashBlock;
|
|
int32_t nvout = it->first.index;
|
|
if (myGetTransaction(it->first.txhash, tx, hashBlock))
|
|
{
|
|
class BaseCCChecker *baseChecker = ccCheckerTable[evalcode];
|
|
|
|
// if a checker is set for evalcode use it otherwise use the default checker:
|
|
if (baseChecker && baseChecker->checkCC(it->first.txhash, tx.vout, nvout, evalcode, funcids, filtertxid) || defaultCCChecker.checkCC(it->first.txhash, tx.vout, nvout, evalcode, funcids, filtertxid))
|
|
{
|
|
std::cerr << __func__ << " " << "filtered utxo with amount=" << tx.vout[nvout].nValue << std::endl;
|
|
|
|
struct CC_utxo utxo;
|
|
utxo.txid = it->first.txhash;
|
|
utxo.vout = (int32_t)it->first.index;
|
|
utxo.nValue = it->second.satoshis;
|
|
//utxo.height = it->second.blockHeight;
|
|
utxoSelected.push_back(utxo);
|
|
total += it->second.satoshis;
|
|
}
|
|
}
|
|
else
|
|
std::cerr << __func__ << " " << "ERROR: cant load tx for txid, please reindex" << std::endl;
|
|
}
|
|
}
|
|
|
|
|
|
if (amount == 0) {
|
|
// just return total value
|
|
ptr->total = total;
|
|
len = (int32_t)(sizeof(*ptr) - sizeof(ptr->utxos)/*subtract not serialized part of NSPV_utxoresp*/);
|
|
return len;
|
|
}
|
|
|
|
// pick optimal utxos for the requested amount
|
|
CAmount remains = amount;
|
|
std::vector<struct CC_utxo> utxoAdded;
|
|
|
|
while (utxoSelected.size() > 0)
|
|
{
|
|
int64_t below = 0, above = 0;
|
|
int32_t abovei = -1, belowi = -1, ind = -1;
|
|
|
|
if (CC_vinselect(&abovei, &above, &belowi, &below, utxoSelected.data(), utxoSelected.size(), remains) < 0)
|
|
{
|
|
std::cerr << "error CC_vinselect" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl;
|
|
return 0;
|
|
}
|
|
if (abovei >= 0) // best is 'above'
|
|
ind = abovei;
|
|
else if (belowi >= 0) // second try is 'below'
|
|
ind = belowi;
|
|
else
|
|
{
|
|
std::cerr << "error finding unspent" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
utxoAdded.push_back(utxoSelected[ind]);
|
|
total += utxoSelected[ind].nValue;
|
|
remains -= utxoSelected[ind].nValue;
|
|
|
|
// remove used utxo[ind]:
|
|
utxoSelected[ind] = utxoSelected.back();
|
|
utxoSelected.pop_back();
|
|
|
|
if (total >= amount) // found the requested amount
|
|
break;
|
|
if (utxoAdded.size() >= maxinputs) // reached maxinputs
|
|
break;
|
|
}
|
|
ptr->numutxos = (uint16_t)utxoAdded.size();
|
|
ptr->total = total;
|
|
ptr->utxos = (NSPV_utxoresp*)calloc(ptr->numutxos, sizeof(ptr->utxos[0]));
|
|
|
|
for (uint16_t i = 0; i < ptr->numutxos; i++)
|
|
{
|
|
ptr->utxos[i].satoshis = utxoAdded[i].nValue;
|
|
ptr->utxos[i].txid = utxoAdded[i].txid;
|
|
ptr->utxos[i].vout = utxoAdded[i].vout;
|
|
}
|
|
|
|
len = (int32_t)(sizeof(*ptr) - sizeof(ptr->utxos)/*subtract not serialized part of NSPV_utxoresp*/ + sizeof(*ptr->utxos)*ptr->numutxos);
|
|
if (len < maxlen)
|
|
return len; // good length
|
|
else
|
|
{
|
|
NSPV_utxosresp_purge(ptr);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int32_t NSPV_getaddresstxids(struct NSPV_txidsresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter)
|
|
{
|
|
int32_t maxlen,txheight,ind=0,n = 0,len = 0; CTransaction tx; uint256 hashBlock;
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
|
|
SetCCtxids(txids,coinaddr,isCC);
|
|
ptr->nodeheight = chainActive.LastTip()->GetHeight();
|
|
maxlen = MAX_BLOCK_SIZE(ptr->nodeheight) - 512;
|
|
maxlen /= sizeof(*ptr->txids);
|
|
strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1);
|
|
ptr->CCflag = isCC;
|
|
ptr->filter = filter;
|
|
if ( skipcount < 0 )
|
|
skipcount = 0;
|
|
if ( (ptr->numtxids= (int32_t)txids.size()) >= 0 && ptr->numtxids < maxlen )
|
|
{
|
|
if ( skipcount >= ptr->numtxids )
|
|
skipcount = ptr->numtxids-1;
|
|
ptr->skipcount = skipcount;
|
|
if ( ptr->numtxids-skipcount > 0 )
|
|
{
|
|
ptr->txids = (struct NSPV_txidresp *)calloc(ptr->numtxids-skipcount,sizeof(*ptr->txids));
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
|
{
|
|
if ( n >= skipcount )
|
|
{
|
|
ptr->txids[ind].txid = it->first.txhash;
|
|
ptr->txids[ind].vout = (int32_t)it->first.index;
|
|
ptr->txids[ind].satoshis = (int64_t)it->second;
|
|
ptr->txids[ind].height = (int64_t)it->first.blockHeight;
|
|
ind++;
|
|
}
|
|
n++;
|
|
}
|
|
}
|
|
ptr->numtxids = ind;
|
|
len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->txids)*ptr->numtxids - sizeof(ptr->txids));
|
|
return(len);
|
|
}
|
|
if ( ptr->txids != 0 )
|
|
free(ptr->txids);
|
|
memset(ptr,0,sizeof(*ptr));
|
|
return(0);
|
|
}
|
|
|
|
int32_t NSPV_mempoolfuncs(bits256 *satoshisp,int32_t *vindexp,std::vector<uint256> &txids,char *coinaddr,bool isCC,uint8_t funcid,uint256 txid,int32_t vout)
|
|
{
|
|
int32_t num = 0,vini = 0,vouti = 0; uint8_t evalcode=0,func=0; std::vector<uint8_t> vopret; char destaddr[64];
|
|
*vindexp = -1;
|
|
memset(satoshisp,0,sizeof(*satoshisp));
|
|
if ( funcid == NSPV_CC_TXIDS)
|
|
{
|
|
std::vector<std::pair<CAddressIndexKey, CAmount> > tmp_txids; uint256 tmp_txid,hashBlock;
|
|
int32_t n=0,skipcount=vout>>16; uint8_t eval=(vout>>8)&0xFF, func=vout&0xFF;
|
|
|
|
CTransaction tx;
|
|
SetCCtxids(tmp_txids,coinaddr,isCC);
|
|
if ( skipcount < 0 ) skipcount = 0;
|
|
if ( skipcount >= tmp_txids.size() )
|
|
skipcount = tmp_txids.size()-1;
|
|
if ( tmp_txids.size()-skipcount > 0 )
|
|
{
|
|
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=tmp_txids.begin(); it!=tmp_txids.end(); it++)
|
|
{
|
|
if (txid!=zeroid || func!=0)
|
|
{
|
|
myGetTransaction(it->first.txhash,tx,hashBlock);
|
|
std::vector<std::pair<uint8_t, vscript_t>> oprets; uint256 tokenid,txid;
|
|
std::vector<uint8_t> vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode;
|
|
std::vector<CPubKey> pubkeys;
|
|
|
|
if (DecodeTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
|
|
{
|
|
vopret=vOpretExtra;
|
|
}
|
|
else GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey, vopret);
|
|
script = (uint8_t *)vopret.data();
|
|
if ( vopret.size() > 2 && script[0]==eval )
|
|
{
|
|
switch (eval)
|
|
{
|
|
case EVAL_CHANNELS:EVAL_PEGS:EVAL_ORACLES:EVAL_GAMES:EVAL_IMPORTGATEWAY:EVAL_ROGUE:
|
|
E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> tmp_txid;);
|
|
if (e!=eval || (txid!=zeroid && txid!=tmp_txid) || (func!=0 && f!=func)) continue;
|
|
break;
|
|
case EVAL_TOKENS:EVAL_DICE:EVAL_DILITHIUM:EVAL_FAUCET:EVAL_LOTO:EVAL_PAYMENTS:EVAL_REWARDS:
|
|
E_UNMARSHAL(vopret,ss >> e; ss >> f;);
|
|
if (e!=eval || (func!=0 && f!=func)) continue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( n >= skipcount ) txids.push_back(it->first.txhash);
|
|
n++;
|
|
}
|
|
return (n-skipcount);
|
|
}
|
|
return (0);
|
|
}
|
|
if ( mempool.size() == 0 )
|
|
return(0);
|
|
if ( funcid == NSPV_MEMPOOL_CCEVALCODE )
|
|
{
|
|
isCC = true;
|
|
evalcode = vout & 0xff;
|
|
func = (vout >> 8) & 0xff;
|
|
}
|
|
LOCK(mempool.cs);
|
|
BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx)
|
|
{
|
|
const CTransaction &tx = e.GetTx();
|
|
const uint256 &hash = tx.GetHash();
|
|
if ( funcid == NSPV_MEMPOOL_ALL )
|
|
{
|
|
txids.push_back(hash);
|
|
num++;
|
|
continue;
|
|
}
|
|
else if ( funcid == NSPV_MEMPOOL_INMEMPOOL )
|
|
{
|
|
if ( hash == txid )
|
|
{
|
|
txids.push_back(hash);
|
|
return(++num);
|
|
}
|
|
continue;
|
|
}
|
|
else if ( funcid == NSPV_MEMPOOL_CCEVALCODE )
|
|
{
|
|
if ( tx.vout.size() > 1 )
|
|
{
|
|
CScript scriptPubKey = tx.vout[tx.vout.size()-1].scriptPubKey;
|
|
if ( GetOpReturnData(scriptPubKey,vopret) != 0 )
|
|
{
|
|
if ( vopret[0] == evalcode && vopret[1] == func )
|
|
{
|
|
txids.push_back(hash);
|
|
num++;
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if ( funcid == NSPV_MEMPOOL_ISSPENT )
|
|
{
|
|
BOOST_FOREACH(const CTxIn &txin,tx.vin)
|
|
{
|
|
//fprintf(stderr,"%s/v%d ",uint256_str(str,txin.prevout.hash),txin.prevout.n);
|
|
if ( txin.prevout.n == vout && txin.prevout.hash == txid )
|
|
{
|
|
txids.push_back(hash);
|
|
*vindexp = vini;
|
|
return(++num);
|
|
}
|
|
vini++;
|
|
}
|
|
}
|
|
else if ( funcid == NSPV_MEMPOOL_ADDRESS )
|
|
{
|
|
BOOST_FOREACH(const CTxOut &txout,tx.vout)
|
|
{
|
|
if ( txout.scriptPubKey.IsPayToCryptoCondition() == isCC )
|
|
{
|
|
Getscriptaddress(destaddr,txout.scriptPubKey);
|
|
if ( strcmp(destaddr,coinaddr) == 0 )
|
|
{
|
|
txids.push_back(hash);
|
|
*vindexp = vouti;
|
|
if ( num < 4 )
|
|
satoshisp->ulongs[num] = txout.nValue;
|
|
num++;
|
|
}
|
|
}
|
|
vouti++;
|
|
}
|
|
}
|
|
//fprintf(stderr,"are vins for %s\n",uint256_str(str,hash));
|
|
}
|
|
return(num);
|
|
}
|
|
|
|
int32_t NSPV_mempooltxids(struct NSPV_mempoolresp *ptr,char *coinaddr,uint8_t isCC,uint8_t funcid,uint256 txid,int32_t vout)
|
|
{
|
|
std::vector<uint256> txids; bits256 satoshis; uint256 tmp,tmpdest; int32_t i,len = 0;
|
|
ptr->nodeheight = chainActive.LastTip()->GetHeight();
|
|
strncpy(ptr->coinaddr,coinaddr,sizeof(ptr->coinaddr)-1);
|
|
ptr->CCflag = isCC;
|
|
ptr->txid = txid;
|
|
ptr->vout = vout;
|
|
ptr->funcid = funcid;
|
|
if ( NSPV_mempoolfuncs(&satoshis,&ptr->vindex,txids,coinaddr,isCC,funcid,txid,vout) >= 0 )
|
|
{
|
|
if ( (ptr->numtxids= (int32_t)txids.size()) >= 0 )
|
|
{
|
|
if ( ptr->numtxids > 0 )
|
|
{
|
|
ptr->txids = (uint256 *)calloc(ptr->numtxids,sizeof(*ptr->txids));
|
|
for (i=0; i<ptr->numtxids; i++)
|
|
{
|
|
tmp = txids[i];
|
|
dragon_rwbignum(0,(uint8_t *)&tmp,sizeof(*ptr->txids),(uint8_t *)&ptr->txids[i]);
|
|
}
|
|
}
|
|
if ( funcid == NSPV_MEMPOOL_ADDRESS )
|
|
{
|
|
memcpy(&tmp,&satoshis,sizeof(tmp));
|
|
dragon_rwbignum(0,(uint8_t *)&tmp,sizeof(ptr->txid),(uint8_t *)&ptr->txid);
|
|
}
|
|
len = (int32_t)(sizeof(*ptr) + sizeof(*ptr->txids)*ptr->numtxids - sizeof(ptr->txids));
|
|
return(len);
|
|
}
|
|
}
|
|
if ( ptr->txids != 0 )
|
|
free(ptr->txids);
|
|
memset(ptr,0,sizeof(*ptr));
|
|
return(0);
|
|
}
|
|
|
|
int32_t NSPV_remoterpc(struct NSPV_remoterpcresp *ptr,char *json,int n)
|
|
{
|
|
std::vector<uint256> txids; int32_t i,len = 0; UniValue result; std::string response;
|
|
UniValue request(UniValue::VOBJ),rpc_result(UniValue::VOBJ); JSONRequest jreq; CPubKey mypk;
|
|
|
|
try
|
|
{
|
|
request.read(json,n);
|
|
jreq.parse(request);
|
|
strcpy(ptr->method,jreq.strMethod.c_str());
|
|
len+=sizeof(ptr->method);
|
|
std::map<std::string, bool>::iterator it = nspv_remote_commands.find(jreq.strMethod);
|
|
if (it==nspv_remote_commands.end())
|
|
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not supported!");
|
|
const CRPCCommand *cmd=tableRPC[jreq.strMethod];
|
|
if (!cmd)
|
|
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
|
|
if (it->second)
|
|
{
|
|
if (!request.exists("mypk"))
|
|
throw JSONRPCError(RPC_PARSE_ERROR, "No pubkey supplied in remote rpc request, necessary for this type of rpc");
|
|
std::string str=request["mypk"].get_str();
|
|
mypk=pubkey2pk(ParseHex(str));
|
|
if (!mypk.IsValid())
|
|
throw JSONRPCError(RPC_PARSE_ERROR, "Not valid pubkey passed in remote rpc call");
|
|
}
|
|
if ((result = cmd->actor(jreq.params,false,mypk)).isObject() || result.isArray())
|
|
{
|
|
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
|
|
response=rpc_result.write();
|
|
memcpy(ptr->json,response.c_str(),response.size());
|
|
len+=response.size();
|
|
return (len);
|
|
}
|
|
else throw JSONRPCError(RPC_MISC_ERROR, "Error in executing RPC on remote node");
|
|
}
|
|
catch (const UniValue& objError)
|
|
{
|
|
rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
|
|
response=rpc_result.write();
|
|
}
|
|
catch (const runtime_error& e)
|
|
{
|
|
rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
|
response=rpc_result.write();
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
rpc_result = JSONRPCReplyObj(NullUniValue,JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
|
response=rpc_result.write();
|
|
}
|
|
memcpy(ptr->json,response.c_str(),response.size());
|
|
len+=response.size();
|
|
return (len);
|
|
}
|
|
|
|
uint8_t *NSPV_getrawtx(CTransaction &tx,uint256 &hashBlock,int32_t *txlenp,uint256 txid)
|
|
{
|
|
uint8_t *rawtx = 0;
|
|
*txlenp = 0;
|
|
{
|
|
if (!GetTransaction(txid, tx, hashBlock, false))
|
|
{
|
|
//fprintf(stderr,"error getting transaction %s\n",txid.GetHex().c_str());
|
|
return(0);
|
|
}
|
|
string strHex = EncodeHexTx(tx);
|
|
*txlenp = (int32_t)strHex.size() >> 1;
|
|
if ( *txlenp > 0 )
|
|
{
|
|
rawtx = (uint8_t *)calloc(1,*txlenp);
|
|
decode_hex(rawtx,*txlenp,(char *)strHex.c_str());
|
|
}
|
|
}
|
|
return(rawtx);
|
|
}
|
|
|
|
int32_t NSPV_sendrawtransaction(struct NSPV_broadcastresp *ptr,uint8_t *data,int32_t n)
|
|
{
|
|
CTransaction tx;
|
|
ptr->retcode = 0;
|
|
if ( NSPV_txextract(tx,data,n) == 0 )
|
|
{
|
|
//LOCK(cs_main);
|
|
ptr->txid = tx.GetHash();
|
|
//fprintf(stderr,"try to addmempool transaction %s\n",ptr->txid.GetHex().c_str());
|
|
if ( myAddtomempool(tx) != 0 )
|
|
{
|
|
ptr->retcode = 1;
|
|
//int32_t i;
|
|
//for (i=0; i<n; i++)
|
|
// fprintf(stderr,"%02x",data[i]);
|
|
fprintf(stderr," relay transaction %s retcode.%d\n",ptr->txid.GetHex().c_str(),ptr->retcode);
|
|
RelayTransaction(tx);
|
|
} else ptr->retcode = -3;
|
|
|
|
} else ptr->retcode = -1;
|
|
return(sizeof(*ptr));
|
|
}
|
|
|
|
int32_t NSPV_gettxproof(struct NSPV_txproof *ptr,int32_t vout,uint256 txid,int32_t height)
|
|
{
|
|
int32_t flag = 0,len = 0; CTransaction _tx; uint256 hashBlock; CBlock block; CBlockIndex *pindex;
|
|
ptr->height = -1;
|
|
if ( (ptr->tx= NSPV_getrawtx(_tx,hashBlock,&ptr->txlen,txid)) != 0 )
|
|
{
|
|
ptr->txid = txid;
|
|
ptr->vout = vout;
|
|
ptr->hashblock = hashBlock;
|
|
if ( height == 0 )
|
|
ptr->height = hush_blockheight(hashBlock);
|
|
else
|
|
{
|
|
ptr->height = height;
|
|
if ( (pindex= hush_chainactive(height)) != 0 && hush_blockload(block,pindex) == 0 )
|
|
{
|
|
BOOST_FOREACH(const CTransaction&tx, block.vtx)
|
|
{
|
|
if ( tx.GetHash() == txid )
|
|
{
|
|
flag = 1;
|
|
break;
|
|
}
|
|
}
|
|
if ( flag != 0 )
|
|
{
|
|
set<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();
|
|
//fprintf(stderr,"%s txproof.(%s)\n",txid.GetHex().c_str(),HexStr(proof).c_str());
|
|
if ( ptr->txprooflen > 0 )
|
|
{
|
|
ptr->txproof = (uint8_t *)calloc(1,ptr->txprooflen);
|
|
memcpy(ptr->txproof,&proof[0],ptr->txprooflen);
|
|
}
|
|
//fprintf(stderr,"gettxproof slen.%d\n",(int32_t)(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen));
|
|
}
|
|
}
|
|
}
|
|
ptr->unspentvalue = CCgettxout(txid,vout,1,1);
|
|
}
|
|
return(sizeof(*ptr) - sizeof(ptr->tx) - sizeof(ptr->txproof) + ptr->txlen + ptr->txprooflen);
|
|
}
|
|
|
|
int32_t NSPV_getntzsproofresp(struct NSPV_ntzsproofresp *ptr,uint256 prevntztxid,uint256 nextntztxid)
|
|
{
|
|
int32_t i; uint256 hashBlock,bhash0,bhash1,desttxid0,desttxid1; CTransaction tx;
|
|
ptr->prevtxid = prevntztxid;
|
|
ptr->prevntz = NSPV_getrawtx(tx,hashBlock,&ptr->prevtxlen,ptr->prevtxid);
|
|
ptr->prevtxidht = hush_blockheight(hashBlock);
|
|
if ( NSPV_notarizationextract(0,&ptr->common.prevht,&bhash0,&desttxid0,tx) < 0 )
|
|
return(-2);
|
|
else if ( hush_blockheight(bhash0) != ptr->common.prevht )
|
|
return(-3);
|
|
|
|
ptr->nexttxid = nextntztxid;
|
|
ptr->nextntz = NSPV_getrawtx(tx,hashBlock,&ptr->nexttxlen,ptr->nexttxid);
|
|
ptr->nexttxidht = hush_blockheight(hashBlock);
|
|
if ( NSPV_notarizationextract(0,&ptr->common.nextht,&bhash1,&desttxid1,tx) < 0 )
|
|
return(-5);
|
|
else if ( hush_blockheight(bhash1) != ptr->common.nextht )
|
|
return(-6);
|
|
|
|
else if ( ptr->common.prevht > ptr->common.nextht || (ptr->common.nextht - ptr->common.prevht) > 1440 )
|
|
{
|
|
fprintf(stderr,"illegal prevht.%d nextht.%d\n",ptr->common.prevht,ptr->common.nextht);
|
|
return(-7);
|
|
}
|
|
//fprintf(stderr,"%s -> prevht.%d, %s -> nexht.%d\n",ptr->prevtxid.GetHex().c_str(),ptr->common.prevht,ptr->nexttxid.GetHex().c_str(),ptr->common.nextht);
|
|
ptr->common.numhdrs = (ptr->common.nextht - ptr->common.prevht + 1);
|
|
ptr->common.hdrs = (struct NSPV_equihdr *)calloc(ptr->common.numhdrs,sizeof(*ptr->common.hdrs));
|
|
//fprintf(stderr,"prev.%d next.%d allocate numhdrs.%d\n",prevht,nextht,ptr->common.numhdrs);
|
|
for (i=0; i<ptr->common.numhdrs; i++)
|
|
{
|
|
//hashBlock = NSPV_hdrhash(&ptr->common.hdrs[i]);
|
|
//fprintf(stderr,"hdr[%d] %s\n",prevht+i,hashBlock.GetHex().c_str());
|
|
if ( NSPV_setequihdr(&ptr->common.hdrs[i],ptr->common.prevht+i) < 0 )
|
|
{
|
|
fprintf(stderr,"error setting hdr.%d\n",ptr->common.prevht+i);
|
|
free(ptr->common.hdrs);
|
|
ptr->common.hdrs = 0;
|
|
return(-1);
|
|
}
|
|
}
|
|
//fprintf(stderr,"sizeof ptr %ld, common.%ld lens.%d %d\n",sizeof(*ptr),sizeof(ptr->common),ptr->prevtxlen,ptr->nexttxlen);
|
|
return(sizeof(*ptr) + sizeof(*ptr->common.hdrs)*ptr->common.numhdrs - sizeof(ptr->common.hdrs) - sizeof(ptr->prevntz) - sizeof(ptr->nextntz) + ptr->prevtxlen + ptr->nexttxlen);
|
|
}
|
|
|
|
int32_t NSPV_getspentinfo(struct NSPV_spentinfo *ptr,uint256 txid,int32_t vout)
|
|
{
|
|
int32_t len = 0;
|
|
ptr->txid = txid;
|
|
ptr->vout = vout;
|
|
ptr->spentvini = -1;
|
|
len = (int32_t)(sizeof(*ptr) - sizeof(ptr->spent.tx) - sizeof(ptr->spent.txproof));
|
|
if ( CCgetspenttxid(ptr->spent.txid,ptr->spentvini,ptr->spent.height,txid,vout) == 0 )
|
|
{
|
|
if ( NSPV_gettxproof(&ptr->spent,0,ptr->spent.txid,ptr->spent.height) > 0 )
|
|
len += ptr->spent.txlen + ptr->spent.txprooflen;
|
|
else
|
|
{
|
|
NSPV_txproof_purge(&ptr->spent);
|
|
return(-1);
|
|
}
|
|
}
|
|
return(len);
|
|
}
|
|
|
|
void komodo_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a request
|
|
{
|
|
int32_t len,slen,ind,reqheight,n; 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;
|
|
if ( len == 1+sizeof(reqheight) )
|
|
dragon_rwnum(0,&request[1],sizeof(reqheight),&reqheight);
|
|
else reqheight = 0;
|
|
//fprintf(stderr,"request height.%d\n",reqheight);
|
|
memset(&I,0,sizeof(I));
|
|
if ( (slen= NSPV_getinfo(&I,reqheight)) > 0 )
|
|
{
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_INFORESP;
|
|
//fprintf(stderr,"slen.%d version.%d\n",slen,I.version);
|
|
if ( NSPV_rwinforesp(1,&response[1],&I) == slen )
|
|
{
|
|
//fprintf(stderr,"send info resp to id %d\n",(int32_t)pfrom->id);
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_inforesp_purge(&I);
|
|
}
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_UTXOS )
|
|
{
|
|
//fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len);
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_utxosresp U;
|
|
if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
|
{
|
|
int32_t skipcount = 0; char coinaddr[64]; uint8_t filter; uint8_t isCC = 0;
|
|
memcpy(coinaddr,&request[2],request[1]);
|
|
coinaddr[request[1]] = 0;
|
|
if ( request[1] == len-3 )
|
|
isCC = (request[len-1] != 0);
|
|
else if ( request[1] == len-7 )
|
|
{
|
|
isCC = (request[len-5] != 0);
|
|
dragon_rwnum(0,&request[len-4],sizeof(skipcount),&skipcount);
|
|
}
|
|
else
|
|
{
|
|
isCC = (request[len-9] != 0);
|
|
dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount);
|
|
dragon_rwnum(0,&request[len-4],sizeof(filter),&filter);
|
|
}
|
|
if ( 0 && isCC != 0 )
|
|
fprintf(stderr,"utxos %s isCC.%d skipcount.%d filter.%x\n",coinaddr,isCC,skipcount,filter);
|
|
memset(&U,0,sizeof(U));
|
|
if ( (slen= NSPV_getaddressutxos(&U,coinaddr,isCC,skipcount,filter)) > 0 )
|
|
{
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_UTXOSRESP;
|
|
if ( NSPV_rwutxosresp(1,&response[1],&U) == slen )
|
|
{
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_utxosresp_purge(&U);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_TXIDS )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_txidsresp T;
|
|
if ( len < 64+5 && (request[1] == len-3 || request[1] == len-7 || request[1] == len-11) )
|
|
{
|
|
int32_t skipcount = 0; char coinaddr[64]; uint32_t filter; uint8_t isCC = 0;
|
|
memcpy(coinaddr,&request[2],request[1]);
|
|
coinaddr[request[1]] = 0;
|
|
if ( request[1] == len-3 )
|
|
isCC = (request[len-1] != 0);
|
|
else if ( request[1] == len-7 )
|
|
{
|
|
isCC = (request[len-5] != 0);
|
|
dragon_rwnum(0,&request[len-4],sizeof(skipcount),&skipcount);
|
|
}
|
|
else
|
|
{
|
|
isCC = (request[len-9] != 0);
|
|
dragon_rwnum(0,&request[len-8],sizeof(skipcount),&skipcount);
|
|
dragon_rwnum(0,&request[len-4],sizeof(filter),&filter);
|
|
}
|
|
if ( 0 && isCC != 0 )
|
|
fprintf(stderr,"txids %s isCC.%d skipcount.%d filter.%d\n",coinaddr,isCC,skipcount,filter);
|
|
memset(&T,0,sizeof(T));
|
|
if ( (slen= NSPV_getaddresstxids(&T,coinaddr,isCC,skipcount,filter)) > 0 )
|
|
{
|
|
//fprintf(stderr,"slen.%d\n",slen);
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_TXIDSRESP;
|
|
if ( NSPV_rwtxidsresp(1,&response[1],&T) == slen )
|
|
{
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_txidsresp_purge(&T);
|
|
}
|
|
} else fprintf(stderr,"len.%d req1.%d\n",len,request[1]);
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_MEMPOOL )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_mempoolresp M; char coinaddr[64];
|
|
if ( len < sizeof(M)+64 )
|
|
{
|
|
int32_t vout; uint256 txid; uint8_t funcid,isCC = 0;
|
|
n = 1;
|
|
n += dragon_rwnum(0,&request[n],sizeof(isCC),&isCC);
|
|
n += dragon_rwnum(0,&request[n],sizeof(funcid),&funcid);
|
|
n += dragon_rwnum(0,&request[n],sizeof(vout),&vout);
|
|
n += dragon_rwbignum(0,&request[n],sizeof(txid),(uint8_t *)&txid);
|
|
slen = request[n++];
|
|
if ( slen < 63 )
|
|
{
|
|
memcpy(coinaddr,&request[n],slen), n += slen;
|
|
coinaddr[slen] = 0;
|
|
if ( isCC != 0 )
|
|
fprintf(stderr,"(%s) isCC.%d funcid.%d %s/v%d len.%d slen.%d\n",coinaddr,isCC,funcid,txid.GetHex().c_str(),vout,len,slen);
|
|
memset(&M,0,sizeof(M));
|
|
if ( (slen= NSPV_mempooltxids(&M,coinaddr,isCC,funcid,txid,vout)) > 0 )
|
|
{
|
|
//fprintf(stderr,"NSPV_mempooltxids slen.%d\n",slen);
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_MEMPOOLRESP;
|
|
if ( NSPV_rwmempoolresp(1,&response[1],&M) == slen )
|
|
{
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_mempoolresp_purge(&M);
|
|
}
|
|
}
|
|
} else fprintf(stderr,"len.%d req1.%d\n",len,request[1]);
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_NTZS )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_ntzsresp N; int32_t height;
|
|
if ( len == 1+sizeof(height) )
|
|
{
|
|
dragon_rwnum(0,&request[1],sizeof(height),&height);
|
|
memset(&N,0,sizeof(N));
|
|
if ( (slen= NSPV_getntzsresp(&N,height)) > 0 )
|
|
{
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_NTZSRESP;
|
|
if ( NSPV_rwntzsresp(1,&response[1],&N) == slen )
|
|
{
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_ntzsresp_purge(&N);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_NTZSPROOF )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_ntzsproofresp P; uint256 prevntz,nextntz;
|
|
if ( len == 1+sizeof(prevntz)+sizeof(nextntz) )
|
|
{
|
|
dragon_rwbignum(0,&request[1],sizeof(prevntz),(uint8_t *)&prevntz);
|
|
dragon_rwbignum(0,&request[1+sizeof(prevntz)],sizeof(nextntz),(uint8_t *)&nextntz);
|
|
memset(&P,0,sizeof(P));
|
|
if ( (slen= NSPV_getntzsproofresp(&P,prevntz,nextntz)) > 0 )
|
|
{
|
|
// fprintf(stderr,"slen.%d msg prev.%s next.%s\n",slen,prevntz.GetHex().c_str(),nextntz.GetHex().c_str());
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_NTZSPROOFRESP;
|
|
if ( NSPV_rwntzsproofresp(1,&response[1],&P) == slen )
|
|
{
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_ntzsproofresp_purge(&P);
|
|
} else fprintf(stderr,"err.%d\n",slen);
|
|
}
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_TXPROOF )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_txproof P; uint256 txid; int32_t height,vout;
|
|
if ( len == 1+sizeof(txid)+sizeof(height)+sizeof(vout) )
|
|
{
|
|
dragon_rwnum(0,&request[1],sizeof(height),&height);
|
|
dragon_rwnum(0,&request[1+sizeof(height)],sizeof(vout),&vout);
|
|
dragon_rwbignum(0,&request[1+sizeof(height)+sizeof(vout)],sizeof(txid),(uint8_t *)&txid);
|
|
//fprintf(stderr,"got txid %s/v%d ht.%d\n",txid.GetHex().c_str(),vout,height);
|
|
memset(&P,0,sizeof(P));
|
|
if ( (slen= NSPV_gettxproof(&P,vout,txid,height)) > 0 )
|
|
{
|
|
//fprintf(stderr,"slen.%d\n",slen);
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_TXPROOFRESP;
|
|
if ( NSPV_rwtxproof(1,&response[1],&P) == slen )
|
|
{
|
|
//fprintf(stderr,"send response\n");
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_txproof_purge(&P);
|
|
} else fprintf(stderr,"gettxproof error.%d\n",slen);
|
|
} else fprintf(stderr,"txproof reqlen.%d\n",len);
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_SPENTINFO )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_spentinfo S; int32_t vout; uint256 txid;
|
|
if ( len == 1+sizeof(txid)+sizeof(vout) )
|
|
{
|
|
dragon_rwnum(0,&request[1],sizeof(vout),&vout);
|
|
dragon_rwbignum(0,&request[1+sizeof(vout)],sizeof(txid),(uint8_t *)&txid);
|
|
memset(&S,0,sizeof(S));
|
|
if ( (slen= NSPV_getspentinfo(&S,txid,vout)) > 0 )
|
|
{
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_SPENTINFORESP;
|
|
if ( NSPV_rwspentinfo(1,&response[1],&S) == slen )
|
|
{
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_spentinfo_purge(&S);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_BROADCAST )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_broadcastresp B; uint32_t n,offset; uint256 txid;
|
|
if ( len > 1+sizeof(txid)+sizeof(n) )
|
|
{
|
|
dragon_rwbignum(0,&request[1],sizeof(txid),(uint8_t *)&txid);
|
|
dragon_rwnum(0,&request[1+sizeof(txid)],sizeof(n),&n);
|
|
memset(&B,0,sizeof(B));
|
|
offset = 1 + sizeof(txid) + sizeof(n);
|
|
if ( n < MAX_TX_SIZE_AFTER_SAPLING && request.size() == offset+n && (slen= NSPV_sendrawtransaction(&B,&request[offset],n)) > 0 )
|
|
{
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_BROADCASTRESP;
|
|
if ( NSPV_rwbroadcastresp(1,&response[1],&B) == slen )
|
|
{
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
}
|
|
NSPV_broadcast_purge(&B);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( request[0] == NSPV_REMOTERPC )
|
|
{
|
|
if ( timestamp > pfrom->prevtimes[ind] )
|
|
{
|
|
struct NSPV_remoterpcresp R; int32_t p;
|
|
p = 1;
|
|
p+=dragon_rwnum(0,&request[p],sizeof(slen),&slen);
|
|
memset(&R,0,sizeof(R));
|
|
if (request.size() == p+slen && (slen=NSPV_remoterpc(&R,(char *)&request[p],slen))>0 )
|
|
{
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_REMOTERPCRESP;
|
|
NSPV_rwremoterpcresp(1,&response[1],&R,slen);
|
|
pfrom->PushMessage("nSPV",response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
NSPV_remoterpc_purge(&R);
|
|
}
|
|
}
|
|
}
|
|
else if (request[0] == NSPV_CCMODULEUTXOS) // get cc module utxos from coinaddr for the requested amount, evalcode, funcid list and txid
|
|
{
|
|
//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];
|
|
int64_t amount;
|
|
uint8_t evalcode;
|
|
char funcids[27];
|
|
uint256 filtertxid;
|
|
bool errorFormat = false;
|
|
const int32_t BITCOINADDRESSMINLEN = 20;
|
|
|
|
int32_t minreqlen = sizeof(uint8_t) + sizeof(uint8_t) + BITCOINADDRESSMINLEN + sizeof(amount) + sizeof(evalcode) + sizeof(uint8_t) + sizeof(filtertxid);
|
|
int32_t maxreqlen = sizeof(uint8_t) + sizeof(uint8_t) + sizeof(coinaddr)-1 + sizeof(amount) + sizeof(evalcode) + sizeof(uint8_t) + sizeof(funcids)-1 + sizeof(filtertxid);
|
|
|
|
if (len >= minreqlen && len <= maxreqlen)
|
|
{
|
|
n = 1;
|
|
int32_t addrlen = request[n++];
|
|
if (addrlen < sizeof(coinaddr))
|
|
{
|
|
memcpy(coinaddr, &request[n], addrlen);
|
|
coinaddr[addrlen] = 0;
|
|
n += addrlen;
|
|
dragon_rwnum(0, &request[n], sizeof(amount), &amount);
|
|
n += sizeof(amount);
|
|
dragon_rwnum(0, &request[n], sizeof(evalcode), &evalcode);
|
|
n += sizeof(evalcode);
|
|
|
|
int32_t funcidslen = request[n++];
|
|
if (funcidslen < sizeof(funcids))
|
|
{
|
|
memcpy(funcids, &request[n], funcidslen);
|
|
funcids[funcidslen] = 0;
|
|
n += funcidslen;
|
|
dragon_rwbignum(0, &request[n], sizeof(filtertxid), (uint8_t *)&filtertxid);
|
|
std::cerr << __func__ << " " << "request addr=" << coinaddr << " amount=" << amount << " evalcode=" << (int)evalcode << " funcids=" << funcids << " filtertxid=" << filtertxid.GetHex() << std::endl;
|
|
|
|
memset(&U, 0, sizeof(U));
|
|
if ((slen = NSPV_getccmoduleutxos(&U, coinaddr, amount, evalcode, funcids, filtertxid)) > 0)
|
|
{
|
|
std::cerr << __func__ << " " << "created utxos, slen=" << slen << std::endl;
|
|
response.resize(1 + slen);
|
|
response[0] = NSPV_CCMODULEUTXOSRESP;
|
|
if (NSPV_rwutxosresp(1, &response[1], &U) == slen)
|
|
{
|
|
pfrom->PushMessage("nSPV", response);
|
|
pfrom->prevtimes[ind] = timestamp;
|
|
std::cerr << __func__ << " " << "returned nSPV response" << std::endl;
|
|
}
|
|
NSPV_utxosresp_purge(&U);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // HUSH_NSPVFULLNODE_H
|
|
|