@@ -206,6 +206,229 @@ int32_t NSPV_getaddressutxos(struct NSPV_utxosresp *ptr,char *coinaddr,bool isCC
206
206
return (0 );
207
207
}
208
208
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
+
209
432
int32_t NSPV_getaddresstxids (struct NSPV_txidsresp *ptr,char *coinaddr,bool isCC,int32_t skipcount,uint32_t filter)
210
433
{
211
434
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
860
1083
}
861
1084
}
862
1085
}
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
+ }
864
1147
}
865
1148
866
1149
#endif // KOMODO_NSPVFULLNODE_H
0 commit comments