/******************************************************************************
 * Copyright © 2014-2017 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.            *
 *                                                                            *
 ******************************************************************************/

#include "../iguana/iguana777.h"
#include "../iguana/exchanges777.h"

typedef char *basilisk_servicefunc(struct supernet_info *myinfo,char *CMD,void *addr,char *remoteaddr,uint32_t basilisktag,cJSON *valsobj,uint8_t *data,int32_t datalen,bits256 hash,int32_t from_basilisk);

int32_t basilisk_notarycmd(char *cmd)
{
    //&& strcmp(cmd,"DEX") != 0 && strcmp(cmd,"ACC") != 0 && strcmp(cmd,"RID") != 0 &&
    if ( strcmp(cmd,"PIN") != 0 && strcmp(cmd,"OUT") != 0 && strcmp(cmd,"MSG") != 0 && strcmp(cmd,"VOT") != 0 )
        return(0);
    else return(1);
}

/*int32_t basilisk_specialrelay_CMD(char *CMD)
{
    if ( strcmp(CMD,"OUT") == 0 || strcmp(CMD,"MSG") == 0 || strcmp(CMD,"BLK") == 0 || strcmp(CMD,"MEM") == 0 || strcmp(CMD,"GTX") == 0 || strcmp(CMD,"RID") == 0 )
        return(1);
    else return(0);
}*/

cJSON *basilisk_utxosweep(struct supernet_info *myinfo,char *symbol,int64_t *satoshis,uint64_t limit,int32_t maxvins,char *coinaddr)
{
    int32_t i,n,numvins = 0; char *retstr; uint64_t value,biggest = 0; struct iguana_info *coin=0; cJSON *item,*biggestitem=0,*array,*utxos = 0;
    coin = iguana_coinfind(symbol);
    if ( (retstr= dex_listunspent(myinfo,coin,0,0,symbol,coinaddr)) != 0 )
    {
        //printf("(%s)\n",retstr);
        if ( (array= cJSON_Parse(retstr)) != 0 )
        {
            n = cJSON_GetArraySize(array);
            for (i=0; i<n; i++)
            {
                item = jitem(array,i);
                if ( (value= SATOSHIDEN*jdouble(item,"amount")) != 0 || (value= SATOSHIDEN*jdouble(item,"value")) != 0 )
                {
                    //fprintf(stderr,"%.8f ",dstr(value));
                    if ( value <= limit )
                    {
                        //fprintf(stderr,"< ");
                        if ( utxos == 0 )
                            utxos = cJSON_CreateArray();
                        if ( numvins < maxvins )
                        {
                            jaddi(utxos,jduplicate(item));
                            numvins++;
                        }
                    }
                    else if ( value > biggest )
                    {
                        //fprintf(stderr,"biggest! ");
                        if ( biggestitem != 0 )
                            free_json(biggestitem);
                        biggestitem = jduplicate(item);
                        *satoshis = biggest = value;
                    } //else fprintf(stderr,"> ");
                }
            }
            free_json(array);
            if ( utxos == 0 && biggestitem != 0 )
            {
                fprintf(stderr,"add biggest.(%s)\n",jprint(biggestitem,0));
                jaddi(utxos,biggestitem);
            }
        }
        free(retstr);
    }
    return(utxos);
}

uint32_t basilisk_calcnonce(struct supernet_info *myinfo,uint8_t *data,int32_t datalen,uint32_t nBits)
{
    int32_t i,numiters = 0; bits256 hash,hash2,threshold; uint32_t basilisktag;
    vcalc_sha256(0,hash.bytes,data,datalen);
    if ( nBits >= GECKO_EASIESTDIFF )
        threshold = bits256_from_compact(GECKO_EASIESTDIFF);
    else threshold = bits256_from_compact(nBits);
    for (i=0; i<numiters; i++)
    {
        //OS_randombytes((void *)hash.uints,sizeof(basilisktag));
        hash.uints[0] = rand();
        vcalc_sha256(0,hash2.bytes,hash.bytes,sizeof(hash));
        if ( bits256_cmp(threshold,hash2) > 0 )
            break;
    }
    iguana_rwnum(0,(void *)hash.uints,sizeof(basilisktag),&basilisktag);
    iguana_rwnum(1,&data[-(int32_t)sizeof(basilisktag)],sizeof(basilisktag),&basilisktag);
    //char str[65],str2[65]; printf("found hash after numiters.%d %s vs %s basilisktag.%u\n",numiters,bits256_str(str,threshold),bits256_str(str2,hash2),basilisktag);
    return(basilisktag);
}

char *basilisk_addhexstr(char **ptrp,cJSON *valsobj,char *strbuf,int32_t strsize,uint8_t *data,int32_t datalen)
{
    *ptrp = 0;
    if ( data != 0 && datalen > 0 )
    {
        if ( valsobj != 0 && jobj(valsobj,"data") != 0 )
        {
            printf("basilisk_addhexstr warning: already have data object\n");
            jdelete(valsobj,"data");
        }
        if ( (datalen<<1)+1 > strsize )
        {
            strbuf = calloc(1,(datalen << 1) + 1);
            *ptrp = (void *)strbuf;
        }
        init_hexbytes_noT(strbuf,data,datalen);
        if ( valsobj != 0 )
            jaddstr(valsobj,"data",strbuf);
    } else return(0);
    return(strbuf);
}

uint8_t *get_dataptr(int32_t hdroffset,uint8_t **ptrp,int32_t *datalenp,uint8_t *space,int32_t spacesize,char *hexstr)
{
    *ptrp = 0; uint8_t *data = 0;
    if ( hexstr != 0 && (*datalenp= is_hexstr(hexstr,0)) > 0 )
    {
        *datalenp >>= 1;
        if ( (*datalenp+hdroffset) <= spacesize )
        {
            memset(space,0,hdroffset);
            data = &space[hdroffset];
        } else *ptrp = data = calloc(1,*datalenp + hdroffset);
        decode_hex(&data[hdroffset],*datalenp,hexstr);
    }
    if ( data != 0 )
        return(&data[hdroffset]);
    else return(data);
}

uint8_t *basilisk_jsondata(int32_t extraoffset,uint8_t **ptrp,uint8_t *space,int32_t spacesize,int32_t *datalenp,char *symbol,cJSON *sendjson,uint32_t basilisktag)
{
    char *sendstr,*hexstr=0; uint8_t *data,hexspace[4096],*allocptr=0,*hexdata=0; int32_t datalen,hexlen=0;
    if ( jobj(sendjson,"symbol") == 0 )
        jaddstr(sendjson,"symbol",symbol);
    if ( (hexstr= jstr(sendjson,"data")) != 0 )
    {
        hexdata = get_dataptr(0,&allocptr,&hexlen,hexspace,sizeof(hexspace),hexstr);
        //printf("jsondata.%s from sendjson\n",hexstr);
        jdelete(sendjson,"data");
    }
    *ptrp = 0;
    sendstr = jprint(sendjson,0);
    datalen = (int32_t)strlen(sendstr) + 1;
    if ( (datalen + extraoffset + BASILISK_HDROFFSET + hexlen) <= spacesize )
        data = space;
    else
    {
        data = calloc(1,datalen + extraoffset + BASILISK_HDROFFSET + hexlen);
        *ptrp = data;
    }
    data += extraoffset + BASILISK_HDROFFSET;
    memcpy(data,sendstr,datalen);
    //printf("jsondata.(%s) + hexlen.%d\n",sendstr,hexlen);
    free(sendstr);
    if ( hexlen > 0 && hexdata != 0 )
    {
        //int32_t i; for (i=0; i<hexlen; i++)
        //    printf("%02x",hexdata[i]);
        //printf(" <- hexdata\n");
        memcpy(&data[datalen],hexdata,hexlen);
        datalen += hexlen;
    }
    *datalenp = datalen;
    if ( allocptr != 0 )
        free(allocptr);
    return(data);
}

struct basilisk_item *basilisk_itemcreate(struct supernet_info *myinfo,char *CMD,char *symbol,uint32_t basilisktag,int32_t numrequired,cJSON *vals,int32_t timeoutmillis,void *metricfunc)
{
    struct basilisk_item *ptr;
    ptr = calloc(1,sizeof(*ptr));
    ptr->basilisktag = basilisktag;
    if ( (ptr->numrequired= numrequired) == 0 )
        ptr->numrequired = 1;
    strcpy(ptr->CMD,CMD);
    safecopy(ptr->symbol,symbol,sizeof(ptr->symbol));
    ptr->duration = timeoutmillis;
    ptr->expiration = OS_milliseconds() + timeoutmillis;
    //printf("itemcreate.%p %s %f timeout.%d\n",ptr,CMD,OS_milliseconds(),timeoutmillis);
    return(ptr);
}

int32_t basilisk_sendcmd(struct supernet_info *myinfo,char *destipaddr,char *type,uint32_t *basilisktagp,int32_t encryptflag,int32_t delaymillis,uint8_t *data,int32_t datalen,int32_t fanout,uint32_t nBits) // data must be offset by sizeof(iguana_msghdr)+sizeof(basilisktag)
{
    int32_t i,l,s,valid,val,n=0,retval = -1; char cmd[12]; struct iguana_info *coin,*tmp; struct iguana_peer *addr; bits256 hash; uint32_t *alreadysent,r;
    if ( fanout <= 0 )
        fanout = sqrt(myinfo->NOTARY.NUMRELAYS) + 1;
    else if ( fanout > BASILISK_MAXFANOUT )
        fanout = BASILISK_MAXFANOUT;
    if ( type == 0 )
        type = "INF";
    if ( strlen(type) > 3 )
    {
        printf("basilisk_sendcmd illegal type(%s)\n",type);
        return(-1);
    }
    if ( destipaddr != 0 )
    {
        if ( destipaddr[0] == 0 )
        {
            destipaddr = 0; // broadcast
        }
        else if ( strcmp(destipaddr,"127.0.0.1") == 0 || strcmp(destipaddr,myinfo->ipaddr) == 0 )
        {
            printf("return after locally basilisk_msgprocess\n");
            hash = GENESIS_PUBKEY;
            basilisk_msgprocess(myinfo,0,0,type,*basilisktagp,data,datalen);
            return(0);
        }
    }
    iguana_rwnum(1,&data[-(int32_t)sizeof(*basilisktagp)],sizeof(*basilisktagp),basilisktagp);
    if ( *basilisktagp == 0 )
    {
        if ( nBits != 0 )
            *basilisktagp = basilisk_calcnonce(myinfo,data,datalen,nBits);
        else *basilisktagp = rand();
        iguana_rwnum(1,&data[-(int32_t)sizeof(*basilisktagp)],sizeof(*basilisktagp),basilisktagp);
    }
    data -= sizeof(*basilisktagp), datalen += sizeof(*basilisktagp);
    memset(cmd,0,sizeof(cmd));
    sprintf(cmd,"SuperNET%s",type);
    if ( destipaddr != 0 )
    {
        cmd[6] = 'E', cmd[7] = 'T';
        HASH_ITER(hh,myinfo->allcoins,coin,tmp)
        {
            if (  coin->peers == 0 )
                continue;
            if ( coin->FULLNODE == 0 && coin->VALIDATENODE == 0 )
                cmd[0] = 's';
            else cmd[0] = 'S';
            for (i=0; i<IGUANA_MAXPEERS; i++)
            {
                addr = &coin->peers->active[i];
                if ( addr->usock >= 0 && strcmp(addr->ipaddr,destipaddr) == 0 )
                {
                    return(iguana_queue_send(addr,delaymillis,&data[-(int32_t)sizeof(struct iguana_msghdr)],cmd,datalen));
                }
            }
        }
        return(-1);
    }
    if ( basilisk_notarycmd(type) != 0 && myinfo->NOTARY.NUMRELAYS == 0 )
    {
        //printf("no notary nodes to send (%s) to\n",type);
        return(-1);
    }
    //portable_mutex_lock(&myinfo->allcoins_mutex);
    //dex_reqsend(myinfo,&data[-(int32_t)sizeof(struct iguana_msghdr)],datalen);
    alreadysent = calloc(IGUANA_MAXPEERS * IGUANA_MAXCOINS,sizeof(*alreadysent));
    HASH_ITER(hh,myinfo->allcoins,coin,tmp)
    {
        if (  coin->peers == 0 )
            continue;
        if ( basilisk_notarycmd(type) != 0 && strcmp(coin->symbol,"RELAY") != 0 )
            continue;
        if ( coin->FULLNODE == 0 && coin->VALIDATENODE == 0 )
            cmd[0] = 's';
        else cmd[0] = 'S';
        r = rand() % IGUANA_MAXPEERS;
        for (l=0; l<IGUANA_MAXPEERS; l++)
        {
            i = (l + r) % IGUANA_MAXPEERS;
            addr = &coin->peers->active[i];
            if ( addr->supernet != 0 || addr->basilisk != 0 )
                valid = 1;
            else valid = 0;
            if ( addr->usock >= 0 )
            {
                s = 0;
                valid = (addr->supernet != 0);
                if ( basilisk_notarycmd(type) != 0 || (strcmp(type,"INF") == 0 && strcmp(coin->symbol,"RELAY") == 0) )
                {
                    valid = 0;
                    /*OS_randombytes((void *)&r2,sizeof(r2));
                    if ( (r2 % myinfo->NOTARY.NUMRELAYS) >= sqrt(myinfo->NOTARY.NUMRELAYS) )
                    {
                        printf("fanout.%d s.%d n.%d skip %s\n",fanout,s,n,addr->ipaddr);
                        continue;
                    }*/
                    for (s=0; s<myinfo->NOTARY.NUMRELAYS; s++)
                        if ( addr->ipbits != myinfo->myaddr.myipbits && myinfo->NOTARY.RELAYS[s].ipbits == addr->ipbits )
                            break;
                    if ( s == myinfo->NOTARY.NUMRELAYS )
                    {
                        //printf("skip non-relay.(%s)\n",addr->ipaddr);
                        continue;
                    }
                    valid = 1;
                    //printf("send to other relay.(%s)\n",addr->ipaddr);
                }
                for (s=0; s<n; s++)
                    if ( alreadysent[s] == addr->ipbits )
                    {
                        //printf("already sent to %s\n",addr->ipaddr);
                        continue;
                    }
                if ( s == n && valid == 1 && (destipaddr == 0 || strcmp(addr->ipaddr,destipaddr) == 0) )
                {
                    val = 0;
                    //fprintf(stderr,">>> (%s).%u ",addr->ipaddr,coin->chain->portp2p);
                    //printf("n.%d/fanout.%d i.%d l.%d [%s].tag%u send %s [%x] datalen.%d addr->supernet.%u basilisk.%u to (%s).%d destip.%s\n",n,fanout,i,l,cmd,*(uint32_t *)data,type,*(int32_t *)&data[datalen-4],datalen,addr->supernet,addr->basilisk,addr->ipaddr,addr->A.port,destipaddr!=0?destipaddr:"broadcast");
                    if ( encryptflag != 0 && bits256_nonz(addr->pubkey) != 0 )
                    {
                        void *ptr; uint8_t *cipher,space[8192]; int32_t cipherlen; bits256 privkey;
                        cmd[6] = 'e', cmd[7] = 't';
                        memset(privkey.bytes,0,sizeof(privkey));
                        if ( (cipher= SuperNET_ciphercalc(&ptr,&cipherlen,&privkey,&addr->pubkey,data,datalen,space,sizeof(space))) != 0 )
                        {
                            if ( (val= iguana_queue_send(addr,delaymillis,&cipher[-(int32_t)sizeof(struct iguana_msghdr)],cmd,cipherlen)) >= cipherlen )
                                alreadysent[n++] = (uint32_t)addr->ipbits;
                            if ( ptr != 0 )
                                free(ptr);
                        }
                    }
                    else
                    {
                        cmd[6] = 'E', cmd[7] = 'T';
                        if ( (val= iguana_queue_send(addr,delaymillis,&data[-(int32_t)sizeof(struct iguana_msghdr)],cmd,datalen)) >= datalen )
                        {
                            alreadysent[n++] = (uint32_t)addr->ipbits;
                            if ( n >= IGUANA_MAXPEERS*IGUANA_MAXCOINS )
                                break;
                        }
                    }
                    if ( destipaddr != 0 || (fanout > 0 && n >= fanout) )
                    {
                        free(alreadysent);
                        return(val);
                    }
                    else if ( val > retval )
                        retval = val;
                }
            }
        }
        if ( n >= IGUANA_MAXPEERS*IGUANA_MAXCOINS )
            break;
    }
    //portable_mutex_unlock(&myinfo->allcoins_mutex);
    free(alreadysent);
    return(n);
}

void basilisk_sendback(struct supernet_info *myinfo,char *origCMD,char *symbol,char *remoteaddr,uint32_t basilisktag,char *retstr)
{
    uint8_t *data,space[4096],*allocptr; struct iguana_info *virt; cJSON *valsobj; int32_t datalen,encryptflag=0,delaymillis=0;
    //printf("%s retstr.(%s) -> remote.(%s) basilisktag.%u\n",origCMD,retstr,remoteaddr,basilisktag);
    if ( retstr != 0 && remoteaddr != 0 && remoteaddr[0] != 0 && strcmp(remoteaddr,"127.0.0.1") != 0 )
    {
        if ( (valsobj= cJSON_Parse(retstr)) != 0 )
        {
            jaddstr(valsobj,"origcmd",origCMD);
            jaddstr(valsobj,"symbol",symbol);
            if ( myinfo->ipaddr[0] != 0 )
                jaddstr(valsobj,"relay",myinfo->ipaddr);
            jaddnum(valsobj,"timestamp",(uint32_t)time(NULL));
            if ( (virt= iguana_coinfind(symbol)) != 0 )
            {
                jaddnum(valsobj,"hwm",virt->blocks.hwmchain.height);
                if ( bits256_nonz(virt->blocks.hwmchain.RO.hash2) != 0 )
                    jaddbits256(valsobj,"chaintip",virt->blocks.hwmchain.RO.hash2);
            }
            data = basilisk_jsondata(sizeof(struct iguana_msghdr),&allocptr,space,sizeof(space),&datalen,symbol,valsobj,basilisktag);
            //printf("sendback.%d -> %s\n",datalen,remoteaddr);
            basilisk_sendcmd(myinfo,remoteaddr,"RET",&basilisktag,encryptflag,delaymillis,data,datalen,0,0);
            if ( allocptr != 0 )
                free(allocptr);
            free_json(valsobj);
        }
    }
}

struct basilisk_item *basilisk_issueremote(struct supernet_info *myinfo,struct iguana_peer *addr,int32_t *numsentp,char *CMD,char *symbol,int32_t blockflag,cJSON *valsobj,int32_t fanout,int32_t numrequired,uint32_t basilisktag,int32_t timeoutmillis,void *deprecated_dontuse,char *retstr,int32_t encryptflag,int32_t delaymillis,uint32_t nBits)
{
    struct basilisk_item *pending; uint8_t *allocptr,*data,space[4096]; int32_t datalen;
    pending = basilisk_itemcreate(myinfo,CMD,symbol,basilisktag,numrequired,valsobj,timeoutmillis,0);
    pending->nBits = nBits;
    *numsentp = 0;
    if ( retstr != 0 )
    {
        pending->retstr = retstr;
        pending->numresults = pending->numrequired;
    }
    else
    {
        valsobj = jduplicate(valsobj);
        data = basilisk_jsondata(sizeof(struct iguana_msghdr),&allocptr,space,sizeof(space),&datalen,symbol,valsobj,basilisktag);
        free_json(valsobj), valsobj = 0;
        *numsentp = pending->numsent = basilisk_sendcmd(myinfo,addr != 0 ? addr->ipaddr : 0,CMD,&pending->basilisktag,encryptflag,delaymillis,data,datalen,fanout,pending->nBits);
        if ( blockflag != 0 )
        {
            portable_mutex_lock(&myinfo->basilisk_mutex);
            HASH_ADD(hh,myinfo->basilisks.issued,basilisktag,sizeof(basilisktag),pending);
            portable_mutex_unlock(&myinfo->basilisk_mutex);
            if ( pending->expiration <= OS_milliseconds() )
                pending->expiration = OS_milliseconds() + pending->duration;
            strcpy(pending->symbol,"RELAY");
            strcpy(pending->CMD,CMD);
            //printf("block for %f\n",pending->expiration - OS_milliseconds());
            while ( OS_milliseconds() < pending->expiration )
            {
                portable_mutex_lock(&myinfo->basilisk_mutex);
                if ( pending->numresults >= pending->numrequired )
                {
                    portable_mutex_unlock(&myinfo->basilisk_mutex);
                    //printf("%p <<<<<<<<<<<<< numresults.%d vs numrequired.%d\n",pending,pending->numresults,pending->numrequired);
                    break;
                }
                portable_mutex_unlock(&myinfo->basilisk_mutex);
                usleep(10000);
            }
        }
        if ( allocptr != 0 )
            free(allocptr);
    }
    return(pending);
}

struct basilisk_item *basilisk_requestservice(struct supernet_info *myinfo,struct iguana_peer *addr,char *CMD,int32_t blockflag,cJSON *valsobj,bits256 hash,uint8_t *data,int32_t datalen,uint32_t nBits)
{
    int32_t minfanout,numrequired,timeoutmillis,numsent,delaymillis,encryptflag,fanout; struct basilisk_item *ptr; char buf[4096],*symbol,*str = 0; struct iguana_info *virt;
    //printf("request.(%s)\n",jprint(valsobj,0));
    basilisk_addhexstr(&str,valsobj,buf,sizeof(buf),data,datalen);
    if ( str != 0 )
        free(str);
    if ( bits256_nonz(hash) == 0 || (bits256_cmp(hash,GENESIS_PUBKEY) != 0 && bits256_nonz(hash) != 0) )
    {
        if ( jobj(valsobj,"hash") != 0 )
            jdelete(valsobj,"hash");
        jaddbits256(valsobj,"hash",hash);
    }
    if ( (timeoutmillis= jint(valsobj,"timeout")) == 0 )
        timeoutmillis = BASILISK_TIMEOUT;
    minfanout = sqrt(myinfo->NOTARY.NUMRELAYS)+1;
    if ( minfanout < 8 )
        minfanout = 8;
    if ( jobj(valsobj,"fanout") == 0 )
        fanout = minfanout;
    else fanout = jint(valsobj,"fanout");
    if ( fanout < minfanout )
        fanout = minfanout;
    if ( (numrequired= jint(valsobj,"numrequired")) <= 0 )
        numrequired = MIN(fanout/2,sqrt(myinfo->NOTARY.NUMRELAYS)+1);
    if ( (symbol= jstr(valsobj,"coin")) != 0 || (symbol= jstr(valsobj,"symbol")) != 0 )
    {
        if ( (virt= iguana_coinfind(symbol)) != 0 )
        {
            jaddstr(valsobj,"symbol",symbol);
            jaddnum(valsobj,"longest",virt->longestchain);
            jaddnum(valsobj,"hwm",virt->blocks.hwmchain.height);
        }
    }
    if ( symbol == 0 )
        symbol = "RELAY";
    encryptflag = jint(valsobj,"encrypt");
    delaymillis = jint(valsobj,"delay");
    ptr = basilisk_issueremote(myinfo,addr,&numsent,CMD,symbol,blockflag,valsobj,fanout,numrequired,0,timeoutmillis,0,0,encryptflag,delaymillis,nBits);
    return(ptr);
}

char *basilisk_standardservice(char *CMD,struct supernet_info *myinfo,void *_addr,bits256 hash,cJSON *valsobj,char *hexstr,int32_t blockflag) // client side
{
    uint32_t nBits = 0; uint8_t space[4096],*allocptr=0,*data = 0; struct basilisk_item *ptr; int32_t i,datalen = 0; cJSON *retjson; char *retstr=0;
    if ( myinfo->IAMNOTARY != 0 && myinfo->NOTARY.RELAYID >= 0 && (strcmp(CMD,"INF") != 0 && basilisk_notarycmd(CMD) == 0) )
        return(clonestr("{\"error\":\"unsupported special relay command\"}"));
    data = get_dataptr(BASILISK_HDROFFSET,&allocptr,&datalen,space,sizeof(space),hexstr);
//printf("request.(%s)\n",jprint(valsobj,0));
    ptr = basilisk_requestservice(myinfo,_addr,CMD,blockflag,valsobj,hash,data,datalen,nBits);
    if ( allocptr != 0 )
        free(allocptr);
    if ( ptr != 0 )
    {
        if ( (retstr= ptr->retstr) != 0 )
            ptr->retstr = 0;
        else
        {
            if ( ptr->numresults > 0 )
            {
                retjson = cJSON_CreateArray();
                for (i=0; i<ptr->numresults; i++)
                    jaddi(retjson,ptr->results[i]), ptr->results[i] = 0;
                //printf("numresults.%d (%p)\n",ptr->numresults,ptr);
            }
            else
            {
                retjson = cJSON_CreateObject();
                if ( ptr->numsent > 0 )
                {
                    //queue_enqueue("submitQ",&myinfo->basilisks.submitQ,&ptr->DL,0);
                    jaddstr(retjson,"result","error");
                    jaddnum(retjson,"packetsize",ptr->numsent);
                } else jaddstr(retjson,"error","didnt find any nodes to send to");
            }
            retstr = jprint(retjson,1);
        }
        ptr->finished = OS_milliseconds() + 10000;
    }
    if ( (0) && strcmp("MSG",CMD) == 0 )
        printf("%s.(%s) -> (%s)\n",CMD,jprint(valsobj,0),retstr!=0?retstr:"");
    return(retstr);
}

int32_t basilisk_relayid(struct supernet_info *myinfo,uint32_t ipbits)
{
    int32_t j;
    for (j=0; j<myinfo->NOTARY.NUMRELAYS; j++)
        if ( myinfo->NOTARY.RELAYS[j].ipbits == ipbits )
            return(j);
    return(-1);
}

#include "basilisk_bitcoin.c"
#include "basilisk_nxt.c"
#include "basilisk_ether.c"
#include "basilisk_waves.c"
#include "basilisk_lisk.c"
#include "smartaddress.c"

#include "basilisk_MSG.c"
#include "tradebots_marketmaker.c"
#include "tradebots_liquidity.c"
#include "basilisk_tradebot.c"
#include "basilisk_swap.c"
#include "basilisk_DEX.c"
#include "basilisk_ping.c"
#include "basilisk_vote.c"
#include "basilisk_CMD.c"
#include "jumblr.c"

void basilisk_functions(struct iguana_info *coin,int32_t protocol)
{
    coin->protocol = protocol;
    switch ( protocol )
    {
        /*case IGUANA_PROTOCOL_BITCOIN:
            coin->basilisk_balances = basilisk_bitcoinbalances;
            coin->basilisk_rawtx = basilisk_bitcoinrawtx;
            //coin->basilisk_rawtxmetric = basilisk_bitcoin_rawtxmetric;
            coin->basilisk_value = basilisk_bitcoinvalue;
            coin->basilisk_valuemetric = basilisk_bitcoin_valuemetric;
            break;
        case IGUANA_PROTOCOL_IOTA:
            coin->basilisk_balances = basilisk_iotabalances;
            coin->basilisk_rawtx = basilisk_iotarawtx;
            break;
        case IGUANA_PROTOCOL_NXT:
            coin->basilisk_balances = basilisk_nxtbalances;
            coin->basilisk_rawtx = basilisk_nxtrawtx;
            break;
        case IGUANA_PROTOCOL_ETHER:
            coin->basilisk_balances = basilisk_etherbalances;
            coin->basilisk_rawtx = basilisk_etherrawtx;
            break;
        case IGUANA_PROTOCOL_WAVES:
            coin->basilisk_balances = basilisk_wavesbalances;
            coin->basilisk_rawtx = basilisk_wavesrawtx;
            break;
        case IGUANA_PROTOCOL_LISK:
            coin->basilisk_balances = basilisk_liskbalances;
            coin->basilisk_rawtx = basilisk_liskrawtx;
            break;*/
    }
}

int32_t basilisk_hashes_send(struct supernet_info *myinfo,struct iguana_info *virt,struct iguana_peer *addr,char *CMD,bits256 *hashes,int32_t num)
{
    bits256 hash; uint8_t *serialized; int32_t i,len = 0; char *str=0,*retstr,*hexstr,space[4096]; bits256 txid; cJSON *vals;
    if ( virt != 0 && addr != 0 )
    {
        memset(hash.bytes,0,sizeof(hash));
        serialized = (void *)hashes;
        for (i=0; i<num; i++)
        {
            txid = hashes[i];
            len += iguana_rwbignum(1,&serialized[len],sizeof(txid),txid.bytes);
        }
        if ( (hexstr= basilisk_addhexstr(&str,0,space,sizeof(space),serialized,len)) != 0 )
        {
            vals = cJSON_CreateObject();
            jaddstr(vals,"symbol",virt->symbol);
            if ( (retstr= basilisk_standardservice(CMD,myinfo,addr,hash,vals,hexstr,0)) != 0 )
                free(retstr);
            free_json(vals);
            if ( str != 0 )
                free(str);
        }
        return(0);
    } else return(-1);
}

void basilisk_geckoresult(struct supernet_info *myinfo,char *remoteaddr,char *retstr,uint8_t *data,int32_t datalen)
{
    struct iguana_info *virt; char *symbol,*str,*type; cJSON *retjson; bits256 hash2;
    if ( retstr != 0 && (retjson= cJSON_Parse(retstr)) != 0 )
    {
        if ( (symbol= jstr(retjson,"symbol")) != 0 && (virt= iguana_coinfind(symbol)) != 0 )
        {
            if ( data != 0 )
            {
                str = 0;
                if ( (type= jstr(retjson,"type")) != 0 )
                {
                    hash2 = jbits256(retjson,"hash");
                    if ( strcmp(type,"HDR") == 0 && virt->virtualchain != 0 )
                        str = gecko_headersarrived(myinfo,virt,remoteaddr,data,datalen,hash2);
                    else if ( strcmp(type,"MEM") == 0 && virt->virtualchain != 0 )
                        str = gecko_mempoolarrived(myinfo,virt,remoteaddr,data,datalen,hash2);
                    else if ( strcmp(type,"BLK") == 0 && virt->virtualchain != 0 )
                        str = gecko_blockarrived(myinfo,virt,remoteaddr,data,datalen,hash2,0);
                    else if ( strcmp(type,"GTX") == 0 && virt->virtualchain != 0 )
                        str = gecko_txarrived(myinfo,virt,remoteaddr,data,datalen,hash2);
                }
                if ( str != 0 )
                    free(str);
            }
        }
        free_json(retjson);
    }
}

void basilisk_result(struct supernet_info *myinfo,char *remoteaddr,uint32_t basilisktag,cJSON *vals,uint8_t *data,int32_t datalen)
{
    char *retstr,CMD[16]; struct basilisk_item *pending; cJSON *item;
    if ( vals != 0 )
    {
        retstr = jprint(vals,0);
        safecopy(CMD,jstr(vals,"origcmd"),sizeof(CMD));
        //if ( 0 && strcmp("RID",CMD) != 0 )
        //printf("(%s) -> Q.%u results vals.(%s)\n",CMD,basilisktag,retstr);//(int32_t)strlen(retstr));
        if ( strcmp(CMD,"GET") == 0 )
            basilisk_geckoresult(myinfo,remoteaddr,retstr,data,datalen);
        else
        {
            portable_mutex_lock(&myinfo->basilisk_mutex);
            HASH_FIND(hh,myinfo->basilisks.issued,&basilisktag,sizeof(basilisktag),pending);
            portable_mutex_unlock(&myinfo->basilisk_mutex);
            if ( pending != 0 && retstr != 0 )
            {
                if ( (item= cJSON_Parse(retstr)) != 0 )
                {
                    if ( jobj(item,"myip") == 0 )
                        jaddstr(item,"myip",remoteaddr);
                    if ( pending->numresults < sizeof(pending->results)/sizeof(*pending->results) )
                    {
                        //printf("%p.(%s).%d\n",pending,jprint(item,0),pending->numresults);
                        pending->results[pending->numresults++] = item;
                    }
                } else printf("couldnt parse.(%s)\n",retstr);
            } //else printf("couldnt find issued.%u\n",basilisktag);
        }
        free(retstr);
    }
}

void basilisk_wait(struct supernet_info *myinfo,struct iguana_info *coin)
{
    if ( coin != 0 )
    {
        while ( coin->basilisk_busy != 0 )
            usleep(1000);
    }
    else
    {
        while ( myinfo->basilisk_busy != 0 )
            usleep(1000);
    }
}

void basilisk_msgprocess(struct supernet_info *myinfo,void *_addr,uint32_t senderipbits,char *type,uint32_t basilisktag,uint8_t *data,int32_t datalen)
{
    cJSON *valsobj; char *symbol,*retstr=0,remoteaddr[64],CMD[4],cmd[4]; int32_t height,origlen,from_basilisk,i,timeoutmillis,flag,numrequired,jsonlen; uint8_t *origdata; struct iguana_info *coin=0; bits256 hash; struct iguana_peer *addr = _addr;
    static basilisk_servicefunc *basilisk_services[][2] =
    {
        { (void *)"OUT", &basilisk_respond_OUT },       // send MSG to hash/id/num
        { (void *)"MSG", &basilisk_respond_MSG },       // get MSG (hash, id, num)
        { (void *)"ADD", &basilisk_respond_addrelay },  // relays register with each other bus
        { (void *)"VOT", &basilisk_respond_VOT },       // VOTE handler for something
        //{ (void *)"PIN", &basilisk_respond_PIN },
        
        // encrypted data for jumblr
        { (void *)"HOP", &basilisk_respond_forward },    // message forwarding
        { (void *)"BOX", &basilisk_respond_mailbox },    // create/send/check mailbox pubkey
        
        // small virtual private network
        { (void *)"VPN", &basilisk_respond_VPNcreate },  // create virtual network's hub via privkey
        { (void *)"ARC", &basilisk_respond_VPNjoin },    // join
        { (void *)"GAB", &basilisk_respond_VPNmessage }, // private message
        { (void *)"SAY", &basilisk_respond_VPNbroadcast }, // broadcast
        { (void *)"EAR", &basilisk_respond_VPNreceive }, // network receive (via poll)
        { (void *)"END", &basilisk_respond_VPNlogout },  // logout
         
        //{ (void *)"DEX", &basilisk_respond_DEX },
        //{ (void *)"RID", &basilisk_respond_RID },
        //{ (void *)"ACC", &basilisk_respond_ACC },
        { (void *)"BYE", &basilisk_respond_goodbye },    // disconnect
        
        // gecko chains
        { (void *)"GET", &basilisk_respond_geckoget },      // requests headers, block or tx
        { (void *)"HDR", &basilisk_respond_geckoheaders },  // reports headers
        { (void *)"BLK", &basilisk_respond_geckoblock },    // reports virtchain block
        { (void *)"MEM", &basilisk_respond_mempool },       // reports virtchain mempool
        { (void *)"GTX", &basilisk_respond_geckotx },       // reports virtchain tx
        
        // coin services
        { (void *)"VAL", &basilisk_respond_value },
        { (void *)"BAL", &basilisk_respond_balances },
        { (void *)"INF", &basilisk_respond_getinfo },
    };
    strncpy(CMD,type,3), CMD[3] = cmd[3] = 0;
    if ( isupper((int32_t)CMD[0]) != 0 && isupper((int32_t)CMD[1]) != 0 && isupper((int32_t)CMD[2]) != 0 )
        from_basilisk = 1;
    else from_basilisk = 0;
    origdata = data;
    origlen = datalen;
    for (i=0; i<3; i++)
    {
        CMD[i] = toupper((int32_t)CMD[i]);
        cmd[i] = tolower((int32_t)CMD[i]);
    }
    if ( myinfo->IAMNOTARY != 0 )//RELAYID >= 0 )
    {
        if ( basilisk_notarycmd(CMD) == 0 && strcmp(CMD,"INF") != 0 )
            return;
    } else if ( basilisk_notarycmd(CMD) != 0 )
        return;
    symbol = "RELAY";
    if ( senderipbits == 0 )
        expand_ipbits(remoteaddr,myinfo->myaddr.myipbits);
    else expand_ipbits(remoteaddr,senderipbits);
    if ( (valsobj= cJSON_Parse((char *)data)) != 0 )
    {
        if ( jobj(valsobj,"coin") != 0 )
            coin = iguana_coinfind(jstr(valsobj,"coin"));
        else if ( jobj(valsobj,"symbol") != 0 )
            coin = iguana_coinfind(jstr(valsobj,"symbol"));
        if ( coin != 0 )
        {
            if ( (height= juint(valsobj,"hwm")) > 0 )
            {
                if ( height > addr->height )
                    addr->height = height;
                if ( height > coin->longestchain )
                    coin->longestchain = height;
            }
        }
        if ( strcmp(type,"RET") == 0 )
        {
            basilisk_result(myinfo,remoteaddr,basilisktag,valsobj,data,datalen);
            return;
        }
    }
    else
    {
        printf("unexpected binary packet datalen.%d\n",datalen);
        return;
    }
    for (i=flag=0; i<sizeof(basilisk_services)/sizeof(*basilisk_services); i++) // iguana node
        if ( strcmp((char *)basilisk_services[i][0],type) == 0 )
        {
            flag = 1;
            break;
        }
    if ( flag == 0 )
        return;
    myinfo->basilisk_busy = 1;
    if ( valsobj != 0 )
    {
        jsonlen = (int32_t)strlen((char *)data) + 1;
        if ( datalen > jsonlen )
            data += jsonlen, datalen -= jsonlen;
        else data = 0, datalen = 0;
        if ( coin == 0 )
            coin = iguana_coinfind(symbol);
        if ( coin != 0 )
        {
            symbol = coin->symbol;
            coin->basilisk_busy = 1;
        }
        hash = jbits256(valsobj,"hash");
        timeoutmillis = jint(valsobj,"timeout");
        if ( (numrequired= jint(valsobj,"numrequired")) == 0 )
            numrequired = sqrt(myinfo->NOTARY.NUMRELAYS)+1;
        if ( senderipbits != 0 )
            expand_ipbits(remoteaddr,senderipbits);
        else remoteaddr[0] = 0;
        for (i=0; i<sizeof(basilisk_services)/sizeof(*basilisk_services); i++) // iguana node
        {
            if ( strcmp((char *)basilisk_services[i][0],type) == 0 )
            {
                if ( (coin != 0 && coin->FULLNODE > 0) || myinfo->NOTARY.RELAYID >= 0 ) // iguana node
                {
                    //printf("\n call %s from_basilisk.%d (%s)\n",addr->ipaddr,from_basilisk,type);
                    if ( (retstr= (*basilisk_services[i][1])(myinfo,type,addr,remoteaddr,basilisktag,valsobj,data,datalen,hash,from_basilisk)) != 0 )
                    {
                        //printf("%s from_basilisk.%d ret.(%s)\n",addr->ipaddr,from_basilisk,retstr);
                        //if ( from_basilisk != 0 || strcmp(CMD,"GET") == 0 )
                            basilisk_sendback(myinfo,CMD,symbol,remoteaddr,basilisktag,retstr);
                        if ( retstr != 0 )
                            free(retstr);
                        break;
                    } else printf("services null return\n");
                } else printf("non-relay got %s unhandled.(%s)\n",coin!=0?coin->symbol:"",type);
            }
        }
        free_json(valsobj);
    }
    if ( coin != 0 )
        coin->basilisk_busy = 0;
    myinfo->basilisk_busy = 0;
}

int32_t basilisk_p2pQ_process(struct supernet_info *myinfo,int32_t maxiters)
{
    struct basilisk_p2pitem *ptr; char senderip[64]; uint32_t n=0,basilisktag,len;
    while ( n < maxiters && (ptr= queue_dequeue(&myinfo->p2pQ)) != 0 )
    {
        len = 0;
        expand_ipbits(senderip,ptr->ipbits);
        //printf("p2p.%d from.(%s) %c%c%c datalen.%d\n",n,senderip,ptr->type[0],ptr->type[1],ptr->type[2],ptr->datalen);
        if ( ptr->type[0] == 'P' && ptr->type[1] == 'I' && ptr->type[2] == 'N' )
        {
            if ( myinfo->NOTARY.RELAYID >= 0 )
            {
                //printf("process ping\n");
                basilisk_ping_process(myinfo,ptr->addr,ptr->ipbits,ptr->data,ptr->datalen);
                //printf("done process ping\n");
            }
        }
        else
        {
            len += iguana_rwnum(0,ptr->data,sizeof(basilisktag),&basilisktag);
            if ( (0) && myinfo->IAMLP == 0 )
                printf("RELAYID.%d ->received.%d basilisk_p2p.(%s) from %s tag.%u\n",myinfo->NOTARY.RELAYID,ptr->datalen,ptr->type,senderip,basilisktag);
            basilisk_msgprocess(myinfo,ptr->addr,ptr->ipbits,ptr->type,basilisktag,&ptr->data[len],ptr->datalen - len);
            if ( (0) && myinfo->IAMLP == 0 )
                printf("processed.%s from %s\n",ptr->type,senderip);
        }
        free(ptr);
        n++;
    }
    return(n);
}

struct basilisk_p2pitem *basilisk_p2pitem_create(struct iguana_info *coin,struct iguana_peer *addr,char *type,uint32_t ipbits,uint8_t *data,int32_t datalen)
{
    struct basilisk_p2pitem *ptr;
    ptr = calloc(1,sizeof(*ptr) + datalen + 16);
    ptr->coin = coin;
    ptr->addr = addr;
    ptr->ipbits = ipbits;
    ptr->datalen = datalen;
    safecopy(ptr->type,type,sizeof(ptr->type));
    memcpy(ptr->data,data,datalen);
    return(ptr);
}

void basilisk_p2p(struct supernet_info *myinfo,struct iguana_info *coin,struct iguana_peer *addr,char *senderip,uint8_t *data,int32_t datalen,char *type,int32_t encrypted)
{
    uint32_t ipbits; int32_t msglen; void *ptr = 0; uint8_t space[4096]; bits256 senderpub;
    ipbits = (uint32_t)calc_ipbits(senderip);
    if ( encrypted != 0 )
    {
        printf("encrypted p2p\n");
        memset(senderpub.bytes,0,sizeof(senderpub));
        if ( (data= SuperNET_deciphercalc(&ptr,&msglen,myinfo->privkey,senderpub,data,datalen,space,sizeof(space))) == 0 )
        {
            printf("basilisk_p2p decrytion error\n");
            return;
        } else datalen = msglen;
        if ( ptr != 0 )
            free(ptr);
    }
    if ( senderip != 0 && senderip[0] != 0 && strcmp(senderip,"127.0.0.1") != 0 )
        ipbits = (uint32_t)calc_ipbits(senderip);
    else ipbits = myinfo->myaddr.myipbits;
    ptr = basilisk_p2pitem_create(coin,addr,type,ipbits,data,datalen);
    queue_enqueue("p2pQ",&myinfo->p2pQ,ptr);
}

int32_t basilisk_issued_purge(struct supernet_info *myinfo,int32_t timepad)
{
    struct basilisk_item *tmp,*pending; cJSON *item; int32_t i,n = 0; double startmilli = OS_milliseconds();
    portable_mutex_lock(&myinfo->basilisk_mutex);
    HASH_ITER(hh,myinfo->basilisks.issued,pending,tmp)
    {
        if ( pending != 0 && ((pending->finished > 0 && startmilli > pending->finished) || startmilli > pending->expiration+timepad) )
        {
            HASH_DELETE(hh,myinfo->basilisks.issued,pending);
            //printf("%f > %f (%d) clear pending.%p numresults.%d %p\n",startmilli,pending->expiration+timepad,timepad,pending,pending->numresults,pending->retstr);
            for (i=0; i<pending->numresults; i++)
                if ( (item= pending->results[i]) != 0 )
                    free_json(item);
            if ( pending->retstr != 0 )
                free(pending->retstr);
            memset(pending,0,sizeof(*pending));
            free(pending);
            n++;
        }
    }
    portable_mutex_unlock(&myinfo->basilisk_mutex);
    return(n);
}

void basilisks_loop(void *arg)
{
    static uint32_t counter;
    struct iguana_info *relay; struct supernet_info *myinfo = arg; int32_t i,iter; double startmilli,endmilli; struct dpow_info *dp;
    iter = 0;
    relay = iguana_coinfind("RELAY");
    printf("start basilisk loop\n");
    while ( 1 )
    {
        if ( relay == 0 )
            relay = iguana_coinfind("RELAY");
        startmilli = OS_milliseconds();
        endmilli = startmilli + 1000;
        //fprintf(stderr,"A ");
        basilisk_issued_purge(myinfo,600000);
        //fprintf(stderr,"B ");
        basilisk_p2pQ_process(myinfo,777);
        //fprintf(stderr,"C ");
        if ( myinfo->IAMNOTARY != 0 )
        {
            if ( relay != 0 )
            {
                //fprintf(stderr,"D ");
                basilisk_ping_send(myinfo,relay);
            }
            counter++;
            //fprintf(stderr,"E ");
            if ( myinfo->numdpows == 1 )
            {
                iguana_dPoWupdate(myinfo,&myinfo->DPOWS[0]);
                endmilli = startmilli + 100;
            }
            else if ( myinfo->numdpows > 1 )
            {
                dp = &myinfo->DPOWS[counter % myinfo->numdpows];
                iguana_dPoWupdate(myinfo,dp);
                //if ( (counter % myinfo->numdpows) != 0 )
                {
                    //fprintf(stderr,"F ");
                    iguana_dPoWupdate(myinfo,&myinfo->DPOWS[0]);
                }
                endmilli = startmilli + 30;
            }
            //fprintf(stderr,"F ");
        }
        else
        {
            //fprintf(stderr,"G ");
            dex_updateclient(myinfo);
            if ( myinfo->IAMLP != 0 )
                endmilli = startmilli + 500;
            else endmilli = startmilli + 1000;
        }
        if ( myinfo->expiration != 0 && (myinfo->dexsock >= 0 || myinfo->IAMLP != 0 || myinfo->DEXactive > time(NULL)) )
        {
            //fprintf(stderr,"H ");
            for (i=0; i<100; i++)
                if ( basilisk_requests_poll(myinfo) <= 0 )
                    break;
        }
        //printf("RELAYID.%d endmilli %f vs now %f\n",myinfo->NOTARY.RELAYID,endmilli,startmilli);
        while ( OS_milliseconds() < endmilli )
            usleep(10000);
        //printf("finished waiting numdpow.%d\n",myinfo->numdpows);
        iter++;
    }
}

void basilisks_init(struct supernet_info *myinfo)
{
    iguana_initQ(&myinfo->p2pQ,"p2pQ");
    iguana_initQ(&myinfo->msgQ,"messageQ");
    portable_mutex_init(&myinfo->bu_mutex);
    portable_mutex_init(&myinfo->allcoins_mutex);
    portable_mutex_init(&myinfo->basilisk_mutex);
    portable_mutex_init(&myinfo->smart_mutex);
    portable_mutex_init(&myinfo->DEX_mutex);
    portable_mutex_init(&myinfo->DEX_swapmutex);
    portable_mutex_init(&myinfo->DEX_reqmutex);
    portable_mutex_init(&myinfo->gecko_mutex);
    portable_mutex_init(&myinfo->messagemutex);
    myinfo->basilisks.launched = iguana_launch(iguana_coinfind("BTCD"),"basilisks_loop",basilisks_loop,myinfo,IGUANA_PERMTHREAD);
    printf("Basilisk initialized\n");
}

#include "../includes/iguana_apidefs.h"
#include "../includes/iguana_apideclares.h"
#include "../includes/iguana_apideclares2.h"

TWO_STRINGS(tradebot,gensvm,base,rel)
{
#ifdef _WIN
    return(clonestr("{\"error\":\"windows doesnt support SVM\"}"));
#else
    int32_t numfeatures = 317*61;
    struct tradebot_arbpair *pair;
    if ( base[0] != 0 && rel[0] != 0 && (pair= tradebots_arbpair_find(base,rel)) != 0 && pair->fp != 0 )
    {
        tradebots_calcanswers(pair);
        ocas_gen(pair->refc,numfeatures,0,(int32_t)(ftell(pair->fp) / sizeof(pair->rawfeatures)));
        return(clonestr("{\"result\":\"success\"}"));
    } else return(clonestr("{\"error\":\"cant find arbpair\"}"));
#endif
}

ZERO_ARGS(tradebot,openliquidity)
{
    int32_t i; cJSON *array = cJSON_CreateArray();
    for (i=0; i<sizeof(myinfo->linfos)/sizeof(*myinfo->linfos); i++)
    {
        if ( myinfo->linfos[i].base[0] != 0 )
            jaddi(array,linfo_json(&myinfo->linfos[i]));
    }
    return(jprint(array,1));
}

ZERO_ARGS(tradebot,allbalances)
{
    int32_t i,n; double value,pending; char *base; cJSON *item,*balances = cJSON_CreateObject();
    if ( myinfo->liquidity_currencies == 0 )
        myinfo->liquidity_currencies = cJSON_Parse("[\"KMD\", \"BTC\"]");
    if ( myinfo->liquidity_currencies != 0 && (n= cJSON_GetArraySize(myinfo->liquidity_currencies)) > 0 )
    {
        for (i=0; i<n; i++)
        {
            base = jstri(myinfo->liquidity_currencies,i);
            value = tradebot_balance(myinfo,base);
            pending = tradebot_pending(myinfo,base);
            item = cJSON_CreateObject();
            jaddnum(item,"value",value);
            jaddnum(item,"pending",pending);
            jadd(balances,base,item);
        }
    }
    return(jprint(balances,1));
}

ZERO_ARGS(tradebot,anchor)
{
    FILE *fp; char *anchorstr,fname[512]; cJSON *anchor; int32_t retval = -1;
    if ( (anchorstr= tradebot_allbalances(myinfo,0,0,0)) != 0 )
    {
        if ( (anchor= cJSON_Parse(anchorstr)) != 0 )
        {
            if ( jobj(anchor,"error") == 0 )
            {
                sprintf(fname,"%s/anchor",GLOBAL_DBDIR), OS_compatible_path(fname);
                if ( (fp= fopen(fname,"wb")) != 0 )
                {
                    if ( fwrite(anchorstr,1,strlen(anchorstr)+1,fp) == strlen(anchorstr)+1 )
                        retval = 0;
                    fclose(fp);
                }
            }
        }
        free(anchorstr);
    }
    if ( retval == 0 )
        return(clonestr("{\"result\":\"success\"}"));
    else return(clonestr("{\"error\":\"couldnt make anchor file\"}"));
}

ZERO_ARGS(tradebot,portfolio)
{
    char *currentstr,*anchorstr,fname[512]; long fsize; cJSON *current,*anchor=0,*portfolio=0;
    if ( (currentstr= tradebot_allbalances(myinfo,0,0,0)) != 0 )
    {
        if ( (current= cJSON_Parse(currentstr)) != 0 )
        {
            sprintf(fname,"%s/anchor",GLOBAL_DBDIR), OS_compatible_path(fname);
            if ( (anchorstr= OS_filestr(&fsize,fname)) != 0 )
            {
                anchor = cJSON_Parse(anchorstr);
                free(anchorstr);
            }
            if ( anchor == 0 )
                anchor = cJSON_Parse("{}");
            portfolio = tradebot_balancesdiff(myinfo,current,anchor);
            free_json(current);
        }
        free(currentstr);
    }
    if ( portfolio == 0 )
        return(clonestr("{\"result\":\"success\"}"));
    else return(jprint(portfolio,1));
}

ARRAY_OBJ_INT(tradebot,goals,currencies,vals,targettime)
{
    static bits256 zero; char *targetcoin; int32_t i,n;
    if ( currencies != 0 && vals != 0 )
    {
        // init things so automatically updates refli.bid and refli.ask
        // volume range with margin
        // currency percentage value in BTC? target distribution, max percentage, min percentage`
        // min price to sell, max price to buy, max volume
        n = cJSON_GetArraySize(currencies);
        for (i=0; i<n; i++)
        {
            targetcoin = jstri(currencies,i);
            tradebot_liquidity_command(myinfo,targetcoin,zero,vals);
        }
        return(clonestr("{\"result\":\"success\"}"));
    } else return(clonestr("{\"error\":\"no currencies or vals\"}"));
}

HASH_ARRAY_STRING(basilisk,getmessage,hash,vals,hexstr)
{
    uint32_t msgid,width,channel; char *retstr;
    if ( bits256_cmp(GENESIS_PUBKEY,jbits256(vals,"srchash")) == 0 )
        jaddbits256(vals,"srchash",hash);
    if ( bits256_cmp(GENESIS_PUBKEY,jbits256(vals,"desthash")) == 0 )
        jaddbits256(vals,"desthash",myinfo->myaddr.persistent);
    if ( (msgid= juint(vals,"msgid")) == 0 )
    {
        msgid = (uint32_t)time(NULL);
        jdelete(vals,"msgid");
        jaddnum(vals,"msgid",msgid);
    }
    if ( myinfo->NOTARY.RELAYID >= 0 || myinfo->dexsock >= 0 || myinfo->subsock >= 0 )
    {
        channel = juint(vals,"channel");
        width = juint(vals,"width");
        retstr = basilisk_iterate_MSG(myinfo,channel,msgid,jbits256(vals,"srchash"),jbits256(vals,"desthash"),width);
        //printf("getmessage.(%s)\n",retstr);
        return(retstr);
    }
    //printf("getmessage not relay.%d dexsock.%d subsock.%d\n",myinfo->NOTARY.RELAYID,myinfo->dexsock,myinfo->subsock);
    return(basilisk_standardservice("MSG",myinfo,0,jbits256(vals,"desthash"),vals,hexstr,1));
}

HASH_ARRAY_STRING(basilisk,sendmessage,hash,vals,hexstr)
{
    int32_t keylen,datalen,allocsize = 65536; uint8_t key[BASILISK_KEYSIZE],*space,*space2,*data,*ptr = 0; char *retstr=0;
    space = calloc(1,allocsize);
    space2 = calloc(1,allocsize);
    data = get_dataptr(BASILISK_HDROFFSET,&ptr,&datalen,&space[BASILISK_KEYSIZE],allocsize-BASILISK_KEYSIZE,hexstr);
    if ( myinfo->subsock >= 0 || myinfo->dexsock >= 0 || (myinfo->IAMNOTARY != 0 && myinfo->NOTARY.RELAYID >= 0) )
    {
        keylen = basilisk_messagekey(key,juint(vals,"channel"),juint(vals,"msgid"),jbits256(vals,"srchash"),jbits256(vals,"desthash"));
        if ( data != 0 )
        {
            retstr = basilisk_respond_addmessage(myinfo,key,keylen,data,datalen,0,juint(vals,"duration"));
        } else printf("no get_dataptr\n");
        if ( retstr != 0 )
            free(retstr);
    } //else printf("not notary.%d relayid.%d\n",myinfo->IAMNOTARY,myinfo->NOTARY.RELAYID);
    if ( vals != 0 && juint(vals,"fanout") == 0 )
        jaddnum(vals,"fanout",MAX(8,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)+2));
    if ( BASILISK_KEYSIZE+datalen < allocsize )
    {
        memcpy(space2,key,BASILISK_KEYSIZE);
        if ( data != 0 && datalen != 0 )
            memcpy(&space2[BASILISK_KEYSIZE],data,datalen);
        dex_reqsend(myinfo,"DEX",space2,datalen+BASILISK_KEYSIZE,1,"");
    } else printf("sendmessage space too small error for %d\n",datalen);
    free(space);
    free(space2);
    if ( ptr != 0 )
        free(ptr);
    return(basilisk_standardservice("OUT",myinfo,0,jbits256(vals,"desthash"),vals,hexstr,0));
}

HASH_ARRAY_STRING(basilisk,value,hash,vals,hexstr)
{
    char *retstr=0,*symbol,*coinaddr,*infostr; cJSON *retjson,*sobj,*info,*addrs,*txoutjson,*txjson,*array; uint32_t basilisktag,blocktime,numtx=0; bits256 txid,blockhash; struct basilisk_item *ptr,Lptr; uint64_t value; int32_t timeoutmillis,vout,height,n,m;
    if ( vals == 0 )
        return(clonestr("{\"error\":\"null valsobj\"}"));
    //if ( myinfo->IAMNOTARY != 0 || myinfo->NOTARY.RELAYID >= 0 )
    //    return(clonestr("{\"error\":\"special relays only do OUT and MSG\"}"));
    //if ( coin == 0 )
    {
        if ( (symbol= jstr(vals,"symbol")) != 0 || (symbol= jstr(vals,"coin")) != 0 )
            coin = iguana_coinfind(symbol);
    }
    if ( jobj(vals,"fanout") == 0 )
        jaddnum(vals,"fanout",MAX(5,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)+1));
    txid = jbits256(vals,"txid");
    vout = jint(vals,"vout");
    if ( coin != 0 )
    {
        if ( coin->FULLNODE < 0 )
        {
            if ( (txoutjson= dpow_gettxout(myinfo,coin,txid,vout)) != 0 )
            {
                if ( (value= SATOSHIDEN*jdouble(txoutjson,"value")) == 0 )
                    value = SATOSHIDEN*jdouble(txoutjson,"amount");
                if ( (coinaddr= jstr(txoutjson,"address")) == 0 )
                {
                    if ( (sobj= jobj(txoutjson,"scriptPubKey")) != 0 && (addrs= jarray(&n,sobj,"addresses")) != 0 && n > 0 )
                        coinaddr = jstri(addrs,0);
                    printf("no address, check addrs %p coinaddr.%p\n",sobj,coinaddr);
                }
                if ( coinaddr != 0 && value != 0 )
                {
                    retjson = cJSON_CreateObject();
                    jaddstr(retjson,"result","success");
                    jaddstr(retjson,"address",coinaddr);
                    jadd64bits(retjson,"satoshis",value);
                    jaddnum(retjson,"value",dstr(value));
                    jaddnum(retjson,"amount",dstr(value));
                    height = dpow_getchaintip(myinfo,&blockhash,&blocktime,0,&numtx,coin);
                    jaddnum(retjson,"height",height);
                    jaddnum(retjson,"numconfirms",jint(txoutjson,"confirmations"));
                    jaddbits256(retjson,"txid",txid);
                    jaddnum(retjson,"vout",vout);
                    jaddstr(retjson,"coin",coin->symbol);
                }
                else
                {
                    printf("missing fields.(%s)\n",jprint(txoutjson,0));
                    free_json(txoutjson);
                    return(clonestr("{\"error\":\"return from gettxout missing fields\"}"));
                }
                free_json(txoutjson);
                return(jprint(retjson,1));
            } //else return(clonestr("{\"error\":\"null return from gettxout\"}"));
        }
        else
        {
            if ( (basilisktag= juint(vals,"basilisktag")) == 0 )
                basilisktag = rand();
            if ( (timeoutmillis= juint(vals,"timeout")) <= 0 )
                timeoutmillis = BASILISK_TIMEOUT;
            if ( coin->FULLNODE > 0 && (ptr= basilisk_bitcoinvalue(&Lptr,myinfo,coin,remoteaddr,basilisktag,timeoutmillis,vals)) != 0 )
            {
                retstr = ptr->retstr, ptr->retstr = 0;
                ptr->finished = OS_milliseconds() + 10000;
                return(retstr);
            }
        }
    }
    if ( myinfo->reqsock >= 0 )
    {
        if ( (retstr= _dex_getrawtransaction(myinfo,symbol,txid)) != 0 )
        {
            if ( (txoutjson= cJSON_Parse(retstr)) != 0 )
            {
                //printf("TX.(%s)\n",jprint(txoutjson,0));
                retjson = cJSON_CreateObject();
                jaddstr(retjson,"result","success");
                jaddnum(retjson,"numconfirms",jint(txoutjson,"confirmations"));
                if ( (height= jint(txoutjson,"height")) == 0 && coin != 0 )
                    height = coin->longestchain - jint(txoutjson,"confirmations");
                jaddnum(retjson,"height",height);
                if ( (array= jarray(&n,txoutjson,"vout")) != 0 && vout < n && (txjson= jitem(array,vout)) != 0 )
                {
                    //printf("txjson.(%s)\n",jprint(txjson,0));
                    if ( (value= jdouble(txjson,"value") * SATOSHIDEN) != 0 )
                    {
                        if ( (sobj= jobj(txjson,"scriptPubKey")) != 0 && (addrs= jarray(&m,sobj,"addresses")) != 0 && (coinaddr= jstri(addrs,0)) != 0 )
                            jaddstr(retjson,"address",coinaddr);
                        jadd64bits(retjson,"satoshis",value);
                        jaddnum(retjson,"value",dstr(value));
                        if ( (infostr= _dex_getinfo(myinfo,symbol)) != 0 )
                        {
                            if ( (info= cJSON_Parse(infostr)) != 0 )
                            {
                                if ( (height= jint(info,"blocks")) > 0 )
                                {
                                    height -= jint(txoutjson,"confirmations");
                                    jaddnum(retjson,"height",height);
                                }
                                free_json(info);
                            }
                            free(infostr);
                        }
                        jaddbits256(retjson,"txid",txid);
                        jaddnum(retjson,"vout",vout);
                        jaddstr(retjson,"coin",symbol);
                        free(retstr);
                        free_json(txoutjson);
                        return(jprint(retjson,1));
                    }
                }
                free_json(txoutjson);
                return(jprint(retjson,1));
            }
            return(retstr);
        }
    }
    return(basilisk_standardservice("VAL",myinfo,0,hash,vals,hexstr,1));
}

HASH_ARRAY_STRING(basilisk,rawtx,hash,vals,hexstr)
{
    char *retstr=0,*symbol; uint32_t basilisktag; int32_t timeoutmillis,i,retval = -1; uint64_t amount,txfee; cJSON *retarray;
    if ( vals == 0 )
        return(clonestr("{\"error\":\"null valsobj\"}"));
    //if ( coin == 0 )
    {
        if ( (symbol= jstr(vals,"symbol")) != 0 || (symbol= jstr(vals,"coin")) != 0 )
            coin = iguana_coinfind(symbol);
    }
    if ( jobj(vals,"numrequired") == 0 )
        jaddnum(vals,"numrequired",MIN(3,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)+1));
    if ( jobj(vals,"fanout") == 0 )
        jaddnum(vals,"fanout",MAX(3,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)+1));
    if ( coin != 0 )
    {
        //if ( juint(vals,"burn") == 0 )
        //    jaddnum(vals,"burn",0.0001);
        if ( (basilisktag= juint(vals,"basilisktag")) == 0 )
            basilisktag = rand();
        if ( (timeoutmillis= juint(vals,"timeout")) <= 0 )
            timeoutmillis = BASILISK_TIMEOUT;
        if ( (retstr= basilisk_bitcoinrawtx(myinfo,coin,remoteaddr,basilisktag,timeoutmillis,vals,0)) != 0 )
        {
            printf("rawtx.(%s)\n",retstr);
            if ( (amount= j64bits(vals,"satoshis")) == 0 )
                amount = jdouble(vals,"value") * SATOSHIDEN;
            if ( (txfee= j64bits(vals,"txfee")) == 0 )
                txfee = coin->chain->txfee;
            if ( txfee == 0 )
                txfee = 10000;
            retval = -1;
            if ( (retarray= cJSON_Parse(retstr)) != 0 )
            {
                if ( is_cJSON_Array(retarray) != 0 )
                {
                    for (i=0; i<cJSON_GetArraySize(retarray); i++)
                    {
                        if ( basilisk_vins_validate(myinfo,coin,jitem(retarray,i),amount,txfee) == 0 )
                        {
                            retval = 0;
                            break;
                        }
                    }
                } else retval = basilisk_vins_validate(myinfo,coin,retarray,amount,txfee);
                if ( retval < 0 )
                {
                    printf("ERROR.(%s)\n",retstr);
                    free(retstr);
                    retstr = clonestr("{\"error\":\"invalid vin in rawtx\"}");
                }
            }
        }
    } else retstr = clonestr("{\"error\":\"no coin specified or found\"}");
    return(retstr);
}

STRING_ARG(jumblr,setpassphrase,passphrase)
{
    cJSON *retjson,*tmp; char KMDaddr[64],BTCaddr[64],wifstr[64],*smartaddrs; bits256 privkey; struct iguana_info *coinbtc;
    if ( passphrase == 0 || passphrase[0] == 0 || (coin= iguana_coinfind("KMD")) == 0 )//|| coin->FULLNODE >= 0 )
        return(clonestr("{\"error\":\"no passphrase or no native komodod\"}"));
    else
    {
        safecopy(myinfo->jumblr_passphrase,passphrase,sizeof(myinfo->jumblr_passphrase));
        retjson = cJSON_CreateObject();
        jaddstr(retjson,"result","success");
        privkey = jumblr_privkey(myinfo,BTCaddr,0,KMDaddr,JUMBLR_DEPOSITPREFIX);
        smartaddress_add(myinfo,privkey,"deposit","KMD",0.,0.);
        myinfo->jumblr_depositkey = curve25519(privkey,curve25519_basepoint9());
        bitcoin_priv2wif(wifstr,privkey,coin->chain->wiftype);
        if ( coin->FULLNODE < 0 )
            jumblr_importprivkey(myinfo,coin,wifstr);
        jaddstr(retjson,"KMDdeposit",KMDaddr);
        jaddstr(retjson,"BTCdeposit",BTCaddr);
        if ( (coinbtc= iguana_coinfind("BTC")) != 0 )
        {
            bitcoin_priv2wif(wifstr,privkey,coinbtc->chain->wiftype);
            if ( coinbtc->FULLNODE < 0 )
                jumblr_importprivkey(myinfo,coinbtc,wifstr);
            jaddnum(retjson,"BTCdeposits",dstr(jumblr_balance(myinfo,coinbtc,BTCaddr)));
        }
        privkey = jumblr_privkey(myinfo,BTCaddr,0,KMDaddr,"");
        smartaddress_add(myinfo,privkey,"jumblr","KMD",0.,0.);
        myinfo->jumblr_pubkey = curve25519(privkey,curve25519_basepoint9());
        jaddstr(retjson,"KMDjumblr",KMDaddr);
        jaddstr(retjson,"BTCjumblr",BTCaddr);
        if ( coinbtc != 0 )
            jaddnum(retjson,"BTCjumbled",dstr(jumblr_balance(myinfo,coinbtc,BTCaddr)));
        if ( (smartaddrs= InstantDEX_smartaddresses(myinfo,0,0,0)) != 0 )
        {
            if ( (tmp= cJSON_Parse(smartaddrs)) != 0 )
                jadd(retjson,"smartaddresses",tmp);
            free(smartaddrs);
        }
        return(jprint(retjson,1));
    }
}

ZERO_ARGS(jumblr,runsilent)
{
    myinfo->runsilent = 1;
    return(clonestr("{\"result\":\"success\",\"mode\":\"runsilent\"}"));
}

ZERO_ARGS(jumblr,totransparent)
{
    myinfo->runsilent = 0;
    return(clonestr("{\"result\":\"success\",\"mode\":\"totransparent\"}"));
}

ZERO_ARGS(jumblr,status)
{
    cJSON *retjson; char KMDaddr[64],BTCaddr[64]; struct jumblr_item *ptr,*tmp; struct iguana_info *coinbtc; int64_t received,deposited,jumblred,step_t2z,step_z2z,step_z2t,finished,pending,maxval,minval;
    if ( strcmp(coin->symbol,"KMD") == 0 && coin->FULLNODE < 0 && myinfo->jumblr_passphrase[0] != 0 )
    {
        jumblr_opidsupdate(myinfo,coin);
        retjson = cJSON_CreateObject();
        step_t2z = step_z2z = step_z2t = deposited = finished = pending = 0;
        jumblr_privkey(myinfo,BTCaddr,0,KMDaddr,JUMBLR_DEPOSITPREFIX);
        jaddstr(retjson,"mode",myinfo->runsilent == 0 ? "totransparent" : "runsilent");
        jaddstr(retjson,"KMDdeposit",KMDaddr);
        jaddstr(retjson,"BTCdeposit",BTCaddr);
        if ( (coinbtc= iguana_coinfind("BTC")) != 0 )
            jaddnum(retjson,"BTCdeposits",dstr(jumblr_balance(myinfo,coinbtc,BTCaddr)));
        received = jumblr_receivedby(myinfo,coin,KMDaddr);
        deposited = jumblr_balance(myinfo,coin,KMDaddr);
        jumblr_privkey(myinfo,BTCaddr,0,KMDaddr,"");
        jaddstr(retjson,"KMDjumblr",KMDaddr);
        jaddstr(retjson,"BTCjumblr",BTCaddr);
        if ( coinbtc != 0 )
            jaddnum(retjson,"BTCjumbled",dstr(jumblr_balance(myinfo,coinbtc,BTCaddr)));
        finished = jumblr_receivedby(myinfo,coin,KMDaddr);
        jumblred = jumblr_balance(myinfo,coin,KMDaddr);
        HASH_ITER(hh,myinfo->jumblrs,ptr,tmp)
        {
            if ( strlen(ptr->src) >= 40 )
            {
                if ( strlen(ptr->dest) >= 40 )
                    step_z2z += ptr->amount;
                else step_z2t += ptr->amount;
            } else step_t2z += ptr->amount;
        }
        jaddstr(retjson,"result","success");
        jaddnum(retjson,"deposits",dstr(deposited));
        jaddnum(retjson,"t_to_z",dstr(step_t2z));
        jaddnum(retjson,"z_to_z",dstr(step_z2z));
        jaddnum(retjson,"z_to_t",dstr(step_z2t));
        maxval = MAX(step_t2z,MAX(step_z2z,step_z2t));
        minval = MIN(step_t2z,MIN(step_z2z,step_z2t));
        if ( maxval > minval )
        {
            pending = (maxval - minval);
            if ( pending < finished*.1 )
                pending = 0;
        }
        jaddnum(retjson,"pending",dstr(pending));
        jaddnum(retjson,"jumbled",dstr(jumblred));
        jaddnum(retjson,"received",dstr(received));
        jaddnum(retjson,"finished",dstr(finished));
        return(jprint(retjson,1));
    }
    else
    {
        printf("(%s) (%s) %d\n",coin->symbol,myinfo->jumblr_passphrase,coin->FULLNODE);
        return(clonestr("{\"error\":\"jumblr status no passphrase or no native komodod\"}"));
    }
}

HASH_ARRAY_STRING(basilisk,balances,hash,vals,hexstr)
{
    char *retstr=0,*symbol; uint32_t basilisktag; struct basilisk_item *ptr,Lptr; int32_t timeoutmillis;
    if ( myinfo->NOTARY.RELAYID >= 0 )
        return(clonestr("{\"error\":\"special relays only do OUT and MSG\"}"));
    if ( vals == 0 )
        return(clonestr("{\"error\":\"need vals object\"}"));
    if ( (symbol= jstr(vals,"symbol")) != 0 || (symbol= jstr(vals,"coin")) != 0 )
        coin = iguana_coinfind(symbol);
    if ( jobj(vals,"history") == 0 )
        jaddnum(vals,"history",3);
    if ( jobj(vals,"fanout") == 0 )
        jaddnum(vals,"fanout",MAX(8,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)+1));
    if ( jobj(vals,"numrequired") == 0 )
        jaddnum(vals,"numrequired",MIN(juint(vals,"fanout")/2,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)));
    if ( jobj(vals,"addresses") == 0 )
    {
        jadd(vals,"addresses",iguana_getaddressesbyaccount(myinfo,coin,"*"));
        //printf("added all %s addresses: %s\n",coin->symbol,jprint(vals,0));
    } //else printf("have addresses.(%s)\n",jprint(jobj(vals,"addresses"),0));
    if ( (basilisktag= juint(vals,"basilisktag")) == 0 )
        basilisktag = rand();
    if ( (timeoutmillis= juint(vals,"timeout")) <= 0 )
        timeoutmillis = BASILISK_TIMEOUT;
    if ( coin != 0 )
    {
        if ( coin->FULLNODE > 0 || coin->VALIDATENODE > 0 || coin->notarychain >= 0 )
        {
            if ( (ptr= basilisk_bitcoinbalances(&Lptr,myinfo,coin,remoteaddr,basilisktag,timeoutmillis,vals)) != 0 )
            {
                retstr = ptr->retstr, ptr->retstr = 0;
                ptr->finished = OS_milliseconds() + 10000;
                return(retstr);
            }
            return(clonestr("{\"error\":\"no result\"}"));
        }
    } else printf("no coin\n");
    if ( (retstr= basilisk_standardservice("BAL",myinfo,0,hash,vals,hexstr,1)) != 0 )
    {
        basilisk_unspents_process(myinfo,coin,retstr);
    }
    return(retstr);
}

HASH_ARRAY_STRING(basilisk,history,hash,vals,hexstr)
{
    int64_t total = 0; int32_t i,n; char *symbol; cJSON *retjson,*unspents,*spends,*array; //struct basilisk_spend *s; struct basilisk_unspent *bu; int32_t i; struct iguana_waccount *wacct,*tmp; struct iguana_waddress *waddr,*tmp2; 
    if ( vals == 0 )
        return(clonestr("{\"error\":\"need vals object\"}"));
    //if ( coin == 0 )
    {
        if ( (symbol= jstr(vals,"symbol")) != 0 || (symbol= jstr(vals,"coin")) != 0 )
            coin = iguana_coinfind(symbol);
    }
    if ( coin == 0 )
        return(clonestr("{\"error\":\"couldnt find coin\"}"));
    unspents = cJSON_CreateArray();
    spends = cJSON_CreateArray();
    portable_mutex_lock(&myinfo->bu_mutex);
    //HASH_ITER(hh,myinfo->wallet,wacct,tmp)
    {
        //HASH_ITER(hh,wacct->waddr,waddr,tmp2)
        {
            if ( myinfo->Cunspents != 0 )
            {
                //printf("Cunspents.(%s)\n",jprint(waddr->Cunspents,0));
                if ( (array= jobj(myinfo->Cunspents,coin->symbol)) != 0 )
                {
                    if ( (n= cJSON_GetArraySize(array)) > 0 )
                    {
                        for (i=0; i<n; i++)
                            total += jdouble(jitem(array,i),"amount") * SATOSHIDEN;
                    }
                    jaddi(unspents,jduplicate(array));
                }
            }
            if ( myinfo->Cspends != 0 )
            {
                //printf("Cspends.(%s)\n",jprint(waddr->Cspends,0));
                if ( (array= jobj(myinfo->Cspends,coin->symbol)) != 0  )
                    jaddi(spends,jduplicate(array));
            }
        }
    }
    portable_mutex_unlock(&myinfo->bu_mutex);
    retjson = cJSON_CreateObject();
    jaddstr(retjson,"result","success");
    jadd(retjson,"unspents",unspents);
    jadd(retjson,"spends",spends);
    jaddstr(retjson,"coin",coin->symbol);
    jaddnum(retjson,"balance",dstr(total));
    //printf("return history balance %s %.8f\n",coin->symbol,dstr(total));
    return(jprint(retjson,1));
}

INT_ARG(passthru,paxfiats,mask)
{
    if ( mask == 0 )
        mask = -1;
    komodo_assetcoins(-1,mask);
    return(clonestr("{\"result\":\"success\"}"));
}

INT_ARG(basilisk,paxfiats,mask)
{
    if ( mask == 0 )
        mask = -1;
    komodo_assetcoins(0,mask);
    return(clonestr("{\"result\":\"success\"}"));
}

INT_ARG(iguana,paxfiats,mask)
{
    if ( mask == 0 )
        mask = -1;
    komodo_assetcoins(1,mask);
    return(clonestr("{\"result\":\"success\"}"));
}

int32_t utxocmp(cJSON *utxo,cJSON *utxo2)
{
    bits256 txid,txid2; int32_t vout=-1,vout2=-1;
    //printf("cmp (%s) vs (%s)\n",jprint(utxo,0),jprint(utxo2,0));
    txid = jbits256(utxo,"txid");
    vout = jint(utxo,"vout");
    txid2 = jbits256(utxo2,"txid");
    vout2 = jint(utxo2,"vout");
    if ( bits256_cmp(txid,txid2) == 0 && vout == vout2 )
        return(0);
    else return(-1);
}

ZERO_ARGS(basilisk,cancelrefresh)
{
    myinfo->cancelrefresh = 1;
    return(clonestr("{\"result\":\"refresh cancel started\"}"));
}

TWO_STRINGS(basilisk,refresh,symbol,address)
{
    cJSON *array=0,*array2=0,*array3,*item,*item2; char *retstr; int32_t i,j,n,m,vout; bits256 txid;
    myinfo->cancelrefresh = 0;
    if ( symbol != 0 && iguana_isnotarychain(symbol) >= 0 && address != 0 && address[0] != 0 )
    {
        if ( (retstr= _dex_listunspent(myinfo,symbol,address)) != 0 )
        {
            array = cJSON_Parse(retstr);
            free(retstr);
        }
        if ( (retstr= _dex_listunspent2(myinfo,symbol,address)) != 0 )
        {
            if ( array == 0 )
                array = cJSON_Parse(retstr);
            else array2 = cJSON_Parse(retstr);
            free(retstr);
        }
        if ( array != 0 && array2 != 0 ) // merge
        {
            m = cJSON_GetArraySize(array2);
            array3 = jduplicate(array);
            n = cJSON_GetArraySize(array3);
            //printf("MERGE %s and %s\n",jprint(array,0),jprint(array2,0));
            for (j=0; j<m; j++)
            {
                if ( myinfo->cancelrefresh != 0 )
                    break;
                item2 = jitem(array2,j);
                for (i=0; i<n; i++)
                    if ( utxocmp(jitem(array,i),item2) == 0 )
                        break;
                if ( i == n )
                {
                    //printf("FOUND NEW %s\n",jprint(item2,0));
                    jaddi(array3,jduplicate(item2));
                }
            }
            free_json(array);
            free_json(array2), array2 = 0;
            array = array3, array3 = 0;
        }
        if ( array != 0 ) // gettxout
        {
            n = cJSON_GetArraySize(array);
            array3 = cJSON_CreateArray();
            for (i=0; i<n; i++)
            {
                if ( myinfo->cancelrefresh != 0 )
                    break;
                item = jitem(array,i);
                txid = jbits256(item,"txid");
                vout = jint(item,"vout");
                if ( (retstr= _dex_gettxout(myinfo,symbol,txid,vout)) != 0 )
                {
                    if ( (item2= cJSON_Parse(retstr)) != 0 )
                    {
                        if ( jdouble(item2,"value") > 0 )
                        {
                            jaddbits256(item2,"txid",txid);
                            jaddnum(item2,"vout",vout);
                            jaddnum(item2,"amount",jdouble(item2,"value"));
                            //printf("%s\n",jprint(item2,0));
                            jaddi(array3,item2);
                        }
                        else free_json(item2);
                    }
                    free(retstr);
                }
            }
            free_json(array);
            myinfo->cancelrefresh = 0;
            return(jprint(array3,1));
        } else return(clonestr("[]"));
    }
    myinfo->cancelrefresh = 0;
    return(clonestr("{\"error\":\"invalid coin or address specified\"}"));
}

STRING_ARRAY_OBJ_STRING(basilisk,utxorawtx,symbol,utxos,vals,ignore)
{
    char *destaddr,*changeaddr; int64_t satoshis,txfee; int32_t completed,sendflag,timelock;
    timelock = jint(vals,"timelock");
    sendflag = jint(vals,"sendflag");
    satoshis = jdouble(vals,"amount") * SATOSHIDEN;
    destaddr = jstr(vals,"destaddr");
    changeaddr = jstr(vals,"changeaddr");
    if ( destaddr != 0 && changeaddr != 0 && symbol != 0 && (coin= iguana_coinfind(symbol)) != 0 )
    {
        txfee = jdouble(vals,"txfee") * SATOSHIDEN;
        return(iguana_utxorawtx(myinfo,coin,timelock,destaddr,changeaddr,&satoshis,1,txfee,&completed,sendflag,utxos,0));
    }
    return(clonestr("{\"error\":\"invalid coin or address specified\"}"));
}

HASH_ARRAY_STRING(basilisk,utxocombine,ignore,vals,symbol)
{
    char *coinaddr,*retstr=0; cJSON *utxos; int64_t satoshis,limit,txfee; int32_t maxvins,completed,sendflag,timelock;
    timelock = 0;
    if ( (maxvins= jint(vals,"maxvins")) == 0 )
        maxvins = 20;
    sendflag = jint(vals,"sendflag");
    coinaddr = jstr(vals,"coinaddr");
    limit = jdouble(vals,"maxamount") * SATOSHIDEN;
    if ( limit > 0 && symbol != 0 && symbol[0] != 0 && (utxos= basilisk_utxosweep(myinfo,symbol,&satoshis,limit,maxvins,coinaddr)) != 0 && cJSON_GetArraySize(utxos) > 0 )
    {
        if ( coinaddr != 0 && symbol != 0 && (coin= iguana_coinfind(symbol)) != 0 )
        {
            txfee = jdouble(vals,"txfee") * SATOSHIDEN;
            retstr = iguana_utxorawtx(myinfo,coin,timelock,coinaddr,coinaddr,&satoshis,1,txfee,&completed,sendflag,utxos,0);
        }
        free_json(utxos);
    }
    if ( retstr == 0 )
        return(clonestr("{\"error\":\"invalid coin or address specified or no available utxos\"}"));
    return(retstr);
}

//int64_t iguana_verifytimelock(struct supernet_info *myinfo,struct iguana_info *coin,uint32_t timelocked,char *destaddr,bits256 txid,int32_t vout)
THREE_STRINGS_AND_DOUBLE(tradebot,aveprice,comment,base,rel,basevolume)
{
    double retvals[4],aveprice; cJSON *retjson = cJSON_CreateObject();
    aveprice = instantdex_avehbla(myinfo,retvals,base,rel,basevolume);
    jaddstr(retjson,"result","success");
    jaddnum(retjson,"aveprice",aveprice);
    jaddnum(retjson,"avebid",retvals[0]);
    jaddnum(retjson,"bidvol",retvals[1]);
    jaddnum(retjson,"aveask",retvals[2]);
    jaddnum(retjson,"askvol",retvals[3]);
    return(jprint(retjson,1));
}

ZERO_ARGS(InstantDEX,allcoins)
{
    struct iguana_info *tmp; cJSON *native,*notarychains,*basilisk,*virtual,*full,*retjson = cJSON_CreateObject();
    full = cJSON_CreateArray();
    native = cJSON_CreateArray();
    basilisk = cJSON_CreateArray();
    virtual = cJSON_CreateArray();
    notarychains = cJSON_CreateArray();
    HASH_ITER(hh,myinfo->allcoins,coin,tmp)
    {
        if ( coin->FULLNODE < 0 )
            jaddistr(native,coin->symbol);
        //else if ( coin->virtualchain != 0 )
        //    jaddistr(virtual,coin->symbol);
        else if ( coin->FULLNODE > 0 )//|| coin->VALIDATENODE > 0 )
            jaddistr(full,coin->symbol);
        //else if ( coin->notarychain >= 0 )
        //    jaddistr(notarychains,coin->symbol);
        else jaddistr(basilisk,coin->symbol);
    }
    jadd(retjson,"native",native);
    jadd(retjson,"basilisk",basilisk);
    jadd(retjson,"full",full);
    //jadd(retjson,"virtual",virtual);
    //jadd(retjson,"notarychains",notarychains);
    return(jprint(retjson,1));
}

STRING_ARG(InstantDEX,available,source)
{
    uint64_t total = 0; int32_t i,n=0; char coinaddr[64]; cJSON *item,*unspents,*retjson = 0;
    if ( source != 0 && source[0] != 0 && (coin= iguana_coinfind(source)) != 0 )
    {
        if ( myinfo->expiration != 0 )
        {
            bitcoin_address(coinaddr,coin->chain->pubtype,myinfo->persistent_pubkey33,33);
            if ( (unspents= basilisk_unspents(myinfo,coin,coinaddr)) != 0 )
            {
                //printf("available.(%s)\n",jprint(unspents,0));
                if ( (n= cJSON_GetArraySize(unspents)) > 0 )
                {
                    for (i=0; i<n; i++)
                    {
                        item = jitem(unspents,i);
                        //if ( is_cJSON_True(jobj(item,"spendable")) != 0 )
                        total += jdouble(item,"amount") * SATOSHIDEN;
                        //printf("(%s) -> %.8f\n",jprint(item,0),dstr(total));
                    }
                }
                free_json(unspents);
            }
            retjson = cJSON_CreateObject();
            jaddnum(retjson,"result",dstr(total));
            printf(" n.%d total %.8f (%s)\n",n,dstr(total),jprint(retjson,0));
            return(jprint(retjson,1));
        }
        printf("InstantDEX_available: need to unlock wallet\n");
        return(clonestr("{\"error\":\"need to unlock wallet\"}"));
    }
    printf("InstantDEX_available: %s is not active\n",source!=0?source:"");
    return(clonestr("{\"error\":\"specified coin is not active\"}"));
}

HASH_ARRAY_STRING(InstantDEX,request,hash,vals,hexstr)
{
    uint8_t serialized[512]; bits256 privkey; char buf[512],BTCaddr[64],KMDaddr[64]; struct basilisk_request R; int32_t jumblr,iambob,optionhours; cJSON *reqjson; uint32_t datalen=0,DEX_channel; struct iguana_info *bobcoin,*alicecoin;
    myinfo->DEXactive = (uint32_t)time(NULL) + 3*BASILISK_TIMEOUT + 60;
    jadd64bits(vals,"minamount",jdouble(vals,"minprice") * jdouble(vals,"amount") * SATOSHIDEN);
    if ( jobj(vals,"desthash") == 0 )
        jaddbits256(vals,"desthash",hash);
    jadd64bits(vals,"satoshis",jdouble(vals,"amount") * SATOSHIDEN);
    jadd64bits(vals,"destsatoshis",jdouble(vals,"destamount") * SATOSHIDEN);
    jaddnum(vals,"timestamp",time(NULL));
    if ( (jumblr= jint(vals,"usejumblr")) != 0 )
        privkey = jumblr_privkey(myinfo,BTCaddr,0,KMDaddr,jumblr == 1 ? JUMBLR_DEPOSITPREFIX : "");
    else privkey = myinfo->persistent_priv;
    hash = curve25519(privkey,curve25519_basepoint9());
    if ( jobj(vals,"srchash") == 0 )
        jaddbits256(vals,"srchash",hash);
    printf("service.(%s)\n",jprint(vals,0));
    memset(&R,0,sizeof(R));
    if ( basilisk_request_create(&R,vals,hash,juint(vals,"timestamp"),juint(vals,"DEXselector")) == 0 )
    {
        iambob = bitcoin_coinptrs(hash,&bobcoin,&alicecoin,R.src,R.dest,privkey,GENESIS_PUBKEY);
        if ( (optionhours= jint(vals,"optionhours")) != 0 )
        {
            printf("iambob.%d optionhours.%d R.requestid.%u vs calc %u, q.%u\n",iambob,R.optionhours,R.requestid,basilisk_requestid(&R),R.quoteid);
            if ( iambob != 0 && optionhours > 0 )
            {
                sprintf(buf,"{\"error\":\"illegal call option request hours.%d when iambob.%d\"}",optionhours,iambob);
                printf("ERROR.(%s)\n",buf);
                return(clonestr(buf));
            }
            else if ( iambob == 0 && optionhours < 0 )
            {
                sprintf(buf,"{\"error\":\"illegal put option request hours.%d when iambob.%d\"}",optionhours,iambob);
                printf("ERROR.(%s)\n",buf);
                return(clonestr(buf));
            }
        }
        //if ( myinfo->IAMNOTARY != 0 || myinfo->NOTARY.RELAYID >= 0 )
        //    R.relaybits = myinfo->myaddr.myipbits;
        if ( (reqjson= basilisk_requestjson(&R)) != 0 )
            free_json(reqjson);
        datalen = basilisk_rwDEXquote(1,serialized,&R);
        //int32_t i; for (i=0; i<sizeof(R); i++)
        //    printf("%02x",((uint8_t *)&R)[i]);
        printf(" R.requestid.%u vs calc %u, q.%u datalen.%d\n",R.requestid,basilisk_requestid(&R),R.quoteid,datalen);
        basilisk_rwDEXquote(0,serialized,&R);
    } else printf("error creating request\n");
    if ( datalen > 0 )
    {
        uint32_t msgid;//,crc=0,crcs[2],numiters = 0; uint8_t buf[4096];
        memset(hash.bytes,0,sizeof(hash));
        msgid = (uint32_t)time(NULL);
        DEX_channel = 'D' + ((uint32_t)'E' << 8) + ((uint32_t)'X' << 16);
        myinfo->DEXtrades++; // not exact but allows a one side initiated self-trade
        basilisk_channelsend(myinfo,hash,hash,DEX_channel,msgid,serialized,datalen,60);
        sleep(3);
        /*while ( numiters < 10 && (crc= basilisk_crcsend(myinfo,0,buf,sizeof(buf),hash,myinfo->myaddr.persistent,DEX_channel,msgid,serialized,datalen,crcs)) == 0 )
         {
         //printf("didnt get back what was sent\n");
         sleep(3);
         basilisk_channelsend(myinfo,myinfo->myaddr.persistent,hash,DEX_channel,msgid,serialized,datalen,60);
         numiters++;
         }*/
        //if ( crc != 0 )//basilisk_channelsend(myinfo,R.srchash,R.desthash,DEX_channel,(uint32_t)time(NULL),serialized,datalen,30) == 0 )
        return(clonestr("{\"result\":\"DEX message sent\"}"));
        //else return(clonestr("{\"error\":\"DEX message couldnt be sent\"}"));
    }
    return(clonestr("{\"error\":\"DEX message not sent\"}"));
}

INT_ARG(InstantDEX,automatched,requestid)
{
    // return quoteid
    myinfo->DEXactive = (uint32_t)time(NULL) + INSTANTDEX_LOCKTIME;
    return(clonestr("{\"result\":\"automatched not yet\"}"));
}

int32_t InstantDEX_incoming_func(struct supernet_info *myinfo,void *ptr,uint8_t *data,int32_t datalen)
{
    //int32_t i;
    //for (i=0; i<datalen; i++)
    //    printf("%02x",data[i]);
    //printf(" <- incoming\n");
    return(0);
}

int32_t InstantDEX_process_channelget(struct supernet_info *myinfo,void *ptr,int32_t (*internal_func)(struct supernet_info *myinfo,void *ptr,uint8_t *data,int32_t datalen),uint32_t channel,uint32_t msgid,uint8_t *data,int32_t datalen,uint32_t expiration,uint32_t duration)
{
    return((*internal_func)(myinfo,ptr,data,datalen));
}

INT_ARG(InstantDEX,incoming,requestid)
{
    static uint32_t counter;
    cJSON *retjson,*retarray; bits256 zero; uint32_t DEX_channel,msgid,now,n = myinfo->numsmartaddrs+1; int32_t retval,width,drift=3; bits256 pubkey; uint8_t data[32768];
    now = (uint32_t)time(NULL);
    memset(&zero,0,sizeof(zero));
    width = (now - myinfo->DEXpoll) + 2*drift;
    if ( width < (drift+1) )
        width = 2*drift+1;
    else if ( width > 64 )
        width = 64;
    if ( (counter % n) == n-1 )
        pubkey = myinfo->myaddr.persistent;
    else pubkey = myinfo->smartaddrs[counter % n].pubkey;
    counter++;
    myinfo->DEXpoll = now;
    retjson = cJSON_CreateObject();
    DEX_channel = 'D' + ((uint32_t)'E' << 8) + ((uint32_t)'X' << 16);
    msgid = (uint32_t)time(NULL) + drift;
    if ( (retarray= basilisk_channelget(myinfo,zero,pubkey,DEX_channel,msgid,width)) != 0 )
    {
        //printf("GOT.(%s)\n",jprint(retarray,0));
        if ( (retval= basilisk_process_retarray(myinfo,0,InstantDEX_process_channelget,data,sizeof(data),DEX_channel,msgid,retarray,InstantDEX_incoming_func)) > 0 )
        {
            jaddstr(retjson,"result","success");
        } else jaddstr(retjson,"error","cant process InstantDEX retarray");
        jadd(retjson,"responses",retarray);
    }
    else
    {
        jaddstr(retjson,"error","cant do InstantDEX channelget");
        //char str[65]; printf("error channelget %s %x\n",bits256_str(str,pubkey),msgid);
    }
    return(jprint(retjson,1));
}

/*TWO_INTS(InstantDEX,swapstatus,requestid,quoteid)
 {
 cJSON *vals; char *retstr;
 myinfo->DEXactive = (uint32_t)time(NULL) + INSTANTDEX_LOCKTIME;
 //if ( myinfo->IAMLP != 0 )
 //    return(basilisk_respond_swapstatus(myinfo,myinfo->myaddr.persistent,requestid,quoteid));
 //else
 {
 vals = cJSON_CreateObject();
 jaddnum(vals,"requestid",(uint32_t)requestid);
 jaddnum(vals,"quoteid",(uint32_t)quoteid);
 jaddbits256(vals,"hash",myinfo->myaddr.persistent);
 retstr = basilisk_standardservice("SWP",myinfo,0,myinfo->myaddr.persistent,vals,"",1);
 free_json(vals);
 return(retstr);
 }
 }*/

TWO_INTS(InstantDEX,accept,requestid,quoteid)
{
    cJSON *vals; char *retstr;
    myinfo->DEXactive = (uint32_t)time(NULL) + INSTANTDEX_LOCKTIME;
    if ( myinfo->IAMLP != 0 || myinfo->dexsock >= 0 || myinfo->subsock >= 0 )
        return(basilisk_respond_accept(myinfo,myinfo->persistent_priv,requestid,quoteid,&myinfo->DEXaccept));
    else
    {
        vals = cJSON_CreateObject();
        jaddnum(vals,"quoteid",(uint32_t)quoteid);
        jaddnum(vals,"requestid",(uint32_t)requestid);
        retstr = basilisk_standardservice("ACC",myinfo,0,myinfo->myaddr.persistent,vals,"",1);
        free_json(vals);
        return(retstr);
    }
}

ZERO_ARGS(InstantDEX,init)
{
    basilisk_swaps_init(myinfo);
    return(clonestr("{\"result\":\"success\"}"));
}

ZERO_ARGS(InstantDEX,getswaplist)
{
    return(basilisk_swaplist(myinfo));
}

DOUBLE_ARG(InstantDEX,DEXratio,ratio)
{
    if ( ratio < 0.95 || ratio > 1.01 )
        return(clonestr("{\"result\":\"error\",\"description\":\"DEXratio must be between 0.95 and 1.01\"}"));
    myinfo->DEXratio = ratio;
    return(clonestr("{\"result\":\"success\"}"));
}
#include "../includes/iguana_apiundefs.h"