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.
 
 
 
 
 
 

1032 lines
44 KiB

// Copyright (c) 2016-2024 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 *)"HUSH3" : 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->GetBlockHeader().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 =
{
};
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));
return(len);
}
void hush_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);
}
}
}
}
}
#endif // HUSH_NSPVFULLNODE_H