Skip to content

Commit 1eb3bdd

Browse files
dimxyMixa84
authored andcommitted
added nspv_ccmoduleunspents rpc (#32)
* added NSPV_getccmoduleutxos funcs * fixed req parse len * typo * fixed parse txid * fixed serialized calc * nodeheight fixed * not check txid for creation funcid * logging * changed to myGetTransaction * fixed work with array * calloc fixed * fixed create txid check * reviunted txid * fixed len for amount 0 * fixed resp fields for 0 * added listccmoduleunspent rpc * added rpc impl * added comment and help to rpc * help fixed
1 parent bd9daad commit 1eb3bdd

File tree

7 files changed

+365
-1
lines changed

7 files changed

+365
-1
lines changed

src/cc/CCinclude.h

+2
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ int64_t NSPV_AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total
297297
int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs);
298298
int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinputs);
299299
int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCflag);
300+
int32_t CC_vinselect(int32_t *aboveip, int64_t *abovep, int32_t *belowip, int64_t *belowp, struct CC_utxo utxos[], int32_t numunspents, int64_t value);
301+
300302
bool NSPV_SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey,uint32_t nTime);
301303

302304
// curve25519 and sha256

src/komodo_nSPV_defs.h

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
#define NSPV_TXIDSRESP 0x0f
4545
#define NSPV_MEMPOOL 0x10
4646
#define NSPV_MEMPOOLRESP 0x11
47+
#define NSPV_CCMODULEUTXOS 0x12
48+
#define NSPV_CCMODULEUTXOSRESP 0x13
4749
#define NSPV_MEMPOOL_ALL 0
4850
#define NSPV_MEMPOOL_ADDRESS 1
4951
#define NSPV_MEMPOOL_ISSPENT 2

src/komodo_nSPV_fullnode.h

+284-1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,229 @@ int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr,bool isCC
206206
return(0);
207207
}
208208

209+
class BaseCCChecker {
210+
public:
211+
/// base check function
212+
/// @param vouts vouts where checked vout and opret are
213+
/// @param nvout vout index to check
214+
/// @param evalcode which must be in the opret
215+
/// @param funcids allowed funcids in string
216+
/// @param filtertxid txid that should be in the opret (after funcid)
217+
virtual bool checkCC(uint256 txid, const std::vector<CTxOut> &vouts, int32_t nvout, uint8_t evalcode, std::string funcids, uint256 filtertxid) = 0;
218+
};
219+
220+
/// default cc vout checker for use in NSPV_getccmoduleutxos
221+
/// checks if a vout is cc, has required evalcode, allowed funcids and txid
222+
/// check both cc opret and last vout opret
223+
/// maybe customized in via inheritance
224+
class DefaultCCChecker : public BaseCCChecker {
225+
226+
private:
227+
228+
public:
229+
DefaultCCChecker() { }
230+
virtual bool checkCC(uint256 txid, const std::vector<CTxOut> &vouts, int32_t nvout, uint8_t evalcode, std::string funcids, uint256 filtertxid)
231+
{
232+
CScript opret, dummy;
233+
std::vector< vscript_t > vParams;
234+
vscript_t vopret;
235+
236+
if (nvout < vouts.size())
237+
{
238+
// first check if it is cc vout
239+
if (vouts[nvout].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams))
240+
{
241+
// try to find cc opret
242+
if (vParams.size() > 0)
243+
{
244+
COptCCParams p(vParams[0]); // parse vout data
245+
if (p.vData.size() > 0)
246+
{
247+
vopret = p.vData[0]; // get opret data
248+
}
249+
}
250+
// if no cc opret check last vout opret
251+
if (vopret.size() == 0)
252+
{
253+
GetOpReturnData(vouts.back().scriptPubKey, vopret);
254+
}
255+
if (vopret.size() > 2)
256+
{
257+
uint8_t opretEvalcode, opretFuncid;
258+
uint256 opretTxid;
259+
bool isEof = true;
260+
bool isCreateTx = false;
261+
262+
// parse opret first 3 fields:
263+
bool parseOk = E_UNMARSHAL(vopret,
264+
ss >> opretEvalcode;
265+
ss >> opretFuncid;
266+
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)
267+
{
268+
isCreateTx = true;
269+
}
270+
else
271+
{
272+
ss >> opretTxid;
273+
isCreateTx = false;
274+
}
275+
isEof = ss.eof(); );
276+
277+
opretTxid = revuint256(opretTxid);
278+
std::cerr << __func__ << " " << "opretEvalcode=" << opretEvalcode << " opretFuncid=" << (char)opretFuncid << " isCreateTx=" << isCreateTx << " opretTxid=" << opretTxid.GetHex() << std::endl;
279+
if( parseOk /*parseOk=true if eof reached*/|| !isEof /*more data means okay*/)
280+
{
281+
if (evalcode == opretEvalcode && std::find(funcids.begin(), funcids.end(), (char)opretFuncid) != funcids.end() &&
282+
(isCreateTx && filtertxid == txid || !isCreateTx && filtertxid == opretTxid))
283+
{
284+
return true;
285+
}
286+
}
287+
}
288+
}
289+
}
290+
return false;
291+
}
292+
};
293+
294+
static class DefaultCCChecker defaultCCChecker;
295+
296+
// table of pluggable cc vout checkers for usage in NSPV_getccmoduleutxos
297+
// if the checker is not in the table for a evalcode then defaultCCChecker is used
298+
static std::map<uint8_t, class BaseCCChecker*> ccCheckerTable =
299+
{
300+
};
301+
302+
// implements SPV server's part, gets cc module utxos, filtered by evalcode, funcid and txid on opret, for the specified amount
303+
// if the amount param is 0 returns total available filtere utxo amount and returns no utxos
304+
// first char funcid in the string param is considered as the creation tx funcid so filtertxid is compared to the creation txid itself
305+
// for other funcids filtertxid is compared to the txid in opreturn
306+
int32_t NSPV_getccmoduleutxos(struct NSPV_utxosresp *ptr, char *coinaddr, int64_t amount, uint8_t evalcode, std::string funcids, uint256 filtertxid)
307+
{
308+
int64_t total = 0, totaladded = 0;
309+
uint32_t locktime;
310+
int32_t tipheight=0, len, maxlen;
311+
int32_t maxinputs = CC_MAXVINS;
312+
313+
std::vector<struct CC_utxo> utxoSelected;
314+
utxoSelected.reserve(CC_MAXVINS);
315+
316+
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
317+
SetCCunspents(unspentOutputs, coinaddr, true);
318+
319+
maxlen = MAX_BLOCK_SIZE(tipheight) - 512;
320+
//maxlen /= sizeof(*ptr->utxos); // TODO why was this? we need maxlen in bytes, don't we?
321+
322+
//ptr->numutxos = (uint16_t)unspentOutputs.size();
323+
//if (ptr->numutxos >= 0 && ptr->numutxos < maxlen)
324+
//{
325+
ptr->utxos = NULL;
326+
ptr->numutxos = 0;
327+
strncpy(ptr->coinaddr, coinaddr, sizeof(ptr->coinaddr) - 1);
328+
ptr->CCflag = 1;
329+
tipheight = chainActive.LastTip()->GetHeight();
330+
ptr->nodeheight = tipheight; // will be checked in libnspv
331+
//}
332+
333+
// select all appropriate utxos:
334+
std::cerr << __func__ << " " << "searching addr=" << coinaddr << std::endl;
335+
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
336+
{
337+
if (myIsutxo_spentinmempool(ignoretxid, ignorevin, it->first.txhash, (int32_t)it->first.index) == 0)
338+
{
339+
//const CCoins *pcoins = pcoinsTip->AccessCoins(it->first.txhash); <-- no opret in coins
340+
CTransaction tx;
341+
uint256 hashBlock;
342+
int32_t nvout = it->first.index;
343+
if (myGetTransaction(it->first.txhash, tx, hashBlock))
344+
{
345+
class BaseCCChecker *baseChecker = ccCheckerTable[evalcode];
346+
347+
// if a checker is set for evalcode use it otherwise use the default checker:
348+
if (baseChecker && baseChecker->checkCC(it->first.txhash, tx.vout, nvout, evalcode, funcids, filtertxid) || defaultCCChecker.checkCC(it->first.txhash, tx.vout, nvout, evalcode, funcids, filtertxid))
349+
{
350+
std::cerr << __func__ << " " << "filtered utxo with amount=" << tx.vout[nvout].nValue << std::endl;
351+
352+
struct CC_utxo utxo;
353+
utxo.txid = it->first.txhash;
354+
utxo.vout = (int32_t)it->first.index;
355+
utxo.nValue = it->second.satoshis;
356+
//utxo.height = it->second.blockHeight;
357+
utxoSelected.push_back(utxo);
358+
total += it->second.satoshis;
359+
}
360+
}
361+
else
362+
std::cerr << __func__ << " " << "ERROR: cant load tx for txid, please reindex" << std::endl;
363+
}
364+
}
365+
366+
367+
if (amount == 0) {
368+
// just return total value
369+
ptr->total = total;
370+
len = (int32_t)(sizeof(*ptr) - sizeof(ptr->utxos)/*subtract not serialized part of NSPV_utxoresp*/);
371+
return len;
372+
}
373+
374+
// pick optimal utxos for the requested amount
375+
CAmount remains = amount;
376+
std::vector<struct CC_utxo> utxoAdded;
377+
378+
while (utxoSelected.size() > 0)
379+
{
380+
int64_t below = 0, above = 0;
381+
int32_t abovei = -1, belowi = -1, ind = -1;
382+
383+
if (CC_vinselect(&abovei, &above, &belowi, &below, utxoSelected.data(), utxoSelected.size(), remains) < 0)
384+
{
385+
std::cerr << "error CC_vinselect" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl;
386+
return 0;
387+
}
388+
if (abovei >= 0) // best is 'above'
389+
ind = abovei;
390+
else if (belowi >= 0) // second try is 'below'
391+
ind = belowi;
392+
else
393+
{
394+
std::cerr << "error finding unspent" << " remains=" << remains << " amount=" << amount << " abovei=" << abovei << " belowi=" << belowi << " ind=" << " utxoSelected.size()=" << utxoSelected.size() << ind << std::endl;
395+
return 0;
396+
}
397+
398+
utxoAdded.push_back(utxoSelected[ind]);
399+
total += utxoSelected[ind].nValue;
400+
remains -= utxoSelected[ind].nValue;
401+
402+
// remove used utxo[ind]:
403+
utxoSelected[ind] = utxoSelected.back();
404+
utxoSelected.pop_back();
405+
406+
if (total >= amount) // found the requested amount
407+
break;
408+
if (utxoAdded.size() >= maxinputs) // reached maxinputs
409+
break;
410+
}
411+
ptr->numutxos = (uint16_t)utxoAdded.size();
412+
ptr->total = total;
413+
ptr->utxos = (NSPV_utxoresp*)calloc(ptr->numutxos, sizeof(ptr->utxos[0]));
414+
415+
for (uint16_t i = 0; i < ptr->numutxos; i++)
416+
{
417+
ptr->utxos[i].satoshis = utxoAdded[i].nValue;
418+
ptr->utxos[i].txid = utxoAdded[i].txid;
419+
ptr->utxos[i].vout = utxoAdded[i].vout;
420+
}
421+
422+
len = (int32_t)(sizeof(*ptr) - sizeof(ptr->utxos)/*subtract not serialized part of NSPV_utxoresp*/ + sizeof(*ptr->utxos)*ptr->numutxos);
423+
if (len < maxlen)
424+
return len; // good length
425+
else
426+
{
427+
NSPV_utxosresp_purge(ptr);
428+
return 0;
429+
}
430+
}
431+
209432
int32_t NSPV_getaddresstxids(struct NSPV_txidsresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter)
210433
{
211434
int32_t maxlen,txheight,ind=0,n = 0,len = 0; CTransaction tx; uint256 hashBlock;
@@ -860,7 +1083,67 @@ void komodo_nSPVreq(CNode *pfrom,std::vector<uint8_t> request) // received a req
8601083
}
8611084
}
8621085
}
863-
}
1086+
1087+
else if (request[0] == NSPV_CCMODULEUTXOS) // get cc module utxos from coinaddr for the requested amount, evalcode, funcid list and txid
1088+
{
1089+
//fprintf(stderr,"utxos: %u > %u, ind.%d, len.%d\n",timestamp,pfrom->prevtimes[ind],ind,len);
1090+
if (timestamp > pfrom->prevtimes[ind])
1091+
{
1092+
struct NSPV_utxosresp U;
1093+
char coinaddr[64];
1094+
int64_t amount;
1095+
uint8_t evalcode;
1096+
char funcids[27];
1097+
uint256 filtertxid;
1098+
bool errorFormat = false;
1099+
const int32_t BITCOINADDRESSMINLEN = 20;
1100+
1101+
int32_t minreqlen = sizeof(uint8_t) + sizeof(uint8_t) + BITCOINADDRESSMINLEN + sizeof(amount) + sizeof(evalcode) + sizeof(uint8_t) + sizeof(filtertxid);
1102+
int32_t maxreqlen = sizeof(uint8_t) + sizeof(uint8_t) + sizeof(coinaddr)-1 + sizeof(amount) + sizeof(evalcode) + sizeof(uint8_t) + sizeof(funcids)-1 + sizeof(filtertxid);
1103+
1104+
if (len >= minreqlen && len <= maxreqlen)
1105+
{
1106+
n = 1;
1107+
int32_t addrlen = request[n++];
1108+
if (addrlen < sizeof(coinaddr))
1109+
{
1110+
memcpy(coinaddr, &request[n], addrlen);
1111+
coinaddr[addrlen] = 0;
1112+
n += addrlen;
1113+
iguana_rwnum(0, &request[n], sizeof(amount), &amount);
1114+
n += sizeof(amount);
1115+
iguana_rwnum(0, &request[n], sizeof(evalcode), &evalcode);
1116+
n += sizeof(evalcode);
1117+
1118+
int32_t funcidslen = request[n++];
1119+
if (funcidslen < sizeof(funcids))
1120+
{
1121+
memcpy(funcids, &request[n], funcidslen);
1122+
funcids[funcidslen] = 0;
1123+
n += funcidslen;
1124+
iguana_rwbignum(0, &request[n], sizeof(filtertxid), (uint8_t *)&filtertxid);
1125+
std::cerr << __func__ << " " << "request addr=" << coinaddr << " amount=" << amount << " evalcode=" << (int)evalcode << " funcids=" << funcids << " filtertxid=" << filtertxid.GetHex() << std::endl;
1126+
1127+
memset(&U, 0, sizeof(U));
1128+
if ((slen = NSPV_getccmoduleutxos(&U, coinaddr, amount, evalcode, funcids, filtertxid)) > 0)
1129+
{
1130+
std::cerr << __func__ << " " << "created utxos, slen=" << slen << std::endl;
1131+
response.resize(1 + slen);
1132+
response[0] = NSPV_CCMODULEUTXOSRESP;
1133+
if (NSPV_rwutxosresp(1, &response[1], &U) == slen)
1134+
{
1135+
pfrom->PushMessage("nSPV", response);
1136+
pfrom->prevtimes[ind] = timestamp;
1137+
std::cerr << __func__ << " " << "returned nSPV response" << std::endl;
1138+
}
1139+
NSPV_utxosresp_purge(&U);
1140+
}
1141+
}
1142+
}
1143+
}
1144+
}
1145+
}
1146+
}
8641147
}
8651148

8661149
#endif // KOMODO_NSPVFULLNODE_H

src/komodo_nSPV_superlite.h

+53
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ void komodo_nSPVresp(CNode *pfrom,std::vector<uint8_t> response) // received a r
203203
NSPV_rwbroadcastresp(0,&response[1],&NSPV_broadcastresult);
204204
fprintf(stderr,"got broadcast response %u size.%d %s retcode.%d\n",timestamp,(int32_t)response.size(),NSPV_broadcastresult.txid.GetHex().c_str(),NSPV_broadcastresult.retcode);
205205
break;
206+
case NSPV_CCMODULEUTXOSRESP:
207+
NSPV_utxosresp_purge(&NSPV_utxosresult);
208+
NSPV_rwutxosresp(0, &response[1], &NSPV_utxosresult);
209+
fprintf(stderr, "got cc module utxos response %u size.%d\n", timestamp, (int32_t)response.size());
210+
break;
211+
206212
default: fprintf(stderr,"unexpected response %02x size.%d at %u\n",response[0],(int32_t)response.size(),timestamp);
207213
break;
208214
}
@@ -925,4 +931,51 @@ UniValue NSPV_broadcast(char *hex)
925931
return(NSPV_broadcast_json(&B,txid));
926932
}
927933

934+
// gets cc utxos filtered by evalcode, funcid and txid in opret, for the specified amount
935+
// if amount == 0 returns total and no utxos
936+
// funcids is string of funcid symbols like "ct". The first symbol is considered as creation tx funcid and filtertxid will be compared to the creation tx id itself.
937+
// For second+ funcids the filtertxid will be compared to txid in opret
938+
UniValue NSPV_ccmoduleutxos(char *coinaddr, int64_t amount, uint8_t evalcode, std::string funcids, uint256 filtertxid)
939+
{
940+
UniValue result(UniValue::VOBJ); uint8_t msg[512]; int32_t i, iter, slen, len = 0;
941+
uint8_t CCflag = 1;
942+
943+
NSPV_utxosresp_purge(&NSPV_utxosresult);
944+
if (bitcoin_base58decode(msg, coinaddr) != 25)
945+
{
946+
result.push_back(Pair("result", "error"));
947+
result.push_back(Pair("error", "invalid address"));
948+
return(result);
949+
}
950+
msg[len++] = NSPV_CCMODULEUTXOS;
951+
952+
slen = (int32_t)strlen(coinaddr);
953+
msg[len++] = slen;
954+
memcpy(&msg[len], coinaddr, slen), len += slen;
955+
956+
len += iguana_rwnum(1, &msg[len], sizeof(amount), &amount);
957+
len += iguana_rwnum(1, &msg[len], sizeof(evalcode), &evalcode);
958+
959+
slen = (int32_t)(funcids.size());
960+
msg[len++] = slen;
961+
memcpy(&msg[len], funcids.data(), slen), len += slen;
962+
963+
len += iguana_rwbignum(1, &msg[len], sizeof(filtertxid), (uint8_t *)&filtertxid);
964+
for (iter = 0; iter<3; iter++)
965+
if (NSPV_req(0, msg, len, NODE_ADDRINDEX, msg[0] >> 1) != 0)
966+
{
967+
for (i = 0; i<NSPV_POLLITERS; i++)
968+
{
969+
usleep(NSPV_POLLMICROS);
970+
if ((NSPV_inforesult.height == 0 || NSPV_utxosresult.nodeheight >= NSPV_inforesult.height) && strcmp(coinaddr, NSPV_utxosresult.coinaddr) == 0 && CCflag == NSPV_utxosresult.CCflag)
971+
return(NSPV_utxosresp_json(&NSPV_utxosresult));
972+
}
973+
}
974+
else sleep(1);
975+
result.push_back(Pair("result", "error"));
976+
result.push_back(Pair("error", "no utxos result"));
977+
result.push_back(Pair("lastpeer", NSPV_lastpeer));
978+
return(result);
979+
}
980+
928981
#endif // KOMODO_NSPVSUPERLITE_H

src/rpc/server.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ static const CRPCCommand vRPCCommands[] =
429429
{ "nSPV", "nspv_spend", &nspv_spend, true },
430430
{ "nSPV", "nspv_broadcast", &nspv_broadcast, true },
431431
{ "nSPV", "nspv_logout", &nspv_logout, true },
432+
{ "nSPV", "nspv_listccmoduleunspent", &nspv_listccmoduleunspent, true },
432433

433434
// rewards
434435
{ "rewards", "rewardslist", &rewardslist, true },

src/rpc/server.h

+1
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ extern UniValue nspv_txproof(const UniValue& params, bool fHelp);
477477
extern UniValue nspv_spend(const UniValue& params, bool fHelp);
478478
extern UniValue nspv_broadcast(const UniValue& params, bool fHelp);
479479
extern UniValue nspv_logout(const UniValue& params, bool fHelp);
480+
extern UniValue nspv_listccmoduleunspent(const UniValue& params, bool fHelp);
480481

481482
extern UniValue getblocksubsidy(const UniValue& params, bool fHelp);
482483

0 commit comments

Comments
 (0)