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.
1525 lines
62 KiB
1525 lines
62 KiB
/******************************************************************************
|
|
* Copyright © 2014-2018 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. *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* - we need to include WinSock2.h header to correctly use windows structure
|
|
* as the application is still using 32bit structure from mingw so, we need to
|
|
* add the include based on checking
|
|
* @author - fadedreamz@gmail.com
|
|
* @remarks - #if (defined(_M_X64) || defined(__amd64__)) && defined(WIN32)
|
|
* is equivalent to #if defined(_M_X64) as _M_X64 is defined for MSVC only
|
|
*/
|
|
#if defined(_M_X64)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <WinSock2.h>
|
|
#endif
|
|
#ifdef _WIN32
|
|
#include <WinSock2.h>
|
|
#endif
|
|
|
|
int32_t set_blocking_mode(int32_t sock,int32_t is_blocking) // from https://stackoverflow.com/questions/2149798/how-to-reset-a-socket-back-to-blocking-mode-after-i-set-it-to-nonblocking-mode?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
|
|
{
|
|
int32_t ret;
|
|
#ifdef _WIN32
|
|
/// @note windows sockets are created in blocking mode by default
|
|
// currently on windows, there is no easy way to obtain the socket's current blocking mode since WSAIsBlocking was deprecated
|
|
u_long non_blocking = is_blocking ? 0 : 1;
|
|
ret = (NO_ERROR == ioctlsocket(sock,FIONBIO,&non_blocking));
|
|
#else
|
|
const int flags = fcntl(sock, F_GETFL, 0);
|
|
if ((flags & O_NONBLOCK) && !is_blocking) { fprintf(stderr,"set_blocking_mode(): socket was already in non-blocking mode\n"); return ret; }
|
|
if (!(flags & O_NONBLOCK) && is_blocking) { fprintf(stderr,"set_blocking_mode(): socket was already in blocking mode\n"); return ret; }
|
|
ret = (0 == fcntl(sock, F_SETFL, is_blocking ? (flags ^ O_NONBLOCK) : (flags | O_NONBLOCK)));
|
|
#endif
|
|
if ( ret == 0 )
|
|
return(-1);
|
|
else return(0);
|
|
}
|
|
|
|
int32_t komodo_connect(int32_t sock,struct sockaddr *saddr,socklen_t addrlen)
|
|
{
|
|
struct timeval tv; fd_set wfd,efd; int32_t res,so_error; socklen_t len;
|
|
#ifdef _WIN32
|
|
set_blocking_mode(sock, 0);
|
|
#else
|
|
fcntl(sock, F_SETFL, O_NONBLOCK);
|
|
#endif // _WIN32
|
|
res = connect(sock,saddr,addrlen);
|
|
|
|
if ( res == -1 )
|
|
{
|
|
#ifdef _WIN32
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms737625%28v=vs.85%29.aspx - read about WSAEWOULDBLOCK return
|
|
errno = WSAGetLastError();
|
|
printf("[Decker] errno.%d --> ", errno);
|
|
if ( errno != EINPROGRESS && errno != WSAEWOULDBLOCK ) // connect failed, do something...
|
|
#else
|
|
if ( errno != EINPROGRESS ) // connect failed, do something...
|
|
#endif
|
|
{
|
|
printf("close socket ...\n");
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
//printf("continue with select ...\n");
|
|
FD_ZERO(&wfd);
|
|
FD_SET(sock,&wfd);
|
|
FD_ZERO(&efd);
|
|
FD_SET(sock,&efd);
|
|
tv.tv_sec = 10;
|
|
tv.tv_usec = 0;
|
|
res = select(sock+1,NULL,&wfd,&efd,&tv);
|
|
if ( res == -1 ) // select failed, do something...
|
|
{
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
if ( res == 0 ) // connect timed out...
|
|
{
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
if ( FD_ISSET(sock,&efd) )
|
|
{
|
|
// connect failed, do something...
|
|
getsockopt(sock,SOL_SOCKET,SO_ERROR,&so_error,&len);
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
}
|
|
set_blocking_mode(sock,1);
|
|
return(0);
|
|
}
|
|
|
|
int32_t LP_socket(int32_t bindflag,char *hostname,uint16_t port)
|
|
{
|
|
int32_t opt,sock,result; char ipaddr[64],checkipaddr[64]; struct timeval timeout;
|
|
struct sockaddr_in saddr; socklen_t addrlen,slen;
|
|
addrlen = sizeof(saddr);
|
|
struct hostent *hostent;
|
|
|
|
/**
|
|
* gethostbyname() is deprecated and cause crash on x64 windows
|
|
* the solution is to implement similar functionality by using getaddrinfo()
|
|
* it is standard posix function and is correctly supported in win32/win64/linux
|
|
* @author - fadedreamz@gmail.com
|
|
*/
|
|
#if defined(_M_X64)
|
|
struct addrinfo *addrresult = NULL;
|
|
struct addrinfo *returnptr = NULL;
|
|
struct addrinfo hints;
|
|
struct sockaddr_in * sockaddr_ipv4;
|
|
int retVal;
|
|
int found = 0;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
#endif
|
|
|
|
if ( parse_ipaddr(ipaddr,hostname) != 0 )
|
|
port = parse_ipaddr(ipaddr,hostname);
|
|
|
|
#if defined(_M_X64)
|
|
retVal = getaddrinfo(ipaddr, NULL, &hints, &addrresult);
|
|
for (returnptr = addrresult; returnptr != NULL && found == 0; returnptr = returnptr->ai_next) {
|
|
switch (returnptr->ai_family) {
|
|
case AF_INET:
|
|
sockaddr_ipv4 = (struct sockaddr_in *) returnptr->ai_addr;
|
|
// we want to break from the loop after founding the first ipv4 address
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we iterate through the loop and didn't find anything,
|
|
// that means we failed in the dns lookup
|
|
if (found == 0) {
|
|
printf("getaddrinfo(%s) returned error\n", hostname);
|
|
freeaddrinfo(addrresult);
|
|
return(-1);
|
|
}
|
|
#else
|
|
hostent = gethostbyname(ipaddr);
|
|
if ( hostent == NULL )
|
|
{
|
|
printf("gethostbyname(%s) returned error: %d port.%d ipaddr.(%s)\n",hostname,errno,port,ipaddr);
|
|
return(-1);
|
|
}
|
|
#endif
|
|
saddr.sin_family = AF_INET;
|
|
saddr.sin_port = htons(port);
|
|
//#ifdef _WIN32
|
|
// saddr.sin_addr.s_addr = (uint32_t)calc_ipbits("127.0.0.1");
|
|
//#else
|
|
|
|
#if defined(_M_X64)
|
|
saddr.sin_addr.s_addr = sockaddr_ipv4->sin_addr.s_addr;
|
|
// graceful cleanup
|
|
sockaddr_ipv4 = NULL;
|
|
freeaddrinfo(addrresult);
|
|
#else
|
|
memcpy(&saddr.sin_addr.s_addr,hostent->h_addr_list[0],hostent->h_length);
|
|
#endif
|
|
expand_ipbits(checkipaddr,saddr.sin_addr.s_addr);
|
|
if ( strcmp(ipaddr,checkipaddr) != 0 )
|
|
printf("bindflag.%d iguana_socket mismatch (%s) -> (%s)\n",bindflag,checkipaddr,ipaddr);
|
|
//#endif
|
|
if ( (sock= socket(AF_INET,SOCK_STREAM,0)) < 0 )
|
|
{
|
|
if ( errno != ETIMEDOUT )
|
|
printf("socket() failed: %s errno.%d", strerror(errno),errno);
|
|
return(-1);
|
|
}
|
|
opt = 1;
|
|
slen = sizeof(opt);
|
|
//printf("set keepalive.%d\n",setsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,(void *)&opt,slen));
|
|
#ifndef _WIN32
|
|
if ( 1 )//&& bindflag != 0 )
|
|
{
|
|
opt = 0;
|
|
getsockopt(sock,SOL_SOCKET,SO_KEEPALIVE,(void *)&opt,&slen);
|
|
opt = 1;
|
|
//printf("keepalive.%d\n",opt);
|
|
}
|
|
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void *)&opt,sizeof(opt));
|
|
#ifdef __APPLE__
|
|
setsockopt(sock,SOL_SOCKET,SO_NOSIGPIPE,&opt,sizeof(opt));
|
|
#endif
|
|
#endif
|
|
if ( bindflag == 0 )
|
|
{
|
|
//#ifdef _WIN32
|
|
if ( 1 ) // connect using async to allow timeout, then switch to sync
|
|
{
|
|
uint32_t starttime = (uint32_t)time(NULL);
|
|
//printf("call connect sock.%d\n",sock);
|
|
result = komodo_connect(sock,(struct sockaddr *)&saddr,addrlen);
|
|
//printf("called connect result.%d lag.%d\n",result,(int32_t)(time(NULL) - starttime));
|
|
if ( result < 0 )
|
|
return(-1);
|
|
timeout.tv_sec = 10;
|
|
timeout.tv_usec = 0;
|
|
setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(void *)&timeout,sizeof(timeout));
|
|
}
|
|
//#else
|
|
else
|
|
{
|
|
result = connect(sock,(struct sockaddr *)&saddr,addrlen);
|
|
if ( result != 0 )
|
|
{
|
|
if ( errno != ECONNRESET && errno != ENOTCONN && errno != ECONNREFUSED && errno != ETIMEDOUT && errno != EHOSTUNREACH )
|
|
{
|
|
//printf("%s(%s) port.%d failed: %s sock.%d. errno.%d\n",bindflag!=0?"bind":"connect",hostname,port,strerror(errno),sock,errno);
|
|
}
|
|
if ( sock >= 0 )
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
}
|
|
//#endif
|
|
}
|
|
else
|
|
{
|
|
while ( (result= bind(sock,(struct sockaddr*)&saddr,addrlen)) != 0 )
|
|
{
|
|
if ( errno == EADDRINUSE )
|
|
{
|
|
sleep(1);
|
|
printf("ERROR BINDING PORT.%d. this is normal tcp timeout, unless another process is using port\n",port);
|
|
fflush(stdout);
|
|
sleep(3);
|
|
printf("%s(%s) port.%d try again: %s sock.%d. errno.%d\n",bindflag!=0?"bind":"connect",hostname,port,strerror(errno),sock,errno);
|
|
if ( bindflag == 1 )
|
|
{
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
sleep(13);
|
|
//continue;
|
|
}
|
|
if ( errno != ECONNRESET && errno != ENOTCONN && errno != ECONNREFUSED && errno != ETIMEDOUT && errno != EHOSTUNREACH )
|
|
{
|
|
printf("%s(%s) port.%d failed: %s sock.%d. errno.%d\n",bindflag!=0?"bind":"connect",hostname,port,strerror(errno),sock,errno);
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
}
|
|
if ( listen(sock,64) != 0 )
|
|
{
|
|
printf("listen(%s) port.%d failed: %s sock.%d. errno.%d\n",hostname,port,strerror(errno),sock,errno);
|
|
if ( sock >= 0 )
|
|
closesocket(sock);
|
|
return(-1);
|
|
}
|
|
}
|
|
#ifdef __APPLE__
|
|
//timeout.tv_sec = 0;
|
|
//timeout.tv_usec = 30000;
|
|
//setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(void *)&timeout,sizeof(timeout));
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 10000;
|
|
setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(void *)&timeout,sizeof(timeout));
|
|
#endif
|
|
return(sock);
|
|
}
|
|
|
|
int32_t LP_socketsend(int32_t sock,uint8_t *serialized,int32_t len)
|
|
{
|
|
int32_t numsent,remains,flags = 0;
|
|
#ifndef _WIN32
|
|
flags = MSG_NOSIGNAL;
|
|
#endif
|
|
remains = len;
|
|
while ( sock >= 0 && remains > 0 )
|
|
{
|
|
if ( (numsent= (int32_t)send(sock,serialized,remains,flags)) < 0 )
|
|
{
|
|
if ( errno == EAGAIN || errno == EWOULDBLOCK )
|
|
{
|
|
sleep(1);
|
|
continue;
|
|
}
|
|
printf("(%s): numsent.%d vs remains.%d len.%d errno.%d (%s) usock.%d\n",serialized,numsent,remains,len,errno,strerror(errno),sock);
|
|
return(-errno);
|
|
}
|
|
else if ( remains > 0 )
|
|
{
|
|
remains -= numsent;
|
|
serialized += numsent;
|
|
if ( remains > 0 )
|
|
printf("%d LP_socket sent.%d remains.%d of len.%d\n",sock,numsent,remains,len);
|
|
}
|
|
//printf("numsent.%d vs remains.%d len.%d sock.%d\n",numsent,remains,len,sock);
|
|
}
|
|
return(len);
|
|
}
|
|
|
|
int32_t LP_socketrecv(int32_t sock,uint8_t *recvbuf,int32_t maxlen)
|
|
{
|
|
int32_t recvlen = -1;
|
|
while ( sock >= 0 )
|
|
{
|
|
if ( (recvlen= (int32_t)recv(sock,recvbuf,maxlen,0)) < 0 )
|
|
{
|
|
if ( errno == EAGAIN )
|
|
{
|
|
//printf("%s recv errno.%d %s len.%d remains.%d\n",ipaddr,errno,strerror(errno),len,remains);
|
|
//printf("EAGAIN for len %d, remains.%d\n",len,remains);
|
|
sleep(1);
|
|
} else return(-errno);
|
|
} else break;
|
|
}
|
|
return(recvlen);
|
|
}
|
|
|
|
struct electrum_info *Electrums[8192];
|
|
int32_t Num_electrums;
|
|
|
|
struct electrum_info *electrum_server(char *symbol,struct electrum_info *ep)
|
|
{
|
|
struct electrum_info *rbuf[128],*recent_ep; uint32_t recent,mostrecent = 0; int32_t i,n = 0;
|
|
portable_mutex_lock(&LP_electrummutex);
|
|
if ( ep == 0 )
|
|
{
|
|
//printf("find random electrum.%s from %d\n",symbol,Num_electrums);
|
|
memset(rbuf,0,sizeof(rbuf));
|
|
recent_ep = 0;
|
|
recent = (uint32_t)time(NULL) - 300;
|
|
for (i=0; i<Num_electrums; i++)
|
|
{
|
|
ep = Electrums[i];
|
|
if ( strcmp(symbol,ep->symbol) == 0 && ep->sock >= 0 )
|
|
{
|
|
if ( ep->lasttime > recent )
|
|
{
|
|
rbuf[n++] = ep;
|
|
if ( n == sizeof(rbuf)/sizeof(*rbuf) )
|
|
break;
|
|
}
|
|
else if ( ep->lasttime > mostrecent )
|
|
{
|
|
mostrecent = ep->lasttime;
|
|
recent_ep = ep;
|
|
}
|
|
}
|
|
}
|
|
ep = recent_ep;
|
|
if ( n > 0 )
|
|
{
|
|
i = (LP_rand() % n);
|
|
ep = rbuf[i];
|
|
}
|
|
}
|
|
else if ( Num_electrums < sizeof(Electrums)/sizeof(*Electrums) )
|
|
Electrums[Num_electrums++] = ep;
|
|
else printf("Electrum server pointer buf overflow %d\n",Num_electrums);
|
|
portable_mutex_unlock(&LP_electrummutex);
|
|
return(ep);
|
|
}
|
|
|
|
int32_t electrum_process_array(struct iguana_info *coin,struct electrum_info *ep,char *coinaddr,cJSON *array,int32_t electrumflag,bits256 reftxid,bits256 reftxid2)
|
|
{
|
|
int32_t i,v,n,ht,flag = 0; char str[65]; uint64_t value; bits256 txid; cJSON *item,*retjson,*txobj; struct LP_transaction *tx;
|
|
if ( array != 0 && coin != 0 && (n= cJSON_GetArraySize(array)) > 0 )
|
|
{
|
|
//printf("PROCESS %s/%s %s num.%d\n",coin->symbol,ep!=0?ep->symbol:"nanolistunspent",coinaddr,n);
|
|
for (i=0; i<n; i++)
|
|
{
|
|
item = jitem(array,i);
|
|
if ( electrumflag == 0 )
|
|
{
|
|
txid = jbits256(item,"txid");
|
|
v = jint(item,"vout");
|
|
value = LP_value_extract(item,0,txid);
|
|
ht = LP_txheight(coin,txid);
|
|
if ( (retjson= LP_gettxout(coin->symbol,coinaddr,txid,v)) != 0 )
|
|
free_json(retjson);
|
|
else
|
|
{
|
|
//printf("external unspent has no gettxout\n");
|
|
flag += LP_address_utxoadd(0,(uint32_t)time(NULL),"electrum process",coin,coinaddr,txid,v,value,0,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
txid = jbits256(item,"tx_hash");
|
|
v = jint(item,"tx_pos");
|
|
value = j64bits(item,"value");
|
|
ht = jint(item,"height");
|
|
}
|
|
if ( bits256_nonz(txid) == 0 )
|
|
continue;
|
|
if ( (tx= LP_transactionfind(coin,txid)) == 0 )
|
|
{
|
|
if ( (bits256_nonz(reftxid) == 0 || bits256_cmp(reftxid,txid) == 0) && (bits256_nonz(reftxid2) == 0 || bits256_cmp(reftxid2,txid) == 0) )
|
|
{
|
|
txobj = LP_transactioninit(coin,txid,0,0);
|
|
LP_transactioninit(coin,txid,1,txobj);
|
|
free_json(txobj);
|
|
tx = LP_transactionfind(coin,txid);
|
|
}
|
|
}
|
|
if ( tx != 0 )
|
|
{
|
|
if (tx->height <= 0 )
|
|
{
|
|
tx->height = ht;
|
|
if ( ep != 0 && coin != 0 && tx->SPV == 0 )
|
|
{
|
|
if ( 0 && strcmp(coinaddr,coin->smartaddr) == 0 )
|
|
tx->SPV = LP_merkleproof(coin,coin->smartaddr,ep,txid,tx->height);
|
|
//printf("%s %s >>>>>>>>>> set %s <- height %d\n",coin->symbol,coinaddr,bits256_str(str,txid),tx->height);
|
|
}
|
|
}
|
|
if ( v >= 0 && v < tx->numvouts )
|
|
{
|
|
if ( tx->outpoints[v].value == 0 && value != tx->outpoints[v].value )
|
|
{
|
|
printf("%s %s >>>>>>>>>> set %s/v%d <- %.8f vs %.8f\n",coin->symbol,coinaddr,bits256_str(str,txid),v,dstr(value),dstr(tx->outpoints[v].value));
|
|
tx->outpoints[v].value = value;
|
|
}
|
|
}
|
|
if ( tx->height > 0 )
|
|
{
|
|
//printf("from electrum_process_array\n");
|
|
flag += LP_address_utxoadd(0,(uint32_t)time(NULL),"electrum process2",coin,coinaddr,txid,v,value,tx->height,-1);
|
|
}
|
|
//printf("v.%d numvouts.%d %.8f (%s)\n",v,tx->numvouts,dstr(tx->outpoints[jint(item,"tx_pos")].value),jprint(item,0));
|
|
} //else printf("cant find tx\n");
|
|
}
|
|
}
|
|
return(flag);
|
|
}
|
|
|
|
cJSON *electrum_version(char *symbol,struct electrum_info *ep,cJSON **retjsonp);
|
|
cJSON *electrum_headers_subscribe(char *symbol,struct electrum_info *ep,cJSON **retjsonp);
|
|
|
|
struct stritem *electrum_sitem(struct electrum_info *ep,char *stratumreq,int32_t timeout,cJSON **retjsonp)
|
|
{
|
|
struct stritem *sitem = (struct stritem *)queueitem(stratumreq);
|
|
sitem->expiration = timeout;
|
|
sitem->DL.type = ep->stratumid++;
|
|
sitem->retptrp = (void **)retjsonp;
|
|
queue_enqueue("sendQ",&ep->sendQ,&sitem->DL);
|
|
return(sitem);
|
|
}
|
|
|
|
void electrum_initial_requests(struct electrum_info *ep)
|
|
{
|
|
cJSON *retjson; char stratumreq[1024];
|
|
retjson = 0;
|
|
sprintf(stratumreq,"{ \"jsonrpc\":\"2.0\", \"id\": %u, \"method\":\"%s\", \"params\": %s }\n",ep->stratumid,"blockchain.headers.subscribe","[]");
|
|
electrum_sitem(ep,stratumreq,3,&retjson);
|
|
|
|
retjson = 0;
|
|
sprintf(stratumreq,"{ \"jsonrpc\":\"2.0\", \"id\": %u, \"method\":\"%s\", \"params\": %s }\n",ep->stratumid,"server.version","[\"barterDEX\", [\"1.1\", \"1.1\"]]");
|
|
electrum_sitem(ep,stratumreq,3,&retjson);
|
|
|
|
retjson = 0;
|
|
sprintf(stratumreq,"{ \"jsonrpc\":\"2.0\", \"id\": %u, \"method\":\"%s\", \"params\": %s }\n",ep->stratumid,"blockchain.estimatefee","[2]");
|
|
electrum_sitem(ep,stratumreq,3,&retjson);
|
|
}
|
|
|
|
int32_t electrum_kickstart(struct electrum_info *ep)
|
|
{
|
|
closesocket(ep->sock);//, ep->sock = -1;
|
|
if ( (ep->sock= LP_socket(0,ep->ipaddr,ep->port)) < 0 )
|
|
{
|
|
printf("error RE-connecting to %s:%u\n",ep->ipaddr,ep->port);
|
|
return(-1);
|
|
}
|
|
else
|
|
{
|
|
ep->stratumid = 0;
|
|
electrum_initial_requests(ep);
|
|
printf("RECONNECT ep.%p %s numerrors.%d too big -> new %s:%u sock.%d\n",ep,ep->symbol,ep->numerrors,ep->ipaddr,ep->port,ep->sock);
|
|
ep->numerrors = 0;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int32_t zeroval();
|
|
|
|
cJSON *electrum_submit(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *method,char *params,int32_t timeout)
|
|
{
|
|
// queue id and string and callback
|
|
char stratumreq[16384]; uint32_t expiration; struct stritem *sitem;
|
|
if ( ep == 0 )
|
|
ep = electrum_server(symbol,0);
|
|
while ( ep != 0 )
|
|
{
|
|
if ( strcmp(ep->symbol,symbol) != 0 )
|
|
{
|
|
printf("electrum_submit ep.%p %s %s:%u called for [%s]???\n",ep,ep->symbol,ep->ipaddr,ep->port,symbol);
|
|
}
|
|
if ( ep != 0 && ep->sock >= 0 && retjsonp != 0 )
|
|
{
|
|
*retjsonp = 0;
|
|
sprintf(stratumreq,"{ \"jsonrpc\":\"2.0\", \"id\": %u, \"method\":\"%s\", \"params\": %s }\n",ep->stratumid,method,params);
|
|
//printf("timeout.%d exp.%d %s %s",timeout,(int32_t)(expiration-time(NULL)),symbol,stratumreq);
|
|
memset(ep->buf,0,ep->bufsize);
|
|
sitem = electrum_sitem(ep,stratumreq,timeout,retjsonp);
|
|
portable_mutex_lock(&ep->mutex); // this helps performance!
|
|
expiration = (uint32_t)time(NULL) + timeout + 1;
|
|
while ( *retjsonp == 0 && time(NULL) <= expiration )
|
|
usleep(15000);
|
|
portable_mutex_unlock(&ep->mutex);
|
|
if ( *retjsonp == 0 || jobj(*retjsonp,"error") != 0 )
|
|
{
|
|
if ( ++ep->numerrors >= LP_ELECTRUM_MAXERRORS )
|
|
{
|
|
// electrum_kickstart(ep); seems to hurt more than help
|
|
}
|
|
} else if ( ep->numerrors > 0 )
|
|
ep->numerrors--;
|
|
if ( ep->prev == 0 )
|
|
{
|
|
if ( *retjsonp == 0 )
|
|
{
|
|
//printf("unexpected %s timeout with null retjson: %s %s\n",ep->symbol,method,params);
|
|
*retjsonp = cJSON_Parse("{\"error\":\"timeout\"}");
|
|
}
|
|
return(*retjsonp);
|
|
}
|
|
} //else printf("couldnt find electrum server for (%s %s) or no retjsonp.%p\n",method,params,retjsonp);
|
|
ep = ep->prev;
|
|
//if ( ep != 0 )
|
|
// printf("using prev ep.%s\n",ep->symbol);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
cJSON *electrum_noargs(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *method,int32_t timeout)
|
|
{
|
|
cJSON *retjson;
|
|
if ( retjsonp == 0 )
|
|
retjsonp = &retjson;
|
|
return(electrum_submit(symbol,ep,retjsonp,method,"[]",timeout));
|
|
}
|
|
|
|
cJSON *electrum_strarg(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *method,char *arg,int32_t timeout)
|
|
{
|
|
char params[16384]; cJSON *retjson;
|
|
if ( strlen(arg) < sizeof(params) )
|
|
{
|
|
if ( retjsonp == 0 )
|
|
retjsonp = &retjson;
|
|
sprintf(params,"[\"%s\"]",arg);
|
|
return(electrum_submit(symbol,ep,retjsonp,method,params,timeout));
|
|
} else return(0);
|
|
}
|
|
|
|
cJSON *electrum_jsonarg(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *method,cJSON *arg,int32_t timeout)
|
|
{
|
|
cJSON *retjson;
|
|
if ( arg != NULL )
|
|
{
|
|
if ( retjsonp == 0 )
|
|
retjsonp = &retjson;
|
|
return(electrum_submit(symbol,ep,retjsonp,method,jprint(arg,0),timeout));
|
|
} else return(0);
|
|
}
|
|
|
|
cJSON *electrum_intarg(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *method,int32_t arg,int32_t timeout)
|
|
{
|
|
char params[64]; cJSON *retjson;
|
|
if ( retjsonp == 0 )
|
|
retjsonp = &retjson;
|
|
sprintf(params,"[\"%d\"]",arg);
|
|
return(electrum_submit(symbol,ep,retjsonp,method,params,timeout));
|
|
}
|
|
|
|
cJSON *electrum_hasharg(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *method,bits256 arg,int32_t timeout)
|
|
{
|
|
char params[128],str[65]; cJSON *retjson;
|
|
if ( retjsonp == 0 )
|
|
retjsonp = &retjson;
|
|
sprintf(params,"[\"%s\"]",bits256_str(str,arg));
|
|
return(electrum_submit(symbol,ep,retjsonp,method,params,timeout));
|
|
}
|
|
|
|
cJSON *electrum_version(char *symbol,struct electrum_info *ep,cJSON **retjsonp)
|
|
{
|
|
char params[128]; cJSON *retjson;
|
|
if ( retjsonp == 0 )
|
|
retjsonp = &retjson;
|
|
sprintf(params,"[\"barterDEX\", [\"1.1\", \"1.1\"]]");
|
|
return(electrum_submit(symbol,ep,retjsonp,"server.version",params,ELECTRUM_TIMEOUT));
|
|
}
|
|
|
|
|
|
cJSON *electrum_banner(char *symbol,struct electrum_info *ep,cJSON **retjsonp) { return(electrum_noargs(symbol,ep,retjsonp,"server.banner",ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_donation(char *symbol,struct electrum_info *ep,cJSON **retjsonp) { return(electrum_noargs(symbol,ep,retjsonp,"server.donation_address",ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_peers(char *symbol,struct electrum_info *ep,cJSON **retjsonp) { return(electrum_noargs(symbol,ep,retjsonp,"server.peers.subscribe",ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_features(char *symbol,struct electrum_info *ep,cJSON **retjsonp) { return(electrum_noargs(symbol,ep,retjsonp,"server.features",ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_headers_subscribe(char *symbol,struct electrum_info *ep,cJSON **retjsonp) { return(electrum_noargs(symbol,ep,retjsonp,"blockchain.headers.subscribe",ELECTRUM_TIMEOUT)); }
|
|
|
|
cJSON *electrum_script_getbalance(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *script) { return(electrum_strarg(symbol,ep,retjsonp,"blockchain.scripthash.get_balance",script,ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_script_gethistory(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *script) { return(electrum_strarg(symbol,ep,retjsonp,"blockchain.scripthash.get_history",script,ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_script_getmempool(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *script) { return(electrum_strarg(symbol,ep,retjsonp,"blockchain.scripthash.get_mempool",script,ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_script_listunspent(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *script) { return(electrum_strarg(symbol,ep,retjsonp,"blockchain.scripthash.listunspent",script,ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_script_subscribe(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *script) { return(electrum_strarg(symbol,ep,retjsonp,"blockchain.scripthash.subscribe",script,ELECTRUM_TIMEOUT)); }
|
|
|
|
cJSON *electrum_address_subscribe(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *addr)
|
|
{
|
|
cJSON *retjson;
|
|
if ( (retjson= electrum_strarg(symbol,ep,retjsonp,"blockchain.address.subscribe",addr,ELECTRUM_TIMEOUT)) != 0 )
|
|
{
|
|
//printf("subscribe.(%s)\n",jprint(retjson,0));
|
|
}
|
|
return(retjson);
|
|
}
|
|
|
|
cJSON *electrum_scripthash_cmd(char *symbol,uint8_t taddr,struct electrum_info *ep,cJSON **retjsonp,char *cmd,char *coinaddr)
|
|
{
|
|
uint8_t addrtype,rmd160[20]; char btcaddr[64],cmdbuf[128]; //char scripthash[51],rmdstr[41],;
|
|
bitcoin_addr2rmd160(symbol,taddr,&addrtype,rmd160,coinaddr);
|
|
bitcoin_address("BTC",btcaddr,0,addrtype,rmd160,20);
|
|
//init_hexbytes_noT(rmdstr,rmd160,20);
|
|
//sprintf(scripthash,"%s",rmdstr);
|
|
//sprintf(cmdbuf,"blockchain.scripthash.%s",cmd);
|
|
sprintf(cmdbuf,"blockchain.address.%s",cmd);
|
|
return(electrum_strarg(symbol,ep,retjsonp,cmdbuf,btcaddr,ELECTRUM_TIMEOUT));
|
|
}
|
|
|
|
cJSON *electrum_address_gethistory(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *addr,bits256 reftxid)
|
|
{
|
|
char str[65]; struct LP_transaction *tx; cJSON *retjson,*txobj,*item; int32_t i,n,height; bits256 txid; struct iguana_info *coin = LP_coinfind(symbol);
|
|
if ( coin == 0 )
|
|
return(0);
|
|
if ( strcmp(symbol,"BCH") == 0 )
|
|
retjson = electrum_scripthash_cmd(symbol,coin->taddr,ep,retjsonp,"get_history",addr);
|
|
else retjson = electrum_strarg(symbol,ep,retjsonp,"blockchain.address.get_history",addr,ELECTRUM_TIMEOUT);
|
|
//printf("history.(%s)\n",jprint(retjson,0));
|
|
if ( retjson != 0 && (n= cJSON_GetArraySize(retjson)) > 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
{
|
|
item = jitem(retjson,i);
|
|
txid = jbits256(item,"tx_hash");
|
|
height = jint(item,"height");
|
|
if ( (tx= LP_transactionfind(coin,txid)) == 0 && (bits256_nonz(reftxid) == 0 || bits256_cmp(txid,reftxid) == 0) )
|
|
{
|
|
//char str[65]; printf("history txinit %s ht.%d\n",bits256_str(str,txid),height);
|
|
txobj = LP_transactioninit(coin,txid,0,0);
|
|
txobj = LP_transactioninit(coin,txid,1,txobj);
|
|
if ( txobj != 0 )
|
|
free_json(txobj);
|
|
if ( height > 0 )
|
|
{
|
|
if ( (tx= LP_transactionfind(coin,txid)) != 0 )
|
|
{
|
|
if ( tx->height > 0 && tx->height != height )
|
|
printf("update %s height.%d <- %d\n",bits256_str(str,txid),tx->height,height);
|
|
tx->height = height;
|
|
LP_address_utxoadd(0,(uint32_t)time(NULL),"electrum history",coin,addr,txid,0,0,height,-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(retjson);
|
|
}
|
|
|
|
int32_t LP_txheight_check(struct iguana_info *coin,char *coinaddr,bits256 txid)
|
|
{
|
|
cJSON *retjson;
|
|
if ( coin->electrum != 0 )
|
|
{
|
|
if ( (retjson= electrum_address_gethistory(coin->symbol,coin->electrum,&retjson,coinaddr,txid)) != 0 )
|
|
free_json(retjson);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
cJSON *electrum_address_getmempool(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *addr,bits256 reftxid,bits256 reftxid2)
|
|
{
|
|
cJSON *retjson; struct iguana_info *coin = LP_coinfind(symbol);
|
|
if ( coin == 0 )
|
|
return(0);
|
|
if ( strcmp(symbol,"BCH") == 0 )
|
|
retjson = electrum_scripthash_cmd(symbol,coin->taddr,ep,retjsonp,"get_mempool",addr);
|
|
else retjson = electrum_strarg(symbol,ep,retjsonp,"blockchain.address.get_mempool",addr,ELECTRUM_TIMEOUT);
|
|
//printf("MEMPOOL.(%s)\n",jprint(retjson,0));
|
|
electrum_process_array(coin,ep,addr,retjson,1,reftxid,reftxid2);
|
|
return(retjson);
|
|
}
|
|
|
|
cJSON *electrum_address_listunspent(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *addr,int32_t electrumflag,bits256 txid,bits256 txid2)
|
|
{
|
|
cJSON *retjson=0; char *retstr; struct LP_address *ap; struct iguana_info *coin; int32_t updatedflag,height,usecache=1;
|
|
if ( (coin= LP_coinfind(symbol)) == 0 )
|
|
return(0);
|
|
if ( strcmp(addr,INSTANTDEX_KMD) == 0 )
|
|
return(cJSON_Parse("[]"));
|
|
if ( ep == 0 || ep->heightp == 0 )
|
|
height = coin->longestchain;
|
|
else height = *(ep->heightp);
|
|
if ( (ap= LP_address(coin,addr)) != 0 )
|
|
{
|
|
if ( ap->unspenttime == 0 )
|
|
{
|
|
ap->unspenttime = (uint32_t)time(NULL);
|
|
ap->unspentheight = height;
|
|
usecache = 1;
|
|
}
|
|
else if ( ap->unspentheight < height )
|
|
usecache = 0;
|
|
else if ( G.LP_pendingswaps != 0 && time(NULL) > ap->unspenttime+13 )
|
|
usecache = 0;
|
|
}
|
|
//usecache = 0; // disable unspents cache
|
|
if ( usecache == 0 || electrumflag > 1 )
|
|
{
|
|
if ( strcmp(symbol,"BCH") == 0 )
|
|
retjson = electrum_scripthash_cmd(symbol,coin->taddr,ep,retjsonp,"listunspent",addr);
|
|
else retjson = electrum_strarg(symbol,ep,retjsonp,"blockchain.address.listunspent",addr,ELECTRUM_TIMEOUT);
|
|
if ( retjson != 0 )
|
|
{
|
|
if ( jobj(retjson,"error") == 0 && is_cJSON_Array(retjson) != 0 )
|
|
{
|
|
if ( 0 && electrumflag > 1 )
|
|
printf("%s.%d u.%u/%d t.%ld %s LISTUNSPENT.(%d)\n",coin->symbol,height,ap->unspenttime,ap->unspentheight,time(NULL),addr,(int32_t)strlen(jprint(retjson,0)));
|
|
updatedflag = 0;
|
|
if ( electrum_process_array(coin,ep,addr,retjson,electrumflag,txid,txid2) != 0 )
|
|
{
|
|
//LP_postutxos(coin->symbol,addr);
|
|
updatedflag = 1;
|
|
}
|
|
retstr = jprint(retjson,0);
|
|
LP_unspents_cache(coin->symbol,addr,retstr,1);
|
|
free(retstr);
|
|
}
|
|
else
|
|
{
|
|
free_json(retjson);
|
|
retjson = 0;
|
|
}
|
|
if ( ap != 0 )
|
|
{
|
|
ap->unspenttime = (uint32_t)time(NULL);
|
|
ap->unspentheight = height;
|
|
}
|
|
}
|
|
}
|
|
if ( retjson == 0 )
|
|
{
|
|
if ( (retstr= LP_unspents_filestr(symbol,addr)) != 0 )
|
|
{
|
|
retjson = cJSON_Parse(retstr);
|
|
free(retstr);
|
|
} else retjson = LP_address_utxos(coin,addr,1);
|
|
}
|
|
return(retjson);
|
|
}
|
|
|
|
cJSON *electrum_address_getbalance(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *addr)
|
|
{
|
|
if ( strcmp(symbol,"BCH") == 0 )
|
|
return(electrum_scripthash_cmd(symbol,0,ep,retjsonp,"get_balance",addr));
|
|
else return(electrum_strarg(symbol,ep,retjsonp,"blockchain.address.get_balance",addr,ELECTRUM_TIMEOUT));
|
|
}
|
|
|
|
cJSON *electrum_addpeer(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *endpoint) { return(electrum_strarg(symbol,ep,retjsonp,"server.add_peer",endpoint,ELECTRUM_TIMEOUT)); }
|
|
cJSON *electrum_sendrawtransaction(char *symbol,struct electrum_info *ep,cJSON **retjsonp,char *rawtx) { return(electrum_strarg(symbol,ep,retjsonp,"blockchain.transaction.broadcast",rawtx,ELECTRUM_TIMEOUT)); }
|
|
|
|
cJSON *electrum_estimatefee(char *symbol,struct electrum_info *ep,cJSON **retjsonp,int32_t numblocks)
|
|
{
|
|
return(electrum_intarg(symbol,ep,retjsonp,"blockchain.estimatefee",numblocks,ELECTRUM_TIMEOUT));
|
|
}
|
|
|
|
cJSON *electrum_getchunk(char *symbol,struct electrum_info *ep,cJSON **retjsonp,int32_t n) { return(electrum_intarg(symbol,ep,retjsonp,"blockchain.block.get_chunk",n,ELECTRUM_TIMEOUT)); }
|
|
|
|
cJSON *electrum_getheader(char *symbol,struct electrum_info *ep,cJSON **retjsonp,int32_t n)
|
|
{
|
|
return(electrum_intarg(symbol,ep,retjsonp,"blockchain.block.get_header",n,ELECTRUM_TIMEOUT));
|
|
}
|
|
|
|
cJSON *LP_cache_transaction(struct iguana_info *coin,bits256 txid,uint8_t *serialized,int32_t len)
|
|
{
|
|
cJSON *txobj; struct LP_transaction *tx;
|
|
if ( (txobj= LP_transaction_fromdata(coin,txid,serialized,len)) != 0 )
|
|
{
|
|
if ( (tx= LP_transactionfind(coin,txid)) == 0 || tx->serialized == 0 )
|
|
{
|
|
txobj = LP_transactioninit(coin,txid,0,txobj);
|
|
LP_transactioninit(coin,txid,1,txobj);
|
|
tx = LP_transactionfind(coin,txid);
|
|
}
|
|
if ( tx != 0 )
|
|
{
|
|
tx->serialized = serialized;
|
|
tx->len = len;
|
|
}
|
|
else
|
|
{
|
|
char str[65]; printf("unexpected couldnt find tx %s %s\n",coin->symbol,bits256_str(str,txid));
|
|
free(serialized);
|
|
}
|
|
}
|
|
return(txobj);
|
|
}
|
|
|
|
cJSON *_electrum_transaction(char *symbol,struct electrum_info *ep,cJSON **retjsonp,bits256 txid)
|
|
{
|
|
char *hexstr,str[65]; int32_t len; cJSON *hexjson,*txobj=0; struct iguana_info *coin; uint8_t *serialized; struct LP_transaction *tx;
|
|
//printf("electrum_transaction %s %s\n",symbol,bits256_str(str,txid));
|
|
if ( bits256_nonz(txid) != 0 && (coin= LP_coinfind(symbol)) != 0 )
|
|
{
|
|
if ( (tx= LP_transactionfind(coin,txid)) != 0 && tx->serialized != 0 )
|
|
{
|
|
//char str[65]; printf("%s cache hit -> TRANSACTION.(%s)\n",symbol,bits256_str(str,txid));
|
|
if ( (txobj= LP_transaction_fromdata(coin,txid,tx->serialized,tx->len)) != 0 )
|
|
{
|
|
*retjsonp = txobj;
|
|
return(txobj);
|
|
}
|
|
}
|
|
if ( bits256_cmp(txid,coin->cachedtxid) == 0 )
|
|
{
|
|
if ( (txobj= LP_transaction_fromdata(coin,txid,coin->cachedtxiddata,coin->cachedtxidlen)) != 0 )
|
|
{
|
|
*retjsonp = txobj;
|
|
return(txobj);
|
|
}
|
|
}
|
|
hexjson = electrum_hasharg(symbol,ep,&hexjson,"blockchain.transaction.get",txid,ELECTRUM_TIMEOUT);
|
|
hexstr = jprint(hexjson,0);
|
|
if ( strlen(hexstr) > 100000 )
|
|
{
|
|
static uint32_t counter;
|
|
if ( counter++ < 3 )
|
|
printf("rawtransaction %s %s too big %d\n",coin->symbol,bits256_str(str,txid),(int32_t)strlen(hexstr));
|
|
free(hexstr);
|
|
free_json(hexjson);
|
|
*retjsonp = cJSON_Parse("{\"error\":\"transaction too big\"}");
|
|
return(*retjsonp);
|
|
}
|
|
if ( hexstr[0] == '"' && hexstr[strlen(hexstr)-1] == '"' )
|
|
hexstr[strlen(hexstr)-1] = 0;
|
|
if ( (len= is_hexstr(hexstr+1,0)) > 2 )
|
|
{
|
|
len = (int32_t)strlen(hexstr+1) >> 1;
|
|
serialized = malloc(len);
|
|
if ( coin->cachedtxiddata != 0 )
|
|
free(coin->cachedtxiddata);
|
|
coin->cachedtxiddata = malloc(len);
|
|
coin->cachedtxidlen = len;
|
|
decode_hex(serialized,len,hexstr+1);
|
|
memcpy(coin->cachedtxiddata,serialized,len);
|
|
free(hexstr);
|
|
//printf("DATA.(%s) from (%s)\n",hexstr+1,jprint(hexjson,0));
|
|
*retjsonp = LP_cache_transaction(coin,txid,serialized,len); // eats serialized
|
|
free_json(hexjson);
|
|
//printf("return from electrum_transaction\n");
|
|
return(*retjsonp);
|
|
} //else printf("%s %s non-hex tx.(%s)\n",coin->symbol,bits256_str(str,txid),jprint(hexjson,0));
|
|
free(hexstr);
|
|
free_json(hexjson);
|
|
}
|
|
*retjsonp = 0;
|
|
return(*retjsonp);
|
|
}
|
|
|
|
cJSON *electrum_transaction(int32_t *heightp,char *symbol,struct electrum_info *ep,cJSON **retjsonp,bits256 txid,char *SPVcheck)
|
|
{
|
|
cJSON *retjson,*array; bits256 zero; struct LP_transaction *tx=0; struct iguana_info *coin;
|
|
coin = LP_coinfind(symbol);
|
|
if ( coin == 0 )
|
|
return(0);
|
|
*heightp = 0;
|
|
if ( ep != 0 )
|
|
portable_mutex_lock(&ep->txmutex);
|
|
retjson = _electrum_transaction(symbol,ep,retjsonp,txid);
|
|
if ( (tx= LP_transactionfind(coin,txid)) != 0 && ep != 0 && coin != 0 && SPVcheck != 0 && SPVcheck[0] != 0 )
|
|
{
|
|
if ( tx->height <= 0 )
|
|
{
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
if ( (array= electrum_address_listunspent(symbol,ep,&array,SPVcheck,2,txid,zero)) != 0 )
|
|
{
|
|
printf("SPVcheck.%s got %d unspents\n",SPVcheck,cJSON_GetArraySize(array));
|
|
free_json(array);
|
|
}
|
|
}
|
|
if ( tx->height > 0 )
|
|
{
|
|
if ( tx->SPV == 0 )
|
|
tx->SPV = LP_merkleproof(coin,SPVcheck,ep,txid,tx->height);
|
|
*heightp = tx->height;
|
|
}
|
|
char str[65]; printf("%s %s %s SPV height %d SPV %d\n",coin->symbol,SPVcheck,bits256_str(str,txid),tx->height,tx->SPV);
|
|
} else if ( tx != 0 )
|
|
*heightp = tx->height;
|
|
if ( ep != 0 )
|
|
portable_mutex_unlock(&ep->txmutex);
|
|
return(retjson);
|
|
}
|
|
|
|
cJSON *electrum_getmerkle(char *symbol,struct electrum_info *ep,cJSON **retjsonp,bits256 txid,int32_t height)
|
|
{
|
|
char params[128],str[65];
|
|
sprintf(params,"[\"%s\", %d]",bits256_str(str,txid),height);
|
|
if ( bits256_nonz(txid) == 0 )
|
|
return(cJSON_Parse("{\"error\":\"null txid\"}"));
|
|
return(electrum_submit(symbol,ep,retjsonp,"blockchain.transaction.get_merkle",params,ELECTRUM_TIMEOUT));
|
|
}
|
|
|
|
void electrum_test()
|
|
{
|
|
cJSON *retjson; int32_t height; bits256 hash,zero; struct electrum_info *ep = 0; char *addr,*script,*symbol = "BTC";
|
|
while ( Num_electrums == 0 )
|
|
{
|
|
sleep(1);
|
|
printf("Num_electrums %p -> %d\n",&Num_electrums,Num_electrums);
|
|
}
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
printf("found electrum server\n");
|
|
if ( (retjson= electrum_version(symbol,ep,0)) != 0 )
|
|
printf("electrum_version %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_banner(symbol,ep,0)) != 0 )
|
|
printf("electrum_banner %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_donation(symbol,ep,0)) != 0 )
|
|
printf("electrum_donation %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_features(symbol,ep,0)) != 0 )
|
|
printf("electrum_features %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_estimatefee(symbol,ep,0,6)) != 0 )
|
|
printf("electrum_estimatefee %s\n",jprint(retjson,1));
|
|
decode_hex(hash.bytes,sizeof(hash),"0000000000000000005087f8845f9ed0282559017e3c6344106de15e46c07acd");
|
|
if ( (retjson= electrum_getheader(symbol,ep,0,3)) != 0 )
|
|
printf("electrum_getheader %s\n",jprint(retjson,1));
|
|
//if ( (retjson= electrum_getchunk(symbol,ep,0,3)) != 0 )
|
|
// printf("electrum_getchunk %s\n",jprint(retjson,1));
|
|
decode_hex(hash.bytes,sizeof(hash),"b967a7d55889fe11e993430921574ec6379bc8ce712a652c3fcb66c6be6e925c");
|
|
if ( (retjson= electrum_getmerkle(symbol,ep,0,hash,403000)) != 0 )
|
|
printf("electrum_getmerkle %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_transaction(&height,symbol,ep,0,hash,0)) != 0 )
|
|
printf("electrum_transaction %s\n",jprint(retjson,1));
|
|
addr = "14NeevLME8UAANiTCVNgvDrynUPk1VcQKb";
|
|
if ( (retjson= electrum_address_gethistory(symbol,ep,0,addr,zero)) != 0 )
|
|
printf("electrum_address_gethistory %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_address_getmempool(symbol,ep,0,addr,zero,zero)) != 0 )
|
|
printf("electrum_address_getmempool %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_address_getbalance(symbol,ep,0,addr)) != 0 )
|
|
printf("electrum_address_getbalance %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_address_listunspent(symbol,ep,0,addr,1,zero,zero)) != 0 )
|
|
printf("electrum_address_listunspent %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_addpeer(symbol,ep,0,"electrum.be:50001")) != 0 )
|
|
printf("electrum_addpeer %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_sendrawtransaction(symbol,ep,0,"0100000001b7e6d69a0fd650926bd5fbe63cc8578d976c25dbdda8dd61db5e05b0de4041fe000000006b483045022100de3ae8f43a2a026bb46f6b09b890861f8aadcb16821f0b01126d70fa9ae134e4022000925a842073484f1056c7fc97399f2bbddb9beb9e49aca76835cdf6e9c91ef3012103cf5ce3233e6d6e22291ebef454edff2b37a714aed685ce94a7eb4f83d8e4254dffffffff014c4eaa0b000000001976a914b598062b55362952720718e7da584a46a27bedee88ac00000000")) != 0 )
|
|
printf("electrum_sendrawtransaction %s\n",jprint(retjson,1));
|
|
|
|
if ( 0 )
|
|
{
|
|
script = "76a914b598062b55362952720718e7da584a46a27bedee88ac";
|
|
if ( (retjson= electrum_script_gethistory(symbol,ep,0,script)) != 0 )
|
|
printf("electrum_script_gethistory %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_script_getmempool(symbol,ep,0,script)) != 0 )
|
|
printf("electrum_script_getmempool %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_script_getbalance(symbol,ep,0,script)) != 0 )
|
|
printf("electrum_script_getbalance %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_script_listunspent(symbol,ep,0,script)) != 0 )
|
|
printf("electrum_script_listunspent %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_script_subscribe(symbol,ep,0,script)) != 0 )
|
|
printf("electrum_script_subscribe %s\n",jprint(retjson,1));
|
|
}
|
|
if ( (retjson= electrum_headers_subscribe(symbol,ep,0)) != 0 )
|
|
printf("electrum_headers %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_peers(symbol,ep,0)) != 0 )
|
|
printf("electrum_peers %s\n",jprint(retjson,1));
|
|
if ( (retjson= electrum_address_subscribe(symbol,ep,0,addr)) != 0 )
|
|
printf("electrum_address_subscribe %s\n",jprint(retjson,1));
|
|
}
|
|
|
|
struct electrum_info *LP_electrum_info(int32_t *alreadyp,char *symbol,char *ipaddr,uint16_t port,int32_t bufsize)
|
|
{
|
|
struct electrum_info *ep=0; int32_t i,sock; struct stritem *sitem; char name[512],*str = "init string";
|
|
*alreadyp = 0;
|
|
portable_mutex_lock(&LP_electrummutex);
|
|
for (i=0; i<Num_electrums; i++)
|
|
{
|
|
ep = Electrums[i];
|
|
//printf("i.%d %p %s %s:%u vs %s.(%s:%u)\n",i,ep,ep->symbol,ep->ipaddr,ep->port,symbol,ipaddr,port);
|
|
if ( strcmp(ep->ipaddr,ipaddr) == 0 && ep->port == port && strcmp(ep->symbol,symbol) == 0 )
|
|
{
|
|
*alreadyp = 1;
|
|
printf("%s.(%s:%u) already an electrum server\n",symbol,ipaddr,port);
|
|
break;
|
|
}
|
|
ep = 0;
|
|
}
|
|
portable_mutex_unlock(&LP_electrummutex);
|
|
if ( ep == 0 )
|
|
{
|
|
if ( (sock= LP_socket(0,ipaddr,port)) < 0 )
|
|
{
|
|
printf("error connecting to %s:%u\n",ipaddr,port);
|
|
return(0);
|
|
}
|
|
ep = calloc(1,sizeof(*ep) + bufsize);
|
|
portable_mutex_init(&ep->mutex);
|
|
portable_mutex_init(&ep->txmutex);
|
|
ep->sock = sock;
|
|
safecopy(ep->symbol,symbol,sizeof(ep->symbol));
|
|
safecopy(ep->ipaddr,ipaddr,sizeof(ep->ipaddr));
|
|
ep->port = port;
|
|
ep->bufsize = bufsize;
|
|
ep->coin = LP_coinfind(symbol);
|
|
ep->lasttime = (uint32_t)time(NULL);
|
|
sprintf(name,"%s_%s_%u_electrum_sendQ",symbol,ipaddr,port);
|
|
queue_enqueue(name,&ep->sendQ,queueitem(str));
|
|
if ( (sitem= queue_dequeue(&ep->sendQ)) == 0 && strcmp(sitem->str,str) != 0 )
|
|
printf("error with string sendQ sitem.%p (%s)\n",sitem,sitem==0?0:sitem->str);
|
|
sprintf(name,"%s_%s_%u_electrum_pendingQ",symbol,ipaddr,port);
|
|
queue_enqueue(name,&ep->pendingQ,queueitem(str));
|
|
if ( (sitem= queue_dequeue(&ep->pendingQ)) == 0 && strcmp(sitem->str,str) != 0 )
|
|
printf("error with string pendingQ sitem.%p (%s)\n",sitem,sitem==0?0:sitem->str);
|
|
electrum_server(symbol,ep);
|
|
}
|
|
return(ep);
|
|
}
|
|
|
|
int32_t LP_recvfunc(struct electrum_info *ep,char *str,int32_t len)
|
|
{
|
|
cJSON *strjson,*errjson,*resultjson,*paramsjson; char *method; int32_t i,n,height; uint32_t idnum=0; struct stritem *stritem; struct iguana_info *coin; struct queueitem *tmp,*item = 0;
|
|
if ( str == 0 || len == 0 )
|
|
return(-1);
|
|
ep->lasttime = (uint32_t)time(NULL);
|
|
if ( (strjson= cJSON_Parse(str)) != 0 )
|
|
{
|
|
//printf("%s RECV.(%ld) id.%d (%s)\n",ep->symbol,strlen(str),jint(strjson,"id"),jint(strjson,"id")==0?str:"");
|
|
resultjson = jobj(strjson,"result");
|
|
//printf("strjson.(%s)\n",jprint(strjson,0));
|
|
if ( (method= jstr(strjson,"method")) != 0 )
|
|
{
|
|
if ( strcmp(method,"blockchain.headers.subscribe") == 0 )
|
|
{
|
|
//printf("%p headers.(%s)\n",strjson,jprint(strjson,0));
|
|
if ( (paramsjson= jarray(&n,strjson,"params")) != 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
resultjson = jitem(paramsjson,i);
|
|
}
|
|
}
|
|
/*else if ( strcmp(method,"blockchain.address.subscribe") == 0 ) never is called
|
|
{
|
|
printf("recv addr subscribe.(%s)\n",jprint(resultjson,0));
|
|
electrum_process_array(ep->coin,resultjson);
|
|
}*/
|
|
}
|
|
if ( resultjson != 0 )
|
|
{
|
|
if ( (height= jint(resultjson,"block_height")) > 0 && ep->heightp != 0 && ep->heighttimep != 0 )
|
|
{
|
|
if ( height > *(ep->heightp) )
|
|
*(ep->heightp) = height;
|
|
*(ep->heighttimep) = (uint32_t)time(NULL);
|
|
if ( (coin= LP_coinfind(ep->symbol)) != 0 )
|
|
coin->updaterate = (uint32_t)time(NULL);
|
|
//printf("%s ELECTRUM >>>>>>>>> set height.%d\n",ep->symbol,height);
|
|
}
|
|
}
|
|
idnum = juint(strjson,"id");
|
|
portable_mutex_lock(&ep->pendingQ.mutex);
|
|
if ( ep->pendingQ.list != 0 )
|
|
{
|
|
DL_FOREACH_SAFE(ep->pendingQ.list,item,tmp)
|
|
{
|
|
stritem = (struct stritem *)item;
|
|
if ( item->type == idnum )
|
|
{
|
|
DL_DELETE(ep->pendingQ.list,item);
|
|
if ( resultjson != 0 )
|
|
*((cJSON **)stritem->retptrp) = jduplicate(resultjson);
|
|
else *((cJSON **)stritem->retptrp) = strjson, strjson = 0;
|
|
//printf("matched idnum.%d result.(%s)\n",idnum,jprint(*((cJSON **)stritem->retptrp),0));
|
|
//resultjson = strjson = 0;
|
|
free(item);
|
|
break;
|
|
}
|
|
if ( stritem->expiration < ep->lasttime )
|
|
{
|
|
DL_DELETE(ep->pendingQ.list,item);
|
|
if ( 0 )
|
|
{
|
|
printf("expired %s (%s)\n",ep->symbol,stritem->str);
|
|
errjson = cJSON_CreateObject();
|
|
jaddnum(errjson,"id",item->type);
|
|
jaddstr(errjson,"error","timeout");
|
|
*((cJSON **)stritem->retptrp) = errjson;
|
|
}
|
|
free(item);
|
|
}
|
|
}
|
|
}
|
|
portable_mutex_unlock(&ep->pendingQ.mutex);
|
|
if ( strjson != 0 )
|
|
free_json(strjson);
|
|
}
|
|
return(item != 0);
|
|
}
|
|
|
|
void LP_dedicatedloop(void *arg)
|
|
{
|
|
struct pollfd fds; int32_t i,len,n,flag,timeout = 10; struct iguana_info *coin; struct stritem *sitem; struct electrum_info *ep = arg;
|
|
if ( (coin= LP_coinfind(ep->symbol)) != 0 )
|
|
ep->heightp = &coin->height, ep->heighttimep = &coin->heighttime;
|
|
electrum_initial_requests(ep);
|
|
printf("LP_dedicatedloop ep.%p sock.%d for %s:%u num.%d %p %s ht.%d\n",ep,ep->sock,ep->ipaddr,ep->port,Num_electrums,&Num_electrums,ep->symbol,*ep->heightp);
|
|
while ( ep->sock >= 0 )
|
|
{
|
|
flag = 0;
|
|
memset(&fds,0,sizeof(fds));
|
|
fds.fd = ep->sock;
|
|
fds.events |= (POLLOUT | POLLIN);
|
|
if ( poll(&fds,1,timeout) > 0 && (fds.revents & POLLOUT) != 0 && ep->pending == 0 && (sitem= queue_dequeue(&ep->sendQ)) != 0 )
|
|
{
|
|
ep->pending = (uint32_t)time(NULL);
|
|
if ( LP_socketsend(ep->sock,(uint8_t *)sitem->str,(int32_t)strlen(sitem->str)) <= 0 )
|
|
{
|
|
printf("%s:%u is dead\n",ep->ipaddr,ep->port);
|
|
closesocket(ep->sock);
|
|
ep->sock = -1;
|
|
break;
|
|
}
|
|
ep->keepalive = (uint32_t)time(NULL);
|
|
if ( sitem->expiration != 0 )
|
|
sitem->expiration += (uint32_t)time(NULL);
|
|
else sitem->expiration = (uint32_t)time(NULL) + ELECTRUM_TIMEOUT;
|
|
queue_enqueue("pendingQ",&ep->pendingQ,&sitem->DL);
|
|
flag++;
|
|
}
|
|
if ( flag == 0 )
|
|
{
|
|
if ( (fds.revents & POLLIN) != 0 )
|
|
{
|
|
len = 0;
|
|
while ( len+65536 < ep->bufsize )
|
|
{
|
|
if ( (n= LP_socketrecv(ep->sock,&ep->buf[len],ep->bufsize-len)) > 0 )
|
|
{
|
|
len += n;
|
|
if ( ep->buf[len - 1] == '\n' )
|
|
break;
|
|
memset(&fds,0,sizeof(fds));
|
|
fds.fd = ep->sock;
|
|
fds.events = POLLIN;
|
|
if ( poll(&fds,1,1000) <= 0 )
|
|
{
|
|
printf("no more electrum data after a second\n");
|
|
electrum_kickstart(ep);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifndef _WIN32
|
|
printf("no more electrum data when expected2 len.%d n.%d\n",len,n);
|
|
electrum_kickstart(ep);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
if ( len > 0 )
|
|
{
|
|
ep->pending = 0;
|
|
LP_recvfunc(ep,(char *)ep->buf,len);
|
|
flag++;
|
|
}
|
|
}
|
|
if ( flag == 0 )
|
|
usleep(100000);
|
|
}
|
|
}
|
|
if ( coin->electrum == ep )
|
|
{
|
|
coin->electrum = ep->prev;
|
|
printf("set %s electrum to %p\n",coin->symbol,coin->electrum);
|
|
} else printf("backup electrum server closing\n");
|
|
printf(">>>>>>>>>> electrum close %s:%u\n",ep->ipaddr,ep->port);
|
|
if ( Num_electrums > 0 )
|
|
{
|
|
portable_mutex_lock(&LP_electrummutex);
|
|
for (i=0; i<Num_electrums; i++)
|
|
{
|
|
if ( Electrums[i] == ep )
|
|
{
|
|
Electrums[i] = Electrums[--Num_electrums];
|
|
Electrums[Num_electrums] = 0;
|
|
break;
|
|
}
|
|
}
|
|
portable_mutex_unlock(&LP_electrummutex);
|
|
}
|
|
ep->sock = -1;
|
|
//free(ep);
|
|
}
|
|
|
|
cJSON *tx_history_to_json(struct LP_tx_history_item *item, struct iguana_info *coin) {
|
|
cJSON *json = cJSON_CreateObject();
|
|
jaddstr(json, "txid", item->txid);
|
|
jaddstr(json, "category", item->category);
|
|
jaddnum(json, "amount", item->amount);
|
|
jaddstr(json, "blockhash", item->blockhash);
|
|
jaddnum(json, "blockindex", item->blockindex);
|
|
jaddnum(json, "blocktime", item->blocktime);
|
|
jaddnum(json, "time", item->time);
|
|
if (item->blockindex > 0) {
|
|
jaddnum(json, "confirmations", coin->height - item->blockindex + 1);
|
|
} else {
|
|
jaddnum(json, "confirmations", 0);
|
|
}
|
|
return json;
|
|
}
|
|
|
|
int history_item_cmp(struct LP_tx_history_item *item1, struct LP_tx_history_item *item2) {
|
|
return(item1->time < item2->time);
|
|
}
|
|
|
|
void LP_electrum_get_tx_until_success(struct iguana_info *coin, char *tx_hash, cJSON **res) {
|
|
cJSON *params = cJSON_CreateArray();
|
|
jaddistr(params, tx_hash);
|
|
jaddi(params, cJSON_CreateBool(cJSON_True));
|
|
while(1) {
|
|
electrum_jsonarg(coin->symbol, coin->electrum, res, "blockchain.transaction.get", params,
|
|
ELECTRUM_TIMEOUT);
|
|
if (jobj(*res, "error") != NULL) {
|
|
char *msg = jprint(*res, 1);
|
|
printf("Error getting electrum tx %s %s %s\n", coin->symbol, jstri(params, 0), msg);
|
|
*res = cJSON_CreateObject();
|
|
free(msg);
|
|
sleep(5);
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
free_json(params);
|
|
}
|
|
|
|
char *LP_get_address_from_tx_out(cJSON *tx_out) {
|
|
cJSON *script_pub_key = jobj(tx_out, "scriptPubKey");
|
|
if (script_pub_key == 0) {
|
|
printf("No scriptPubKey on tx_out: %s\n", jprint(tx_out, 0));
|
|
return 0;
|
|
}
|
|
|
|
struct cJSON *addresses = jobj(script_pub_key, "addresses");
|
|
if (addresses == 0) {
|
|
printf("No addresses on tx_out: %s\n", jprint(tx_out, 0));
|
|
return NULL;
|
|
}
|
|
|
|
if (!is_cJSON_Array(addresses)) {
|
|
printf("Addresses are not array on tx out: %s\n", jprint(tx_out, 0));
|
|
return NULL;
|
|
}
|
|
|
|
if (cJSON_GetArraySize(addresses) > 1) {
|
|
printf("Addresses array size is > 1 on tx out: %s\n", jprint(tx_out, 0));
|
|
return NULL;
|
|
}
|
|
return jstri(addresses, 0);
|
|
}
|
|
|
|
void LP_electrum_txhistory_loop(void *_coin)
|
|
{
|
|
struct iguana_info *coin = _coin;
|
|
if (strcmp(coin->symbol, "QTUM") == 0 || strcmp(coin->symbol, "CRW") == 0 || strcmp(coin->symbol, "BTX") == 0) {
|
|
printf("Tx history loop doesn't support QTUM, CRW and BTX! yet\n");
|
|
return;
|
|
}
|
|
while (coin != NULL && coin->electrum != NULL && coin->inactive == 0) {
|
|
cJSON *history = cJSON_CreateObject();
|
|
if (strcmp(coin->symbol, "BCH") == 0) {
|
|
electrum_scripthash_cmd(coin->symbol, coin->taddr, coin->electrum, &history, "get_history",
|
|
coin->smartaddr);
|
|
} else {
|
|
electrum_strarg(coin->symbol, coin->electrum, &history, "blockchain.address.get_history", coin->smartaddr,
|
|
ELECTRUM_TIMEOUT);
|
|
}
|
|
if (jobj(history, "error") != NULL) {
|
|
char *msg = jprint(history, 1);
|
|
printf("Error getting electrum history of coin %s %s\n", coin->symbol, msg);
|
|
free(msg);
|
|
sleep(10);
|
|
continue;
|
|
}
|
|
int history_size = cJSON_GetArraySize(history);
|
|
for (int i = history_size - 1; i >= 0; i--) {
|
|
cJSON *history_item = jitem(history, i);
|
|
char *tx_hash = jstr(history_item, "tx_hash");
|
|
struct LP_tx_history_item *iter;
|
|
int found = 0;
|
|
int confirmed = 0;
|
|
portable_mutex_lock(&coin->tx_history_mutex);
|
|
DL_FOREACH(coin->tx_history, iter) {
|
|
if (strcmp(iter->txid, tx_hash) == 0) {
|
|
found = 1;
|
|
if (iter->blockindex > 0) {
|
|
confirmed = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
portable_mutex_unlock(&coin->tx_history_mutex);
|
|
struct LP_tx_history_item *item;
|
|
if (!found) {
|
|
// allocate new if not found
|
|
item = malloc(sizeof(struct LP_tx_history_item));
|
|
memset(item, 0, sizeof(struct LP_tx_history_item));
|
|
} else if (confirmed == 0) {
|
|
// update existing if found and not confirmed
|
|
item = iter;
|
|
} else {
|
|
continue;
|
|
}
|
|
cJSON *tx_item = cJSON_CreateObject();
|
|
LP_electrum_get_tx_until_success(coin, tx_hash, &tx_item);
|
|
if (!found) {
|
|
strcpy(item->txid, jstr(tx_item, "txid"));
|
|
// receive by default, but if at least 1 vin contains our address the category is send
|
|
strcpy(item->category, "receive");
|
|
cJSON *vin = jobj(tx_item, "vin");
|
|
cJSON *prev_tx_item = NULL;
|
|
for (int j = 0; j < cJSON_GetArraySize(vin); j++) {
|
|
cJSON *vin_item = jitem(vin, j);
|
|
char *address = jstr(vin_item, "address");
|
|
if (address != NULL) {
|
|
if (strcmp(coin->smartaddr, jstr(vin_item, "address")) == 0) {
|
|
strcpy(item->category, "send");
|
|
break;
|
|
}
|
|
} else {
|
|
// At least BTC and LTC doesn't have an address field for Vins.
|
|
// Need to get previous tx and check the output
|
|
cJSON *params = cJSON_CreateArray();
|
|
// some transactions use few vouts of one prev tx so we can reuse it
|
|
if (prev_tx_item == 0 || strcmp(jstr(prev_tx_item, "txid"), jstr(vin_item, "txid")) != 0) {
|
|
if (prev_tx_item != 0) {
|
|
free_json(prev_tx_item);
|
|
prev_tx_item = NULL;
|
|
}
|
|
LP_electrum_get_tx_until_success(coin, jstr(vin_item, "txid"), &prev_tx_item);
|
|
}
|
|
char *vout_address = LP_get_address_from_tx_out(jitem(jobj(prev_tx_item, "vout"), jint(vin_item, "vout")));
|
|
if (strcmp(coin->smartaddr, vout_address) == 0) {
|
|
strcpy(item->category, "send");
|
|
break;
|
|
}
|
|
free_json(params);
|
|
}
|
|
}
|
|
int size = 0;
|
|
cJSON *vout = jarray(&size, tx_item, "vout");
|
|
for (int k = 0; k < size; k++) {
|
|
cJSON *vout_item = jitem(vout, k);
|
|
char *vout_address = LP_get_address_from_tx_out(vout_item);
|
|
if (vout_address != 0) {
|
|
if (strcmp(coin->smartaddr, vout_address) == 0) {
|
|
if (strcmp(item->category, "receive") == 0) {
|
|
item->amount += jdouble(vout_item, "value");
|
|
}
|
|
} else {
|
|
if (strcmp(item->category, "send") == 0) {
|
|
item->amount -= jdouble(vout_item, "value");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (juint(history_item, "height") > 0) {
|
|
item->time = juint(tx_item, "time");
|
|
strcpy(item->blockhash, jstr(tx_item, "blockhash"));
|
|
item->blockindex = juint(history_item, "height");
|
|
item->blocktime = juint(tx_item, "blocktime");
|
|
} else {
|
|
// set current time temporary until confirmed
|
|
item->time = (uint32_t)time(NULL);
|
|
}
|
|
if (!found) {
|
|
portable_mutex_lock(&coin->tx_history_mutex);
|
|
DL_APPEND(coin->tx_history, item);
|
|
portable_mutex_unlock(&coin->tx_history_mutex);
|
|
}
|
|
free_json(tx_item);
|
|
}
|
|
int (*ptr)(struct LP_tx_history_item*, struct LP_tx_history_item*) = &history_item_cmp;
|
|
// we don't want the history to be accessed while sorting
|
|
struct LP_tx_history_item *_tmp;
|
|
portable_mutex_lock(&coin->tx_history_mutex);
|
|
DL_SORT(coin->tx_history, ptr);
|
|
portable_mutex_unlock(&coin->tx_history_mutex);
|
|
free_json(history);
|
|
sleep(10);
|
|
}
|
|
}
|
|
|
|
cJSON *LP_electrumserver(struct iguana_info *coin,char *ipaddr,uint16_t port)
|
|
{
|
|
struct electrum_info *ep,*prev,*cur; int32_t kickval,already; cJSON *retjson,*array,*item;
|
|
cur = coin->electrum;
|
|
if ( ipaddr == 0 || ipaddr[0] == 0 || port == 0 )
|
|
{
|
|
ep = coin->electrum;
|
|
coin->electrum = 0;
|
|
coin->inactive = (uint32_t)time(NULL);
|
|
retjson = cJSON_CreateObject();
|
|
jaddstr(retjson,"result","success");
|
|
jaddstr(retjson,"status","electrum mode disabled, now in disabled native coin mode");
|
|
if ( ep != 0 )
|
|
{
|
|
array = cJSON_CreateArray();
|
|
while ( ep != 0 )
|
|
{
|
|
item = cJSON_CreateObject();
|
|
jaddstr(item,"ipaddr",ep->ipaddr);
|
|
jaddnum(item,"port",ep->port);
|
|
jaddnum(item,"kickstart",electrum_kickstart(ep));
|
|
jaddi(array,item);
|
|
prev = ep->prev;
|
|
ep->prev = 0;
|
|
ep = prev;
|
|
}
|
|
jadd(retjson,"electrums",array);
|
|
}
|
|
//printf("would have disabled %s electrum here\n",coin->symbol);
|
|
return(retjson);
|
|
}
|
|
retjson = cJSON_CreateObject();
|
|
jaddstr(retjson,"ipaddr",ipaddr);
|
|
jaddnum(retjson,"port",port);
|
|
if ( (ep= LP_electrum_info(&already,coin->symbol,ipaddr,port,IGUANA_MAXPACKETSIZE)) == 0 )
|
|
{
|
|
jaddstr(retjson,"error","couldnt connect to electrum server");
|
|
return(retjson);
|
|
}
|
|
if ( already == 0 )
|
|
{
|
|
if ( ep != 0 && OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)LP_dedicatedloop,(void *)ep) != 0 )
|
|
{
|
|
printf("error launching LP_dedicatedloop %s.(%s:%u)\n",coin->symbol,ep->ipaddr,ep->port);
|
|
jaddstr(retjson,"error","couldnt launch electrum thread");
|
|
}
|
|
else
|
|
{
|
|
//printf("launched %s electrum.(%s:%u)\n",coin->symbol,ep->ipaddr,ep->port);
|
|
jaddstr(retjson,"result","success");
|
|
ep->prev = coin->electrum;
|
|
coin->electrum = ep;
|
|
if ( coin->loadedcache == 0 )
|
|
{
|
|
LP_cacheptrs_init(coin);
|
|
coin->loadedcache = (uint32_t)time(NULL);
|
|
}
|
|
if ( 0 && strcmp(coin->symbol,"ZEC") == 0 )
|
|
{
|
|
void for_satinder();
|
|
sleep(3);
|
|
for_satinder();
|
|
getchar();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( coin->electrum == 0 )
|
|
{
|
|
coin->electrum = ep;
|
|
ep->prev = 0;
|
|
}
|
|
jaddstr(retjson,"result","success");
|
|
jaddstr(retjson,"status","already there");
|
|
if ( ep->numerrors > 0 )
|
|
{
|
|
kickval = electrum_kickstart(ep);
|
|
jaddnum(retjson,"restart",kickval);
|
|
}
|
|
}
|
|
#ifndef NOTETOMIC
|
|
if (coin->electrum != 0 && cur == 0 && strcmp(coin->symbol, "ETOMIC") == 0) {
|
|
cJSON *balance = cJSON_CreateObject();
|
|
electrum_address_getbalance(coin->symbol, coin->electrum, &balance, coin->smartaddr);
|
|
int64_t confirmed = get_cJSON_int(balance, "confirmed");
|
|
int64_t unconfirmed = get_cJSON_int(balance, "unconfirmed");
|
|
if ((confirmed + unconfirmed) < 20 * SATOSHIDEN && get_etomic_from_faucet(coin->smartaddr) != 1) {
|
|
cJSON_Delete(balance);
|
|
return(cJSON_Parse("{\"error\":\"Could not get ETOMIC from faucet!\"}"));
|
|
}
|
|
cJSON_Delete(balance);
|
|
}
|
|
#endif
|
|
if (coin->cache_history > 0 && coin->electrum != 0 && cur == 0) {
|
|
if ( OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)LP_electrum_txhistory_loop,(void *)coin) != 0 )
|
|
{
|
|
printf("error launching LP_electrum_tx_history_loop %s.(%s:%u)\n",coin->symbol,ep->ipaddr,ep->port);
|
|
jaddstr(retjson,"error","couldnt launch electrum tx history thread");
|
|
}
|
|
}
|
|
//printf("(%s)\n",jprint(retjson,0));
|
|
return(retjson);
|
|
}
|
|
|
|
cJSON *address_history_cached(struct iguana_info *coin) {
|
|
cJSON *retjson = cJSON_CreateArray();
|
|
struct LP_tx_history_item *item;
|
|
portable_mutex_lock(&coin->tx_history_mutex);
|
|
DL_FOREACH(coin->tx_history, item) {
|
|
jaddi(retjson, tx_history_to_json(item, coin));
|
|
}
|
|
portable_mutex_unlock(&coin->tx_history_mutex);
|
|
return retjson;
|
|
}
|
|
|