You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

543 lines
22 KiB

/******************************************************************************
* Copyright © 2014-2016 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"
char *basilisk_finish(struct basilisk_item *ptr,int32_t besti,char *errstr)
{
int32_t i; char *retstr = 0;
for (i=0; i<ptr->numresults; i++)
{
if ( besti >= 0 && i != besti )
{
if ( ptr->results[i] != 0 )
free(ptr->results[i]);
} else retstr = ptr->results[i];
ptr->results[i] = 0;
}
if ( retstr == 0 )
retstr = clonestr(errstr);
ptr->retstr = retstr;
ptr->finished = (uint32_t)time(NULL);
return(retstr);
}
cJSON *basilisk_json(struct supernet_info *myinfo,cJSON *hexjson,uint32_t basilisktag,int32_t timeout)
{
char *str,*buf; cJSON *retjson;
jaddnum(hexjson,"basilisktag",basilisktag);
str = jprint(hexjson,0);
buf = malloc(strlen(str)*2 + 1);
init_hexbytes_noT(buf,(uint8_t *)str,(int32_t)strlen(str));
free(str);
retjson = cJSON_CreateObject();
jaddstr(retjson,"hexmsg",buf);
free(buf);
jaddstr(retjson,"agent","SuperNET");
jaddstr(retjson,"method","DHT");
jaddnum(retjson,"request",1);
jaddnum(retjson,"plaintext",1);
jaddbits256(retjson,"categoryhash",myinfo->basilisk_category);
jaddnum(retjson,"timeout",timeout);
return(retjson);
}
char *basilisk_results(uint32_t basilisktag,cJSON *valsobj)
{
cJSON *resultobj = cJSON_CreateObject();
jadd(resultobj,"vals",valsobj);
jaddstr(resultobj,"agent","basilisk");
jaddstr(resultobj,"method","result");
jaddnum(resultobj,"plaintext",1);
jaddnum(resultobj,"basilisktag",basilisktag);
return(jprint(resultobj,1));
}
/*cJSON *basilisk_resultsjson(struct supernet_info *myinfo,char *symbol,char *remoteaddr,uint32_t basilisktag,int32_t timeoutmillis,char *retstr)
{
cJSON *hexjson=0,*retjson=0;
if ( retstr != 0 )
{
if ( remoteaddr != 0 && remoteaddr[0] != 0 )
{
hexjson = cJSON_CreateObject();
jaddstr(hexjson,"agent","basilisk");
jaddstr(hexjson,"method","result");
if ( (retjson= cJSON_Parse(retstr)) != 0 )
jadd(hexjson,"vals",retjson);
retjson = basilisk_json(myinfo,hexjson,basilisktag,timeoutmillis);
free_json(hexjson);
printf("resultsjson.(%s)\n",jprint(retjson,0));
}
else // local request
retjson = cJSON_Parse(retstr);
}
return(retjson);
}*/
#include "basilisk_bitcoin.c"
#include "basilisk_nxt.c"
#include "basilisk_ether.c"
#include "basilisk_waves.c"
#include "basilisk_lisk.c"
int32_t basilisk_submit(struct supernet_info *myinfo,cJSON *reqjson,int32_t timeout,int32_t fanout,struct basilisk_item *ptr)
{
int32_t i,j,k,l,r2,r,n; struct iguana_peer *addr; struct iguana_info *coin; char *reqstr; cJSON *tmpjson;
tmpjson = basilisk_json(myinfo,reqjson,ptr->basilisktag,timeout);
reqstr = jprint(tmpjson,1);
printf("basilisk_submit.(%s)\n",reqstr);
if ( fanout <= 0 )
fanout = BASILISK_MINFANOUT;
else if ( fanout > BASILISK_MAXFANOUT )
fanout = BASILISK_MAXFANOUT;
r2 = rand();
for (l=n=0; l<IGUANA_MAXCOINS; l++)
{
i = (l + r2) % IGUANA_MAXCOINS;
if ( (coin= Coins[i]) != 0 )
{
r = rand();
for (k=0; k<IGUANA_MAXPEERS; k++)
{
j = (k + r) % IGUANA_MAXPEERS;
if ( (addr= &coin->peers.active[j]) != 0 && addr->supernet != 0 && addr->usock >= 0 )
{
ptr->submit = (uint32_t)time(NULL);
printf("submit to (%s)\n",addr->ipaddr);
iguana_send_supernet(addr,reqstr,0);
if ( n++ > fanout )
break;
}
}
}
}
free(reqstr);
return(n);
}
struct basilisk_item *basilisk_issueremote(struct supernet_info *myinfo,char *methodstr,char *symbol,cJSON *vals,int32_t timeoutmillis,int32_t fanout,int32_t minresults,uint32_t basilisktag)
{
double expiration; struct basilisk_item *ptr; cJSON *hexjson;
if ( basilisktag == 0 )
basilisktag = rand();
hexjson = cJSON_CreateObject();
jaddstr(hexjson,"agent","basilisk");
jaddnum(hexjson,"basilisktag",basilisktag);
jaddstr(hexjson,"method",methodstr);
jaddstr(hexjson,"activecoin",symbol);
if ( vals != 0 )
jadd(hexjson,"vals",jduplicate(vals));
printf("issue.(%s)\n",jprint(hexjson,0));
ptr = calloc(1,sizeof(*ptr));
ptr->basilisktag = basilisktag;
ptr->numrequired = minresults;
queue_enqueue("submitQ",&myinfo->basilisks.submitQ,&ptr->DL,0);
if ( basilisk_submit(myinfo,hexjson,timeoutmillis,fanout,ptr) > 0 )
{
if ( timeoutmillis > 0 )
{
expiration = OS_milliseconds() + ((timeoutmillis == 0) ? BASILISK_TIMEOUT : timeoutmillis);
while ( OS_milliseconds() < expiration && ptr->finished == 0 && ptr->numresults < ptr->numrequired )
usleep(timeoutmillis/100 + 1);
}
}
free_json(hexjson);
return(ptr);
}
void basilisk_functions(struct iguana_info *coin)
{
switch ( coin->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_besti(struct basilisk_item *ptr)
{
int32_t i,besti = -1; double metric,bestmetric;
if ( ptr->metricdir > 0 )
bestmetric = -1.;
else if ( ptr->metricdir < 0 )
bestmetric = 1.;
else bestmetric = 0.;
for (i=0; i<ptr->numresults; i++)
{
if ( (metric= ptr->metrics[i]) != 0. )
{
if ( (ptr->metricdir < 0 && (bestmetric > 0. || metric < bestmetric)) || (ptr->metricdir > 0 && (bestmetric < 0. || metric > bestmetric)) || (ptr->metricdir == 0 && bestmetric == 0.) )
{
bestmetric = metric;
besti = i;
}
}
}
if ( besti >= 0 )
{
for (i=0; i<ptr->numresults; i++)
if ( fabs(ptr->metrics[i] - bestmetric) < SMALLVAL )
ptr->numexact++;
}
return(besti);
}
char *basilisk_block(struct supernet_info *myinfo,struct iguana_info *coin,char *remoteaddr,struct basilisk_item *Lptr,struct basilisk_item *ptr)
{
int32_t besti,i,j,numvalid; char *retstr = 0,*errstr; struct iguana_peer *addr;
if ( ptr == Lptr )
{
if ( (retstr= Lptr->retstr) == 0 )
retstr = clonestr("{\"result\":\"null return from local basilisk_issuecmd\"}");
}
else
{
if ( ptr->numrequired == 0 )
ptr->numrequired = 1;
while ( OS_milliseconds() < ptr->expiration )
{
if ( (numvalid= ptr->numresults) >= ptr->numrequired )
{
for (i=numvalid=0; i<ptr->numresults; i++)
{
if ( ptr->metrics[i] != 0. )
numvalid++;
}
}
if ( numvalid < ptr->numrequired )
{
usleep(10000);
continue;
}
if ( ptr->uniqueflag == 0 && ptr->numexact <= (ptr->numresults >> 1) )
besti = -1, errstr = "{\"error\":\"basilisk non-consensus results\"}";
else besti = basilisk_besti(ptr), errstr = "{\"error\":\"basilisk no valid results\"}";
if ( (retstr= basilisk_finish(ptr,besti,errstr)) != 0 && remoteaddr != 0 && remoteaddr[0] != 0 && strcmp(remoteaddr,"127.0.0.1") != 0 )
{
for (j=0; j<IGUANA_MAXCOINS; j++)
{
if ( (coin= Coins[j]) == 0 )
continue;
for (i=0; i<IGUANA_MAXPEERS; i++)
{
if ( (addr= &coin->peers.active[i]) != 0 && addr->usock >= 0 )
{
if ( addr->supernet != 0 && strcmp(addr->ipaddr,remoteaddr) == 0 )
{
printf("send back.%d basilisk_result addr->supernet.%u to (%s).%d\n",(int32_t)strlen(retstr),addr->supernet,addr->ipaddr,addr->A.port);
iguana_send_supernet(addr,retstr,0);
return(retstr);
}
}
if ( 0 && addr->ipbits != 0 )
printf("i.%d (%s) vs (%s) %s\n",i,addr->ipaddr,remoteaddr,coin->symbol);
}
}
}
}
retstr = basilisk_finish(ptr,-1,"{\"error\":\"basilisk timeout\"}");
}
return(retstr);
}
struct basilisk_item *basilisk_issuecmd(struct basilisk_item *Lptr,basilisk_func func,basilisk_metricfunc metricfunc,struct supernet_info *myinfo,char *remoteaddr,uint32_t basilisktag,char *symbol,int32_t timeoutmillis,cJSON *vals)
{
struct iguana_info *coin; struct basilisk_item *ptr;
memset(Lptr,0,sizeof(*Lptr));
if ( (coin= iguana_coinfind(symbol)) != 0 )
{
if ( func != 0 )
{
if ( (ptr= (*func)(Lptr,myinfo,coin,remoteaddr,basilisktag,timeoutmillis,vals)) != 0 )
{
if ( (ptr->metricfunc= metricfunc) != 0 )
ptr->vals = jduplicate(vals);
strcpy(ptr->symbol,symbol);
ptr->expiration = OS_milliseconds() + timeoutmillis;
return(ptr);
}
else Lptr->retstr = clonestr("{\"error\":\"error issuing basilisk command\"}");
} else Lptr->retstr = clonestr("{\"error\":\"null basilisk function\"}");
} else Lptr->retstr = clonestr("{\"error\":\"error missing coin\"}");
return(Lptr);
}
char *basilisk_check(int32_t *timeoutmillisp,uint32_t *basilisktagp,char *symbol,cJSON *vals)
{
if ( symbol != 0 && symbol[0] != 0 && vals != 0 )
{
if ( *basilisktagp == 0 )
*basilisktagp = rand();
if ( (*timeoutmillisp= jint(vals,"timeout")) < 0 )
*timeoutmillisp = BASILISK_TIMEOUT;
return(0);
} else return(clonestr("{\"error\":\"missing activecoin or vals\"}"));
}
char *basilisk_standardcmd(struct supernet_info *myinfo,char *activecoin,char *remoteaddr,uint32_t basilisktag,cJSON *vals,basilisk_func func,basilisk_metricfunc metric)
{
char *retstr; struct basilisk_item *ptr,Lptr; int32_t timeoutmillis; struct iguana_info *coin;
if ( (retstr= basilisk_check(&timeoutmillis,&basilisktag,activecoin,vals)) == 0 )
{
if ( (coin= iguana_coinfind(activecoin)) != 0 )
{
if ( (ptr= basilisk_issuecmd(&Lptr,func,metric,myinfo,remoteaddr,basilisktag,activecoin,timeoutmillis,vals)) != 0 )
return(basilisk_block(myinfo,coin,remoteaddr,&Lptr,ptr));
else return(clonestr("{\"error\":\"null return from basilisk_issuecmd\"}"));
} else return(clonestr("{\"error\":\"couldnt get coin\"}"));
} else return(retstr);
}
#include "../includes/iguana_apidefs.h"
#include "../includes/iguana_apideclares.h"
INT_ARRAY_STRING(basilisk,balances,basilisktag,vals,activecoin)
{
if ( (coin= iguana_coinfind(activecoin)) != 0 )
return(basilisk_standardcmd(myinfo,activecoin,remoteaddr,basilisktag,vals,coin->basilisk_balances,coin->basilisk_balancesmetric));
else return(clonestr("{\"error\":\"cant find missing coin\"}"));
}
INT_ARRAY_STRING(basilisk,value,basilisktag,vals,activecoin)
{
if ( (coin= iguana_coinfind(activecoin)) != 0 )
return(basilisk_standardcmd(myinfo,activecoin,remoteaddr,basilisktag,vals,coin->basilisk_value,coin->basilisk_valuemetric));
else return(clonestr("{\"error\":\"cant find missing coin\"}"));
}
char *basilisk_checkrawtx(int32_t *timeoutmillisp,uint32_t *basilisktagp,char *symbol,cJSON *vals)
{
cJSON *addresses=0; char *changeaddr,*spendscriptstr; int32_t i,n;
*timeoutmillisp = -1;
changeaddr = jstr(vals,"changeaddr");
spendscriptstr = jstr(vals,"spendscript");
addresses = jarray(&n,vals,"addresses");
if ( addresses == 0 || changeaddr == 0 || changeaddr[0] == 0 )
return(clonestr("{\"error\":\"invalid addresses[] or changeaddr\"}"));
else
{
for (i=0; i<n; i++)
if ( strcmp(jstri(addresses,i),changeaddr) == 0 )
return(clonestr("{\"error\":\"changeaddr cant be in addresses[]\"}"));
}
if ( spendscriptstr != 0 && spendscriptstr[0] != 0 )
return(basilisk_check(timeoutmillisp,basilisktagp,symbol,vals));
else return(clonestr("{\"error\":\"missing spendscript\"}"));
}
INT_ARRAY_STRING(basilisk,rawtx,basilisktag,vals,activecoin)
{
char *retstr; struct basilisk_item *ptr,Lptr; int32_t timeoutmillis;
if ( (retstr= basilisk_checkrawtx(&timeoutmillis,(uint32_t *)&basilisktag,activecoin,vals)) == 0 )
{
if ( (ptr= basilisk_issuecmd(&Lptr,coin->basilisk_rawtx,coin->basilisk_rawtxmetric,myinfo,remoteaddr,basilisktag,activecoin,timeoutmillis,vals)) != 0 )
{
if ( (ptr->numrequired= juint(vals,"numrequired")) == 0 )
ptr->numrequired = 1;
ptr->uniqueflag = 1;
return(basilisk_block(myinfo,coin,remoteaddr,&Lptr,ptr));
} else return(clonestr("{\"error\":\"error issuing basilisk rawtx\"}"));
} else return(retstr);
}
INT_AND_ARRAY(basilisk,result,basilisktag,vals)
{
struct basilisk_item *ptr;
if ( vals != 0 )
{
ptr = calloc(1,sizeof(*ptr));
ptr->retstr = jprint(vals,0);
ptr->basilisktag = basilisktag;
printf("Q results vals.(%s)\n",ptr->retstr);
queue_enqueue("resultsQ",&myinfo->basilisks.resultsQ,&ptr->DL,0);
return(clonestr("{\"result\":\"queued basilisk return\"}"));
} else printf("null vals.(%s) or no hexmsg.%p\n",jprint(vals,0),vals);
return(clonestr("{\"error\":\"no hexmsg to return\"}"));
}
#include "../includes/iguana_apiundefs.h"
char *basilisk_hexmsg(struct supernet_info *myinfo,struct category_info *cat,void *ptr,int32_t len,char *remoteaddr) // incoming
{
char *method="",*agent="",*retstr = 0,*tmpstr,*hexmsg; int32_t n; cJSON *array,*json,*valsobj; struct iguana_info *coin=0; uint32_t basilisktag;
array = 0;
if ( (json= cJSON_Parse(ptr)) != 0 )
{
printf("basilisk.(%s)\n",jprint(json,0));
agent = jstr(json,"agent");
method = jstr(json,"method");
if ( agent != 0 && method != 0 && strcmp(agent,"SuperNET") == 0 && strcmp(method,"DHT") == 0 && (hexmsg= jstr(json,"hexmsg")) != 0 )
{
n = (int32_t)(strlen(hexmsg) >> 1);
tmpstr = calloc(1,n + 1);
decode_hex((void *)tmpstr,n,hexmsg);
free_json(json);
printf("NESTED.(%s)\n",tmpstr);
if ( (json= cJSON_Parse(tmpstr)) == 0 )
{
printf("couldnt parse decoded hexmsg.(%s)\n",tmpstr);
free(tmpstr);
return(0);
}
free(tmpstr);
agent = jstr(json,"agent");
method = jstr(json,"method");
}
basilisktag = juint(json,"basilisktag");
if ( strcmp(agent,"basilisk") == 0 && (valsobj= jobj(json,"vals")) != 0 )
{
if ( jobj(valsobj,"coin") != 0 )
coin = iguana_coinfind(jstr(valsobj,"coin"));
else if ( jstr(json,"activecoin") != 0 )
coin = iguana_coinfind(jstr(json,"activecoin"));
if ( coin != 0 )
{
if ( coin->RELAYNODE != 0 || coin->VALIDATENODE != 0 )
{
if ( strcmp(method,"rawtx") == 0 )
retstr = basilisk_rawtx(myinfo,coin,0,remoteaddr,basilisktag,valsobj,coin->symbol);
else if ( strcmp(method,"balances") == 0 )
retstr = basilisk_balances(myinfo,coin,0,remoteaddr,basilisktag,valsobj,coin->symbol);
else if ( strcmp(method,"value") == 0 )
retstr = basilisk_value(myinfo,coin,0,remoteaddr,basilisktag,valsobj,coin->symbol);
if ( retstr != 0 )
free(retstr);
// should automatically send to remote requester
}
else
{
if ( strcmp(method,"result") == 0 )
return(basilisk_result(myinfo,coin,0,remoteaddr,basilisktag,valsobj));
}
}
}
free_json(json);
}
printf("unhandled bitcoin_hexmsg.(%d) from %s (%s/%s)\n",len,remoteaddr,agent,method);
return(retstr);
}
void basilisks_loop(void *arg)
{
basilisk_metricfunc metricfunc; struct basilisk_item *ptr,*tmp,*pending; int32_t i,flag,n; struct supernet_info *myinfo = arg;
//uint8_t *blockspace; struct OS_memspace RAWMEM;
//memset(&RAWMEM,0,sizeof(RAWMEM));
//blockspace = calloc(1,IGUANA_MAXPACKETSIZE);
while ( 1 )
{
//for (i=0; i<IGUANA_MAXCOINS; i++)
// if ( (coin= Coins[i]) != 0 && coin->RELAYNODE == 0 && coin->VALIDATENODE == 0 && coin->active != 0 && coin->chain->userpass[0] != 0 && coin->MAXPEERS == 1 )
// basilisk_bitcoinscan(coin,blockspace,&RAWMEM);
if ( (ptr= queue_dequeue(&myinfo->basilisks.submitQ,0)) != 0 )
{
if ( ptr->finished == 0 )
HASH_ADD(hh,myinfo->basilisks.issued,basilisktag,sizeof(ptr->basilisktag),ptr);
else free(ptr);
continue;
}
else if ( (ptr= queue_dequeue(&myinfo->basilisks.resultsQ,0)) != 0 )
{
HASH_FIND(hh,myinfo->basilisks.issued,&ptr->basilisktag,sizeof(ptr->basilisktag),pending);
if ( pending != 0 )
{
if ( (n= pending->numresults) < sizeof(pending->results)/sizeof(*pending->results) )
{
pending->results[n] = ptr->retstr;
pending->numresults++;
if ( (metricfunc= ptr->metricfunc) == 0 )
pending->metrics[n] = n + 1;
else pending->metrics[n] = (*metricfunc)(myinfo,pending,pending->results[n]);
}
}
free(ptr);
continue;
}
else
{
flag = 0;
HASH_ITER(hh,myinfo->basilisks.issued,pending,tmp)
{
if ( pending->finished != 0 && pending->parent == 0 )
{
HASH_DELETE(hh,myinfo->basilisks.issued,pending);
if ( pending->vals != 0 )
free_json(pending->vals);
if ( pending->dependents != 0 )
free(pending->dependents);
free(pending);
flag++;
}
else if ( OS_milliseconds() > pending->expiration )
{
pending->finished = (uint32_t)time(NULL);
pending->retstr = clonestr("{\"error\":\"basilisk timeout\"}");
for (i=0; i<pending->numresults; i++)
if ( (metricfunc= pending->metricfunc) != 0 )
pending->metrics[i] = (*metricfunc)(myinfo,pending,pending->results[i]);
}
else
{
for (i=0; i<pending->numresults; i++)
if ( pending->metrics[i] == 0. )
{
if ( (metricfunc= pending->metricfunc) != 0 )
pending->metrics[i] = (*metricfunc)(myinfo,pending,pending->results[i]);
flag++;
}
}
}
if ( flag == 0 )
usleep(100000);
}
}
}
void basilisks_init(struct supernet_info *myinfo)
{
bits256 basiliskhash;
iguana_initQ(&myinfo->basilisks.submitQ,"submitQ");
iguana_initQ(&myinfo->basilisks.resultsQ,"resultsQ");
basiliskhash = calc_categoryhashes(0,"basilisk",0);
myinfo->basilisk_category = basiliskhash;
category_subscribe(myinfo,basiliskhash,GENESIS_PUBKEY);
category_processfunc(basiliskhash,GENESIS_PUBKEY,basilisk_hexmsg);
category_processfunc(basiliskhash,myinfo->myaddr.persistent,basilisk_hexmsg);
myinfo->basilisks.launched = iguana_launch(iguana_coinfind("BTCD"),"basilisks_loop",basilisks_loop,myinfo,IGUANA_PERMTHREAD);
}