I am adding a higher level layer to the iguanacore and needed to efficiently find all tx with a spend to a specific address. I was pleased at how simple the entire scan was:
You will notice there are no database calls and only two internal calls needed: pkhashfind and uheight. internally I index based on the rmd160 (pkhash) so given a pkhash it is a hashtable lookup in a bundle to find the tail of the linked list. uheight is just a simple converter from the unspentind coordinate space to block height:
All the data structures are either read-only memory mapped files or in RAM data that is directly accessible using C structures. Each bundle of 2000 blocks are separately indexed, which does means potentially doing 200+ iterations, but in practice a lot of times you just scan backwards till you find what you need and being totally parallel if performance becomes an issue, it can be multithreaded.
In any case, if an address doesnt appear in the bundle, the initial pkhashfind will return null and that is all that is needed to be done for that bundle. Also, the linked list nature means that the number of iterations is directly proportional to the total number of transactions
Code:
int32_t iguana_datachain_scan(struct supernet_info *myinfo,struct iguana_info *coin,uint8_t rmd160[20])
{
int64_t deposits,crypto777_payment; uint32_t lastunspentind,unspentind; int32_t i,j,num,uheight; struct iguana_bundle *bp; struct iguana_ramchain *ramchain; struct iguana_ramchaindata *rdata; struct iguana_pkhash P; struct iguana_unspent *U,*u; struct iguana_txid *T,*tx;
for (i=num=0; i<coin->bundlescount; i++)
{
if ( (bp= coin->bundles[i]) != 0 )
{
ramchain = 0;
if ( iguana_pkhashfind(coin,&ramchain,&deposits,&lastunspentind,&P,rmd160,i,i) != 0 )
{
if ( ramchain != 0 && (rdata= ramchain->H.data) != 0 )
{
unspentind = lastunspentind;
U = RAMCHAIN_PTR(rdata,Uoffset);
T = RAMCHAIN_PTR(rdata,Toffset);
while ( unspentind > 0 )
{
tx = &T[U[unspentind].txidind];
for (crypto777_payment=j=0; j<tx->numvouts; j++)
{
u = &U[tx->firstvout + j];
uheight = iguana_uheight(coin,ramchain->height,T,rdata->numtxids,u);
crypto777_payment = datachain_update(myinfo,0,coin,bp,rmd160,crypto777_payment,u->type,uheight,(((uint64_t)bp->hdrsi << 32) | unspentind),u->value,u->fileid,u->scriptpos,u->scriptlen);
}
num++;
unspentind = U[unspentind].prevunspentind;
}
}
}
}
}
return(num);
}
Code:
int32_t iguana_uheight(struct iguana_info *coin,int32_t bundleheight,struct iguana_txid *T,int32_t numtxids,struct iguana_unspent *up)
{
if ( up->txidind > 0 && up->txidind < numtxids )
return(bundleheight + T[up->txidind].bundlei);
else return(bundleheight);
}
In any case, if an address doesnt appear in the bundle, the initial pkhashfind will return null and that is all that is needed to be done for that bundle. Also, the linked list nature means that the number of iterations is directly proportional to the total number of transactions