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

#ifdef DEFINES_ONLY
#ifndef hostnet777_h
#define hostnet777_h

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <memory.h>
#include <time.h>
#include "../utils/bits777.c"
#include "../utils/ramcoder.c"

#define HOSTNET777_MAXTIMEDIFF 10

#define CARDS777_MAXCARDS 52
#define CARDS777_MAXPLAYERS 9
#define CARDS777_FOLD -1
#define CARDS777_START 1
#define CARDS777_ANTE 2
#define CARDS777_SMALLBLIND 3
#define CARDS777_BIGBLIND 4
#define CARDS777_CHECK 5
#define CARDS777_CALL 6
#define CARDS777_BET 7
#define CARDS777_RAISE 8
#define CARDS777_FULLRAISE 9
#define CARDS777_SENTCARDS 10
#define CARDS777_ALLIN 11
#define CARDS777_FACEUP 12
#define CARDS777_WINNINGS 13
#define CARDS777_RAKES 14
#define CARDS777_CHANGES 15
#define CARDS777_SNAPSHOT 16

struct cards777_handinfo
{
    bits256 checkprod,*cardpubs,*final,community256[5],cards[CARDS777_MAXPLAYERS][2];
    uint64_t othercardpubs[CARDS777_MAXPLAYERS];
    int64_t havemasks[CARDS777_MAXPLAYERS],betsize,hostrake,pangearake,lastraise,bets[CARDS777_MAXPLAYERS],snapshot[CARDS777_MAXPLAYERS+1],won[CARDS777_MAXPLAYERS];
    uint32_t starttime,handmask,lastbettor,startdecktime,betstarted,finished,encodestarted;
    uint32_t cardi,userinput_starttime,handranks[CARDS777_MAXPLAYERS];
    int8_t betstatus[CARDS777_MAXPLAYERS],actions[CARDS777_MAXPLAYERS],turnis[CARDS777_MAXPLAYERS];
    uint8_t numactions,undergun,community[5],sharenrs[CARDS777_MAXPLAYERS][255],hands[CARDS777_MAXPLAYERS][7];
};

struct hostnet777_mtime { uint32_t starttime; int64_t millistart; double millidiff; };

struct cards777_pubdata
{
    int64_t snapshot[CARDS777_MAXPLAYERS]; 
    uint64_t maxrake,hostrake,bigblind,ante,pangearake,summaries,mismatches;
    uint32_t button,readymask,numhands,rakemillis,minbuyin,maxbuyin,summarysize;
    void *table; struct cards777_handinfo hand;
    char newhand[65536],coinstr[16]; uint8_t M,N,numcards,summary[65536]; bits256 data[];
};

struct cards777_privdata
{
    bits256 holecards[2],*audits,*outcards,*xoverz;
    //,*reconstructed[CARDS777_MAXPLAYERS],*mofn[CARDS777_MAXPLAYERS][CARDS777_MAXPLAYERS];
    uint8_t *myshares[CARDS777_MAXPLAYERS],*allshares,hole[2],cardis[2],automuck,autofold; bits256 data[];
};

struct hostnet777_endpoint { char endpoint[128],transport[16],ipaddr[64]; uint16_t port; };
struct hostnet777_id { bits256 pubkey; uint64_t nxt64bits; void *privdata,*pubdata; int32_t pmsock; uint32_t lastcontact; };
union hostnet777 { struct hostnet777_server *server; struct hostnet777_client *client; };
struct hostnet777_hdr
{
    queue_t Q; bits256 privkey,pubkey; struct hostnet777_mtime mT;
    void *privdata,*pubdata; uint64_t nxt64bits;//,recvhashes[64];
    void (*pollfunc)(union hostnet777 *hn);
    uint32_t lastping; int32_t slot,done,state,ind;
};

struct hostnet777_client { struct hostnet777_hdr H; int32_t subsock; struct hostnet777_id my; uint64_t balance,tableid; };

struct hostnet777_server
{
    struct hostnet777_hdr H;
    int32_t num,max,pubsock; struct hostnet777_endpoint ep; //queue_t mailboxQ[CARDS777_MAXPLAYERS];
    struct hostnet777_id clients[];
};

void hostnet777_msg(uint64_t destbits,bits256 destpub,union hostnet777 *src,int32_t blindflag,char *jsonstr,int32_t len);

int32_t cards777_testinit(struct hostnet777_server *srv,int32_t M,struct hostnet777_client **clients,int32_t N,int32_t numcards);
bits256 cards777_decode(bits256 *seedp,bits256 *xoverz,int32_t destplayer,bits256 cipher,bits256 *outcards,int32_t numcards,int32_t N);
bits256 cards777_cardpriv(bits256 playerpriv,bits256 *cardpubs,int32_t numcards,bits256 cipher);
uint8_t *cards777_encode(bits256 *encoded,bits256 *xoverz,uint8_t *allshares,uint8_t *myshares[],uint8_t *sharenrs,int32_t M,bits256 *ciphers,int32_t numcards,int32_t N);
bits256 cards777_initdeck(bits256 *cards,bits256 *cardpubs,int32_t numcards,int32_t N,bits256 *playerpubs,bits256 *playerprivs);
int32_t init_sharenrs(unsigned char sharenrs[255],unsigned char *orig,int32_t m,int32_t n);
uint32_t set_handstr(char *handstr,uint8_t cards[7],int32_t verbose);
int32_t hostnet777_idle(union hostnet777 *hn);
void msleep(uint32_t milliseconds);
struct cards777_privdata *cards777_allocpriv(int32_t numcards,int32_t N);
struct cards777_pubdata *cards777_allocpub(int32_t M,int32_t numcards,int32_t N);
struct hostnet777_server *hostnet777_server(bits256 srvprivkey,bits256 srvpubkey,char *transport,char *ipaddr,uint16_t port,int32_t maxclients);
struct hostnet777_client *hostnet777_client(bits256 privkey,bits256 pubkey,char *srvendpoint,int32_t slot);
int32_t hostnet777_register(struct hostnet777_server *srv,bits256 clientpub,int32_t slot);
int32_t cards777_checkcard(bits256 *cardprivp,int32_t cardi,int32_t slot,int32_t destplayer,bits256 playerpriv,bits256 *cardpubs,int32_t numcards,bits256 card);
int32_t hostnet777_init(union hostnet777 *hn,bits256 *privkeys,int32_t num,int32_t launchflag);
int32_t hostnet777_sendmsg(union hostnet777 *ptr,bits256 destpub,bits256 mypriv,bits256 mypub,uint8_t *msg,int32_t len);
int64_t hostnet777_convmT(struct hostnet777_mtime *mT,int64_t othermillitime);
bits256 cards777_pubkeys(bits256 *pubkeys,int32_t numcards,bits256 cmppubkey);
int32_t pangea_tableaddr(struct cards777_pubdata *dp,uint64_t destbits);
int32_t hostnet777_copybits(int32_t reverse,uint8_t *dest,uint8_t *src,int32_t len);
int32_t cards777_validate(bits256 cardpriv,bits256 final,bits256 *cardpubs,int32_t numcards,bits256 *audit,int32_t numplayers,bits256 playerpub);
void *hostnet777_idler(union hostnet777 *ptr);
int32_t nn_socket_status(int32_t sock,int32_t timeoutmillis);
int32_t nn_createsocket(char *endpoint,int32_t bindflag,char *name,int32_t type,uint16_t port,int32_t sendtimeout,int32_t recvtimeout);
void free_queueitem(void *itemptr);
struct pangea_info *pangea_find(uint64_t tableid,int32_t threadid);
int32_t pangea_ind(struct pangea_info *sp,int32_t slot);
int32_t pangea_slot(struct pangea_info *sp,int32_t ind);
int32_t hostnet777_replace(struct hostnet777_server *srv,bits256 clientpub,int32_t slot);

extern int32_t Debuglevel;

#endif
#else
#ifndef hostnet777_c
#define hostnet777_c

#ifndef hostnet777_h
#define DEFINES_ONLY
#include "hostnet777.c"
#undef DEFINES_ONLY
#endif
#include "../includes/tweetnacl.h"
#include "../includes/curve25519.h"


static bits256 zeropoint;

int64_t hostnet777_convmT(struct hostnet777_mtime *mT,int64_t othermillitime)
{
    int64_t lag,millitime,millis = (uint64_t)milliseconds();
    if ( mT->starttime == 0 )
    {
        mT->starttime = (uint32_t)time(NULL);
        mT->millistart = millis;
        printf("set millistart.%p %lld\n",mT,(long long)millis);
    }
    //printf("%p millis.%lld - millistart.%lld = %lld\n",mT,(long long)millis,(long long)mT->millistart,(long long)(millis - mT->millistart));
    millitime = (millis - mT->millistart) + ((long long)mT->starttime * 1000);
    if ( othermillitime != 0 )
    {
        millitime += mT->millidiff;
        lag = (othermillitime - millitime);
        mT->millidiff = (mT->millidiff * .9) + (.1 * lag);
    }
    return(millitime);
}

double hostnet777_updatelag(uint64_t senderbits,int64_t millitime,int64_t now)
{
    printf("updatelag %llu: %lld\n",(long long)senderbits,(long long)(millitime - now));
    return(millitime - now);
}

int32_t hostnet777_send(int32_t sock,void *ptr,int32_t len)
{
    static int32_t numerrs;
    int32_t j,sendlen = 0;
    if ( sock >= 0 )
    {
        for (j=0; j<10; j++)
        {
            if ( (nn_socket_status(sock,100) & NN_POLLOUT) != 0 )
                break;
        }
        if ( j == 10 )
        {
            printf("socket.%d not ready\n",sock);
            return(-1);
        }
        for (j=0; j<10; j++)
        {
            char *nn_err_strerror();
            int32_t nn_err_errno();
            if ( (sendlen= nn_send(sock,ptr,len,0)) == len )
                break;
            if ( numerrs++ < 100 )
                printf("numerrs.%d retry.%d for sock.%d len.%d vs sendlen.%d (%s) (%s)\n",numerrs,j,sock,len,sendlen,(char *)(len<512?ptr:""),nn_err_strerror(nn_err_errno()));
            msleep(100);
        }
        //printf("hostnet777_send.%d j.%d len.%d sendlen.%d\n",sock,j,len,sendlen);
    } else printf("hostnet777_send neg socket\n");
    return(sendlen);
}

struct hostnet777_id *hostnet777_find64(struct hostnet777_server *srv,uint64_t senderbits)
{
    int32_t i;
    if ( srv->num > 0 )
    {
        for (i=0; i<srv->max; i++)
            if ( srv->clients[i].nxt64bits == senderbits )
                return(&srv->clients[i]);
    }
    return(0);
}

int32_t hostnet777_sendsock(union hostnet777 *ptr,uint64_t destbits)
{
    int32_t ind; //struct hostnet777_id *client;
    if ( (ind= ptr->client->H.slot) != 0 )
    {
        //printf("client.%p ind.%d: %d %d\n",ptr->client,ind,ptr->client->pushsock,ptr->client->my.pmsock);
        //if ( 1 || destbits == 0 )
        //    return(ptr->client->pushsock);
        //else
        return(ptr->client->my.pmsock);
    }
    else
    {
        //printf("server.%p ind.%d: %d %d\n",ptr->server,ind,ptr->server->pullsock,ptr->server->pubsock);
        //if ( destbits == 0 )
            return(ptr->server->pubsock);
        /*else if ( (client= hostnet777_find64(ptr->server,destbits)) != 0 )
        {
            //printf("SERVER -> ind.%d: %d %d\n",ind,ptr->server->pubsock,client->pmsock);
            return(client->pmsock);
        } else printf("error cant find %llu in server clients\n",(long long)destbits);*/
    }
    return(-1);
}

struct hostnet777_id *hostnet777_find(struct hostnet777_server *srv,bits256 senderpub)
{
    int32_t i; uint64_t senderbits = acct777_nxt64bits(senderpub);
    if ( srv->num > 0 )
    {
        for (i=0; i<srv->max; i++)
            if ( srv->clients[i].nxt64bits == senderbits )
                return(&srv->clients[i]);
    }
    return(0);
}

void hostnet777_lastcontact(struct hostnet777_server *srv,bits256 senderpub)
{
    struct hostnet777_id *ptr;
    if ( (ptr= hostnet777_find(srv,senderpub)) != 0 )
        ptr->lastcontact = (uint32_t)time(NULL);
}

int32_t hostnet777_copybits(int32_t reverse,uint8_t *dest,uint8_t *src,int32_t len)
{
    int32_t i; uint8_t *tmp;
    if ( reverse != 0 )
    {
        tmp = dest;
        dest = src;
        src = tmp;
    }
    //printf("src.%p dest.%p len.%d\n",src,dest,len);
    //for (i=0; i<len; i++)
    //    dest[i] = 0;
    memset(dest,0,len);
    len <<= 3;
    for (i=0; i<len; i++)
        if ( GETBIT(src,i) != 0 )
            SETBIT(dest,i);
    return(len >> 3);
}

int32_t hostnet777_serialize(int32_t reverse,bits256 *senderpubp,uint64_t *senderbitsp,bits256 *sigp,uint32_t *timestampp,uint64_t *destbitsp,uint8_t *origbuf)
{
    uint8_t *buf = origbuf; long extra = sizeof(bits256) + sizeof(uint64_t) + sizeof(uint64_t);
    buf += hostnet777_copybits(reverse,buf,(void *)destbitsp,sizeof(uint64_t));
    buf += hostnet777_copybits(reverse,buf,senderpubp->bytes,sizeof(bits256));
    buf += hostnet777_copybits(reverse,buf,(void *)senderbitsp,sizeof(uint64_t));
    buf += hostnet777_copybits(reverse,buf,(void *)timestampp,sizeof(uint32_t)), extra += sizeof(uint32_t);
    if ( *senderbitsp != 0 )
        buf += hostnet777_copybits(reverse,buf,sigp->bytes,sizeof(bits256)), extra += sizeof(bits256);
    else memset(sigp,0,sizeof(*sigp));
    if ( ((long)buf - (long)origbuf) != extra )
    {
        printf("hostnet777_serialize: extrasize mismatch %ld vs %ld\n",((long)buf - (long)origbuf),extra);
    }
    return((int32_t)extra);
}

uint8_t *hostnet777_encode(int32_t *cipherlenp,void *str,int32_t len,bits256 destpubkey,bits256 myprivkey,bits256 mypubkey,uint64_t senderbits,bits256 sig,uint32_t timestamp)
{
    uint8_t *buf,*nonce,*cipher,*ptr; uint64_t destbits; int32_t totalsize,hdrlen; long extra = crypto_box_NONCEBYTES + crypto_box_ZEROBYTES + sizeof(sig);
    destbits = (memcmp(destpubkey.bytes,GENESIS_PUBKEY.bytes,sizeof(destpubkey)) != 0) ? acct777_nxt64bits(destpubkey) : 0;
    totalsize = (int32_t)(len + sizeof(mypubkey) + sizeof(senderbits) + sizeof(destbits) + sizeof(timestamp));
    *cipherlenp = 0;
    if ( (buf= calloc(1,totalsize + extra)) == 0 )
    {
        printf("hostnet777_encode: outof mem for buf[%ld]\n",totalsize+extra);
        return(0);
    }
    if ( (cipher= calloc(1,totalsize + extra)) == 0 )
    {
        printf("hostnet777_encode: outof mem for cipher[%ld]\n",totalsize+extra);
        free(buf);
        return(0);
    }
    ptr = cipher;
    hdrlen = hostnet777_serialize(0,&mypubkey,&senderbits,&sig,&timestamp,&destbits,cipher);
    if ( senderbits != 0 )
        totalsize += sizeof(sig);//, printf("totalsize.%d extra.%ld add %ld\n",totalsize-len,extra,(long)(sizeof(sig) + sizeof(timestamp)));
    if ( destbits != 0 && senderbits != 0 )
    {
        totalsize += crypto_box_NONCEBYTES + crypto_box_ZEROBYTES;//, printf("totalsize.%d extra.%ld add %d\n",totalsize-len,extra,crypto_box_NONCEBYTES + crypto_box_ZEROBYTES);
        nonce = &cipher[hdrlen];
        randombytes(nonce,crypto_box_NONCEBYTES);
        cipher = &nonce[crypto_box_NONCEBYTES];
        //printf("len.%d -> %d %d\n",len,len+crypto_box_ZEROBYTES,len + crypto_box_ZEROBYTES + crypto_box_NONCEBYTES);
        memset(cipher,0,len+crypto_box_ZEROBYTES);
        memset(buf,0,crypto_box_ZEROBYTES);
        memcpy(buf+crypto_box_ZEROBYTES,str,len);
        crypto_box(cipher,buf,len+crypto_box_ZEROBYTES,nonce,destpubkey.bytes,myprivkey.bytes);
        hdrlen += crypto_box_NONCEBYTES + crypto_box_ZEROBYTES;
    }
    else memcpy(&cipher[hdrlen],str,len);
    if ( totalsize != len+hdrlen )
        printf("unexpected totalsize.%d != len.%d + hdrlen.%d %d\n",totalsize,len,hdrlen,len+hdrlen);
    free(buf);
    *cipherlenp = totalsize;
    return(ptr);
}

int32_t hostnet777_decode(uint64_t *senderbitsp,bits256 *sigp,uint32_t *timestampp,uint64_t *destbitsp,uint8_t *str,uint8_t *cipher,int32_t *lenp,uint8_t *myprivkey)
{
    bits256 srcpubkey; uint8_t *nonce; int i,hdrlen,err=0,len = *lenp;
    hdrlen = hostnet777_serialize(1,&srcpubkey,senderbitsp,sigp,timestampp,destbitsp,cipher);
    cipher += hdrlen, len -= hdrlen;
    if ( *destbitsp != 0 && *senderbitsp != 0 )
    {
        nonce = cipher;
        cipher += crypto_box_NONCEBYTES, len -= crypto_box_NONCEBYTES;
        err = crypto_box_open((uint8_t *)str,cipher,len,nonce,srcpubkey.bytes,myprivkey);
        for (i=0; i<len-crypto_box_ZEROBYTES; i++)
            str[i] = str[i+crypto_box_ZEROBYTES];
        *lenp = len - crypto_box_ZEROBYTES;
    } else memcpy(str,cipher,len);
    return(err);
}

int32_t hostnet777_decrypt(bits256 *senderpubp,uint64_t *senderbitsp,uint32_t *timestampp,bits256 mypriv,bits256 mypub,uint8_t *dest,int32_t maxlen,uint8_t *src,int32_t len)
{
    bits256 seed,sig,msgpriv; uint64_t my64bits,destbits,senderbits,sendertmp,desttmp;
    uint8_t *buf; int32_t hdrlen,i,diff,newlen = -1; HUFF H,*hp = &H; struct acct777_sig checksig;
    *senderbitsp = 0;
    my64bits = acct777_nxt64bits(mypub);
    if ( (buf = calloc(1,maxlen)) == 0 )
    {
        printf("hostnet777_decrypt cant allocate maxlen.%d\n",maxlen);
        return(-1);
    }
    hdrlen = hostnet777_serialize(1,senderpubp,&senderbits,&sig,timestampp,&destbits,src);
    if ( destbits != 0 && my64bits != destbits && destbits != acct777_nxt64bits(GENESIS_PUBKEY) )
    {
        free(buf);
        printf("hostnet777_decrypt received destination packet.%llu when my64bits.%llu len.%d\n",(long long)destbits,(long long)my64bits,len);
        return(-1);
    }
    if ( memcmp(mypub.bytes,senderpubp->bytes,sizeof(mypub)) == 0 )
    {
        if ( destbits != 0 )
            printf("hostnet777: got my own msg?\n");
    }
//printf("decrypt(%d) destbits.%llu my64.%llu mypriv.%llx mypub.%llx senderpub.%llx shared.%llx\n",len,(long long)destbits,(long long)my64bits,(long long)mypriv.txid,(long long)mypub.txid,(long long)senderpubp->txid,(long long)seed.txid);
    if ( hostnet777_decode(&sendertmp,&sig,timestampp,&desttmp,(void *)buf,src,&len,mypriv.bytes) == 0 )
    {
        if ( (diff= (*timestampp - (uint32_t)time(NULL))) < 0 )
            diff = -diff;
        if ( 0 && diff > HOSTNET777_MAXTIMEDIFF )
            printf("diff.%d > %d %u vs %u\n",diff,HOSTNET777_MAXTIMEDIFF,*timestampp,(uint32_t)time(NULL));
        else
        {
            if ( 1 )
            {
                memset(seed.bytes,0,sizeof(seed));
                for (i='0'; i<='9'; i++)
                    SETBIT(seed.bytes,i);
                for (i='a'; i<='f'; i++)
                    SETBIT(seed.bytes,i);
                _init_HUFF(hp,len,buf), hp->endpos = (len << 3);
                newlen = ramcoder_decoder(0,1,dest,maxlen,hp,&seed);
            }
            else memcpy(dest,buf,len), newlen = len;
            //printf("T%d decrypted newlen.%d\n",threadid,newlen);
            if ( senderbits != 0 && senderpubp->txid != 0 )
            {
                *senderbitsp = senderbits;
                if ( destbits == 0 )
                    msgpriv = GENESIS_PRIVKEY;
                else msgpriv = mypriv;
                acct777_sign(&checksig,msgpriv,*senderpubp,*timestampp,dest,newlen);
                if ( memcmp(checksig.sigbits.bytes,&sig,sizeof(checksig.sigbits)) != 0 )
                {
                    printf("sender.%llu sig %llx compare error vs %llx using sig->pub from %llu, broadcast.%d\n",(long long)senderbits,(long long)sig.txid,(long long)checksig.sigbits.txid,(long long)senderbits,destbits == 0);
                    //free(buf);
                    //return(0);
                } //else printf("SIG VERIFIED newlen.%d (%llu -> %llu)\n",newlen,(long long)senderbits,(long long)destbits);
            }
        }
    }
    else printf("%llu: hostnet777_decrypt skip: decode_cipher error len.%d -> newlen.%d\n",(long long)acct777_nxt64bits(mypub),len,newlen);
    free(buf);
    return(newlen);
}

int32_t hostnet777_hashes(uint64_t *hashes,int32_t n,uint8_t *msg,int32_t len)
{
    int32_t i,firsti = -1; bits256 hash;
    calc_sha256(0,hash.bytes,msg,len);
    printf("msg.%p len.%d hash.%llx\n",msg,len,(long long)hash.txid);
    for (i=0; i<n; i++)
    {
        if ( hashes[i] == 0 && firsti < 0 )
            firsti = i;
        if ( hash.txid == hashes[i] )
        {
            printf("filter duplicate msg %llx\n",(long long)hash.txid);
            return(i);
        }
    }
    if ( firsti >= 0 )
        hashes[firsti] = hash.txid;
    else
    {
        for (i=n-1; i>0; i--)
            hashes[i] = hashes[i-1];
        hashes[0] = hash.txid;
    }
    return(-1);
}

void hostnet777_processmsg(uint64_t *destbitsp,bits256 *senderpubp,queue_t *Q,bits256 mypriv,bits256 mypub,uint8_t *msg,int32_t origlen,int32_t pmflag,struct hostnet777_mtime *mT)
{
    char *jsonstr = 0; bits256 sig; uint32_t timestamp; int32_t len; uint64_t senderbits,now,millitime; uint8_t *ptr=0; cJSON *json; long extra;
    extra = sizeof(*senderpubp) + sizeof(*destbitsp) + sizeof(sig) + sizeof(senderbits) + sizeof(timestamp);
    if ( (len= origlen) > extra )
    {
        //printf("got msglen.%d\n",origlen);
        if ( (ptr= malloc(len*4 + 8192 + sizeof(struct queueitem) - extra)) == 0 )
        {
            printf("hostnet777_processmsg cant alloc queueitem\n");
            return;
        }
        if ( (len= hostnet777_decrypt(senderpubp,&senderbits,&timestamp,mypriv,mypub,&ptr[sizeof(struct queueitem)],len*4,msg,len)) > 1 && len < len*4 )
        {
            jsonstr = (char *)&ptr[sizeof(struct queueitem)];
            if ( (json= cJSON_Parse(jsonstr)) != 0 )
            {
                millitime = j64bits(json,"millitime");
                now = hostnet777_convmT(mT,millitime);
                //printf("now.%lld vs millitime.%lld lag.%lld\n",(long long)now,(long long)millitime,(long long)(millitime - now));
                if ( pmflag != 0 && juint(json,"timestamp") != timestamp && juint(json,"timestamp")+1 != timestamp )
                    printf("msg.(%s) timestamp.%u mismatch | now.%ld\n",jsonstr,timestamp,(long)time(NULL));
                else if ( pmflag != 0 && j64bits(json,"sender") != senderbits )
                    printf("msg.(%ld) sender.%llu mismatch vs json.%llu\n",(long)strlen(jsonstr),(long long)senderbits,(long long)j64bits(json,"sender"));
                else
                {
                    //printf("%llu: QUEUE msg.%d\n",(long long)acct777_nxt64bits(mypub),len);
                    //if ( hostnet777_hashes(recvhashes,64,ptr,len) < 0 )
                        queue_enqueue("host777",Q,(void *)ptr,0);
                    ptr = 0;
                }
                free_json(json);
            } else printf("parse error.(%s)\n",jsonstr);
        } else printf("decrypt error len.%d origlen.%d\n",len,origlen);
    } else printf("origlen.%d\n",origlen);
    if ( ptr != 0 )
        free(ptr);
}

/*void hostnet777_mailboxQ(queue_t *mailboxQ,void *cipher,int32_t cipherlen)
{
    uint16_t *ptr; struct queueitem *item = calloc(1,sizeof(struct queueitem) + cipherlen + sizeof(uint16_t));
    ptr = (uint16_t *)((long)item + sizeof(struct queueitem));
    ptr[0] = cipherlen;
    memcpy(&ptr[1],cipher,cipherlen);
    queue_enqueue("mailboxQ",mailboxQ,item);
}*/

#define hostnet777_broadcast(ptr,mypriv,mypub,msg,len) hostnet777_sendmsg(ptr,zeropoint,mypriv,mypub,msg,len)
#define hostnet777_blindcast(ptr,msg,len) hostnet777_sendmsg(ptr,zeropoint,zeropoint,zeropoint,msg,len)
#define hostnet777_signedPM(ptr,destpub,mypriv,mypub,msg,len) hostnet777_sendmsg(ptr,destpub,mypriv,mypub,msg,len)
#define hostnet777_blindPM(ptr,destpub,msg,len) hostnet777_sendmsg(ptr,destpub,zeropoint,zeropoint,msg,len)

int32_t hostnet777_sendmsg(union hostnet777 *ptr,bits256 destpub,bits256 mypriv,bits256 mypub,uint8_t *msg,int32_t len)
{
    int32_t cipherlen,datalen,sendsock,i; bits256 seed; uint8_t *data=0,*cipher; uint64_t destbits; struct acct777_sig sig; HUFF H,*hp = &H;
    if ( destpub.txid != 0 )
        destbits = acct777_nxt64bits(destpub);
    else
    {
        destbits = 0;
        destpub = GENESIS_PUBKEY;
    }
    //printf("hostnet777_sendmsg dest.%llu destpub.%llx priv.%llx pub.%llx\n",(long long)destbits,(long long)destpub.txid,(long long)mypriv.txid,(long long)mypub.txid);
    memset(&sig,0,sizeof(sig));
    if ( mypub.txid == 0 || mypriv.txid == 0 )
        mypriv = curve25519_keypair(&mypub), sig.timestamp = (uint32_t)time(NULL);
    else acct777_sign(&sig,mypriv,destpub,(uint32_t)time(NULL),msg,len);
    if ( (sendsock= hostnet777_sendsock(ptr,mypriv.txid != 0 ? destbits : 0)) < 0 )
    {
        printf("%llu: ind.%d no sendsock for %llx -> %llu\n",(long long)ptr->client->H.nxt64bits,ptr->client->H.slot,(long long)acct777_nxt64bits(mypub),(long long)destbits);
        return(-1);
    }
    if ( 1 )
    {
        memset(seed.bytes,0,sizeof(seed));
        data = calloc(1,len*2);
        _init_HUFF(hp,len*2,data);
        for (i='0'; i<='9'; i++)
            SETBIT(seed.bytes,i);
        for (i='a'; i<='f'; i++)
            SETBIT(seed.bytes,i);
        ramcoder_encoder(0,1,msg,len,hp,0,&seed);
        datalen = hconv_bitlen(hp->bitoffset);
    }
    else data = msg, datalen = len;
    if ( (cipher= hostnet777_encode(&cipherlen,data,datalen,destpub,mypriv,mypub,sig.signer64bits,sig.sigbits,sig.timestamp)) != 0 )
    {
        hostnet777_send(sendsock,cipher,cipherlen);
        free(cipher);
    }
    if ( data != msg )
        free(data);
    return(cipherlen);
}

int32_t hostnet777_idle(union hostnet777 *hn)
{
    int32_t len,slot,sock,n = 0; bits256 senderpub,mypriv,mypub; uint64_t destbits; uint8_t *msg;
    long extra = sizeof(bits256)+sizeof(uint64_t);
    if ( (slot= hn->client->H.slot) != 0 )
    {
        mypriv = hn->client->H.privkey, mypub = hn->client->H.pubkey;
        if ( (sock= hn->client->subsock) >= 0 && (len= nn_recv(sock,&msg,NN_MSG,0)) > extra )
        {
            hostnet777_copybits(1,msg,(void *)&destbits,sizeof(uint64_t));
            //printf("client got pub len.%d\n",len);
            if ( destbits == 0 || destbits == hn->client->H.nxt64bits )
                hostnet777_processmsg(&destbits,&senderpub,&hn->client->H.Q,mypriv,mypub,msg,len,0,&hn->client->H.mT), n++;
            nn_freemsg(msg);
        } else if ( hn->client->H.pollfunc != 0 )
            (*hn->client->H.pollfunc)(hn);
    }
    else
    {
        //printf("server idle %.0f\n",milliseconds());
        mypriv = hn->server->H.privkey, mypub = hn->server->H.pubkey;
        for (slot=1; slot<hn->server->num; slot++)
        {
            //printf("check ind.%d %.0f\n",ind,milliseconds());
            if ( (sock= hn->server->clients[slot].pmsock) >= 0 && (len= nn_recv(sock,&msg,NN_MSG,0)) > extra )
            {
                //printf("server got pm[%d] %d\n",slot,len);
                hostnet777_copybits(1,msg,(void *)&destbits,sizeof(uint64_t));
                if ( destbits == 0 || destbits == hn->server->H.nxt64bits )
                {
                    hostnet777_processmsg(&destbits,&senderpub,&hn->server->H.Q,mypriv,mypub,msg,len,1,&hn->server->H.mT);
                    hostnet777_lastcontact(hn->server,senderpub);
                }
                hostnet777_send(hn->server->pubsock,msg,len);
                nn_freemsg(msg);
            }
        }
        if ( hn->server->H.pollfunc != 0 )
            (*hn->server->H.pollfunc)(hn);
    }
    return(n);
}

int32_t hostnet777_replace(struct hostnet777_server *srv,bits256 clientpub,int32_t slot)
{
    char endpoint[128],buf[128]; uint64_t nxt64bits = acct777_nxt64bits(clientpub);
    sprintf(endpoint,"%s://%s:%u",srv->ep.transport,srv->ep.ipaddr,srv->ep.port + slot + 1);
    //sprintf(buf,"%s://127.0.0.1:%u",srv->ep.transport,srv->ep.port + slot + 1);
    strcpy(buf,endpoint);
    if ( srv->clients[slot].pmsock < 0 )
        srv->clients[slot].pmsock = nn_createsocket(buf,1,"NN_PULL",NN_PULL,srv->ep.port + slot + 1,10,10);
    printf("NN_PULL.%d for slot.%d\n",srv->clients[slot].pmsock,slot);
    srv->clients[slot].pubkey = clientpub;
    srv->clients[slot].nxt64bits = nxt64bits;
    srv->clients[slot].lastcontact = (uint32_t)time(NULL);
    return(srv->clients[slot].pmsock);
}

int32_t hostnet777_register(struct hostnet777_server *srv,bits256 clientpub,int32_t slot)
{
    int32_t i,n; struct hostnet777_id *ptr;
    if ( slot < 0 )
    {
        if ( (ptr= hostnet777_find(srv,clientpub)) != 0 )
        {
            slot = (int32_t)(((long)ptr - (long)srv->clients) / sizeof(*srv->clients));
            //printf("hostnet777_register: deregister slot.%d\n",slot);
            if ( ptr->pmsock >= 0 )
                nn_shutdown(ptr->pmsock,0);
            memset(ptr,0,sizeof(*ptr));
            ptr->pmsock = -1;
            srv->num--;
            return(-1);
        }
        for (slot=1; slot<srv->max; slot++)
            if ( srv->clients[slot].nxt64bits == 0 )
                break;
    }
    if ( srv->num >= srv->max )
    {
        printf("hostnet777_register: cant register anymore num.%d vs max.%d\n",srv->num,srv->max);
        return(-1);
    }
    if ( (ptr= hostnet777_find(srv,clientpub)) != 0 )
    {
        printf("hostnet777_register: cant register duplicate %llu\n",(long long)acct777_nxt64bits(clientpub));
        return((int32_t)(((long)ptr - (long)srv->clients) / sizeof(*srv->clients)));
    }
    if ( slot != srv->num )
    {
        printf("hostnet777_register: cant register slot.%d vs num.%d vs max.%d\n",slot,srv->num,srv->max);
        return(-1);
    }
    hostnet777_replace(srv,clientpub,slot);
    srv->num++;
    for (i=n=0; i<srv->max; i++)
        if ( srv->clients[i].nxt64bits != 0 )
            n++;
    if ( n != srv->num )
    {
        printf("mismatched nonz nxt64bits n.%d vs %d\n",n,srv->num);
        srv->num = n;
    }
    return(slot);
}

struct hostnet777_client *hostnet777_client(bits256 privkey,bits256 pubkey,char *srvendpoint,int32_t slot)
{
    char endbuf[128],endbuf2[128]; uint16_t port; struct hostnet777_client *ptr;
    ptr = calloc(1,sizeof(*ptr));
    ptr->H.slot = slot;
    ptr->H.privkey = privkey, ptr->H.pubkey = ptr->my.pubkey = pubkey;
    ptr->H.nxt64bits = ptr->my.nxt64bits = acct777_nxt64bits(pubkey);
    ptr->my.lastcontact = (uint32_t)time(NULL);
    strcpy(endbuf,srvendpoint);
    endbuf[strlen(endbuf)-4] = 0;
    port = atoi(&srvendpoint[strlen(endbuf)]);
    sprintf(endbuf2,"%s%u",endbuf,port + 1 + slot);
    ptr->my.pmsock = nn_createsocket(endbuf2,0,"NN_PUSH",NN_PUSH,0,10,100);
    printf("NN_PUSH %d from (%s) port.%d\n",ptr->my.pmsock,endbuf2,port+1+slot);
    sprintf(endbuf2,"%s%u",endbuf,port);
    ptr->subsock = nn_createsocket(endbuf2,0,"NN_SUB",NN_SUB,0,10,100);
    printf("SUB %d from (%s) port.%d\n",ptr->subsock,endbuf2,port);
    nn_setsockopt(ptr->subsock,NN_SUB,NN_SUB_SUBSCRIBE,"",0);
    //sprintf(endbuf2,"%s%u",endbuf,port);
    //ptr->pushsock = nn_createsocket(endbuf2,0,"NN_PUSH",NN_PUSH,0,10,1);
    //printf("PUSH %d to (%s)\n",ptr->pushsock,endbuf2);
    return(ptr);
}

void hostnet777_freeclient(struct hostnet777_client *client)
{
    client->H.done = 1;
    if ( client->subsock >= 0 )
        nn_shutdown(client->subsock,0);
    //if ( client->pushsock >= 0 )
    //    nn_shutdown(client->pushsock,0);
    if ( client->my.pmsock >= 0 )
        nn_shutdown(client->my.pmsock,0);
}

void hostnet777_freeserver(struct hostnet777_server *srv)
{
    int32_t ind;
    srv->H.done = 1;
    //if ( srv->pullsock >= 0 )
    //    nn_shutdown(srv->pullsock,0);
    if ( srv->pubsock >= 0 )
        nn_shutdown(srv->pubsock,0);
    for (ind=1; ind<srv->max; ind++)
    {
        if ( srv->clients[ind].pmsock >= 0 )
            nn_shutdown(srv->clients[ind].pmsock,0);
    }
}

struct hostnet777_server *hostnet777_server(bits256 srvprivkey,bits256 srvpubkey,char *transport,char *ipaddr,uint16_t port,int32_t maxclients)
{
    struct hostnet777_server *srv; int32_t i; struct hostnet777_endpoint *ep; char buf[128];
    srv = calloc(1,sizeof(*srv) + maxclients*sizeof(struct hostnet777_id));
    srv->max = maxclients;
    ep = &srv->ep;
    if ( (ep->port= port) == 0 )
        ep->port = port = 8000 + (rand() % 1000);
    if ( transport == 0 || transport[0] == 0 )
        transport = TEST_TRANSPORT;
    if ( ipaddr == 0 || ipaddr[0] == 0 )
        ipaddr = "127.0.0.1";
    strcpy(ep->transport,transport), strcpy(ep->ipaddr,ipaddr);
    for (i=0; i<maxclients; i++)
        srv->clients[i].pmsock = -1;
    srv->H.privkey = srvprivkey;
    srv->H.pubkey = srv->clients[0].pubkey = srvpubkey;
    srv->H.nxt64bits = srv->clients[0].nxt64bits = acct777_nxt64bits(srvpubkey);
    sprintf(ep->endpoint,"%s://%s:%u",transport,ipaddr,port);
    if ( strcmp(transport,"tcpmux") == 0 )
        strcat(ep->endpoint,"/pangea");
    //sprintf(buf,"%s://127.0.0.1:%u",transport,port);
    strcpy(buf,ep->endpoint);
    srv->pubsock = nn_createsocket(buf,1,"NN_PUB",NN_PUB,port,10,100);
    printf("PUB.%d to (%s) pangeaport.%d\n",srv->pubsock,ep->endpoint,port);
    srv->num = 1;
    return(srv);
}

void *hostnet777_idler(union hostnet777 *ptr)
{
    while ( ptr->client->H.done == 0 )
    {
        if ( hostnet777_idle(ptr) == 0 )
            msleep(1);
    }
    //printf("hostnet777_idler ind.%d done\n",ptr->client->H.slot);
    sleep(1);
    free(ptr);
    return(0);
}

void hostnet777_msg(uint64_t destbits,bits256 destpub,union hostnet777 *src,int32_t blindflag,char *jsonstr,int32_t len)
{
    if ( destbits == 0 )
    {
        //printf(">>>>>>>>> blind.%d broadcast from %llu, len.%d\n",blindflag,(long long)src->client->H.nxt64bits,len);
        if ( blindflag != 0 )
            hostnet777_blindcast(src,(uint8_t *)jsonstr,len);
        else hostnet777_broadcast(src,src->client->H.privkey,src->client->H.pubkey,(uint8_t *)jsonstr,len);
        if ( src->server->H.slot == 0 )
            queue_enqueue("loopback",&src->client->H.Q,queueitem(jsonstr),1);
    }
    else if ( destbits != src->client->H.nxt64bits )
    {
        //printf(">>>>>>>>> blind.%d PM from %llu to %llu\n",blindflag,(long long)src->client->H.nxt64bits,(long long)destbits);
        if ( blindflag != 0 )
            hostnet777_blindPM(src,destpub,(uint8_t *)jsonstr,len);
        else hostnet777_signedPM(src,destpub,src->client->H.privkey,src->client->H.pubkey,(uint8_t *)jsonstr,len);
    }
    else queue_enqueue("loopback",&src->client->H.Q,queueitem(jsonstr),1);
}

int32_t hostnet777_init(union hostnet777 *hn,bits256 *privkeys,int32_t num,int32_t launchflag)
{
    bits256 pubkey; int32_t slot,threadid; struct hostnet777_server *srv=0;
    for (threadid=0; threadid<num; threadid++)
    {
        pubkey = acct777_pubkey(privkeys[threadid]);
        if ( threadid == 0 )
        {
            if ( (srv= hostnet777_server(privkeys[threadid],pubkey,0,0,0,num)) == 0 )
            {
                printf("cant create hostnet777 server\n");
                return(-1);
            }
            hn[0].server = srv;
            srv->H.privkey = privkeys[threadid], srv->H.pubkey = pubkey;
            if ( launchflag != 0 && portable_thread_create((void *)hostnet777_idler,&hn[0]) == 0 )
                printf("error launching server thread\n");
        }
        else
        {
            if ( (slot= hostnet777_register(srv,pubkey,-1)) >= 0 )
            {
                if ( (hn[threadid].client= hostnet777_client(privkeys[threadid],pubkey,srv->ep.endpoint,slot)) == 0 )
                    printf("error creating clients[%d]\n",threadid);
                else
                {
                    hn[threadid].client->H.privkey = privkeys[threadid], hn[threadid].client->H.pubkey = pubkey;
                    printf("slot.%d client.%p -> %llu pubkey.%llx\n",slot,hn[threadid].client,(long long)hn[threadid].client->H.nxt64bits,(long long)hn[threadid].client->H.pubkey.txid);
                    if ( launchflag != 0 && portable_thread_create((void *)hostnet777_idler,&hn[threadid]) == 0 )
                        printf("error launching clients[%d] thread\n",threadid);
                }
            }
        }
    }
    return(num);
}
     
int32_t hostnet777_block(struct hostnet777_server *srv,uint64_t *senderbitsp,uint32_t *timestampp,union hostnet777 *hn,uint8_t *data,int32_t len,uint8_t *buf,int32_t maxmicro,int32_t blind,int32_t revealed)
{
    static int32_t errs;
    char *jsonstr,*hexstr,*cmdstr,*handstr,tmp[128]; cJSON *json; void *val; struct cards777_privdata *priv; struct cards777_pubdata *dp;
    int32_t i,j,cardi,bestj,destplayer,card,senderslot,retval = -1; bits256 cardpriv; uint32_t rank,bestrank; struct pangea_info *sp;
    *senderbitsp = 0;
    if ( hn == 0 || hn->client == 0 )
    {
        printf("null hn.%p %p\n",hn,hn!=0?hn->client:0);
        return(-1);
    }
    dp = srv->clients[hn->client->H.slot].pubdata;
    sp = dp->table;
    priv = srv->clients[hn->client->H.slot].privdata;
    for (i=0; i<maxmicro; i++)
    {
        if ( (jsonstr= queue_dequeue(&hn->client->H.Q,1)) != 0 )
        {
            //printf("DEQ.(%s)\n",jsonstr);
            if ( (json= cJSON_Parse(jsonstr)) != 0 )
            {
                *senderbitsp = j64bits(json,"sender");
                *timestampp = juint(json,"timestamp");
                if ( (hexstr= jstr(json,"data")) != 0 && strlen(hexstr) == (juint(json,"n")<<1) )
                {
                    decode_hex(buf,len,hexstr);
                    if ( memcmp(buf,data,len) == 0 )
                    {
                        val = hostnet777_find64(srv,*senderbitsp);
                        //printf("blind.%d val.%p\n",blind,val);
                        if ( (blind == 0 && val != 0) || (blind != 0 && val == 0) )
                        {
                            if ( (cmdstr= jstr(json,"cmd")) != 0 )
                            {
                                cardi = juint(json,"cardi");
                                destplayer = juint(json,"dest");
                                senderslot = juint(json,"myslot");
                                if ( strcmp(cmdstr,"pubstr") == 0 )
                                {
                                    //printf("player.%d got pubstr\n",hn->client->H.slot);
                                    memcpy(dp->hand.cardpubs,buf,len);
                                    //if ( (nrs= jstr(json,"sharenrs")) != 0 )
                                    //    decode_hex(dp->hand.sharenrs,(int32_t)strlen(nrs)>>1,nrs);
                                    memset(dp->hand.handranks,0,sizeof(dp->hand.handranks));
                                    memset(priv->hole,0,sizeof(priv->hole));
                                    memset(priv->holecards,0,sizeof(priv->holecards));
                                    memset(dp->hand.community,0,sizeof(dp->hand.community));
                                    dp->hand.handmask = 0;
                                    dp->numhands++;
                                    dp->button++;
                                    if ( dp->button >= dp->N )
                                        dp->button = 0;
                                    exit(1);
                                    printf("deprecated\n");
                                    //sp->balances[pangea_slot(dp->button)]--, dp->balances[(pangea_slot(dp->button) + 1) % dp->N] -= 2;
                                }
                                else if ( strcmp(cmdstr,"encode") == 0 )
                                {
                                    if ( Debuglevel > 2 )
                                        printf("player.%d encodes\n",hn->client->H.slot);
                                    cards777_encode(priv->outcards,priv->xoverz,priv->allshares,priv->myshares,dp->hand.sharenrs[pangea_ind(dp->table,hn->client->H.slot)],dp->M,(void *)buf,dp->numcards,dp->N);
                                }
                                else if ( strcmp(cmdstr,"final") == 0 )
                                    memcpy(dp->hand.final,buf,sizeof(*dp->hand.final) * dp->N * dp->numcards);
                                else if ( strcmp(cmdstr,"decode") == 0 )
                                {
                                    if ( (card= cards777_checkcard(&cardpriv,cardi,pangea_ind(dp->table,hn->client->H.slot),destplayer,hn->client->H.privkey,dp->hand.cardpubs,dp->numcards,*(bits256 *)buf)) >= 0 )
                                        printf("ERROR: player.%d got card.[%d]\n",hn->client->H.slot,card);
                                    printf("deprecated incards, change to audits\n");
                                    //memcpy(&priv->incards[cardi*dp->N + destplayer],buf,sizeof(bits256));
                                }
                                else if ( strcmp(cmdstr,"card") == 0 )
                                {
                                    if ( (card= cards777_checkcard(&cardpriv,cardi,pangea_ind(dp->table,hn->client->H.slot),destplayer,hn->client->H.privkey,dp->hand.cardpubs,dp->numcards,*(bits256 *)buf)) >= 0 )
                                    {
                                        //printf("player.%d got card.[%d]\n",hn->client->H.slot,card);
                                        printf("deprecated incards, change to audits\n");
                                        //memcpy(&priv->incards[cardi*dp->N + destplayer],cardpriv.bytes,sizeof(bits256));
                                    }
                                    else printf("ERROR player.%d got no card\n",hn->client->H.slot);
                                }
                                else if ( strcmp(cmdstr,"facedown") == 0 )
                                {
                                    //printf("player.%d sees that destplayer.%d got card\n",hn->client->H.slot,destplayer);
                                }
                                else if ( strcmp(cmdstr,"faceup") == 0 )
                                {
                                    if ( revealed < 0 || revealed != buf[1] )
                                        printf(">>>>>>>>>>>>>>> ERROR ");
                                    //printf("player.%d was REVEALED.[%d] (%s) cardi.%d\n",hn->client->H.slot,buf[1],hexstr,cardi);
                                    dp->hand.community[cardi - 2*dp->N] = buf[1];
                                }
                                else if ( strcmp(cmdstr,"showdown") == 0 )
                                {
                                    if ( (handstr= jstr(json,"hand")) != 0 )
                                    {
                                        rank = set_handstr(tmp,buf,0);
                                        if ( strcmp(handstr,tmp) != 0 || rank != juint(json,"rank") )
                                            printf("checkhand.(%s) != (%s) || rank.%u != %u\n",tmp,handstr,rank,juint(json,"rank"));
                                        else
                                        {
                                            //printf("sender.%d (%s) (%d %d)\n",senderslot,handstr,buf[5],buf[6]);
                                            dp->hand.handranks[senderslot] = rank;
                                            memcpy(dp->hand.hands[senderslot],buf,7);
                                            dp->hand.handmask |= (1 << senderslot);
                                            if ( dp->hand.handmask == (1 << dp->N)-1 )
                                            {
                                                bestj = 0;
                                                bestrank = dp->hand.handranks[0];
                                                for (j=1; j<dp->N; j++)
                                                    if ( dp->hand.handranks[j] > bestrank )
                                                    {
                                                        bestrank = dp->hand.handranks[j];
                                                        bestj = j;
                                                    }
                                                rank = set_handstr(tmp,dp->hand.hands[bestj],0);
                                                if ( rank == bestrank )
                                                {
                                                    for (j=0; j<dp->N; j++)
                                                    {
                                                        rank = set_handstr(tmp,dp->hand.hands[j],0);
                                                        if ( tmp[strlen(tmp)-1] == ' ' )
                                                            tmp[strlen(tmp)-1] = 0;
                                                        printf("%14s|",tmp[0]!=' '?tmp:tmp+1);
                                                        //printf("(%2d %2d).%d ",dp->hands[j][5],dp->hands[j][6],(int32_t)dp->balances[j]);
                                                    }
                                                    rank = set_handstr(tmp,dp->hand.hands[bestj],0);
                                                    printf("deprecated\n");
                                                    /*dp->balances[bestj] += 3;
                                                    printf("->P%d $%-5lld %s N%d p%d $%d\n",bestj,(long long)dp->balances[bestj],tmp,dp->numhands,hn->client->H.slot,(int32_t)dp->balances[pangea_ind(dp->table,hn->client->H.slot)]);*/
                                                } else printf("bestrank.%u mismatch %u\n",bestrank,rank);
                                            }
                                            //printf("player.%d got rank %u (%s) from %d\n",hn->client->H.slot,rank,handstr,senderslot);
                                        }
                                    }
                                }
                            }
                            retval = 0;
                        }
                    } else printf("NXT.%llu data mismatch %08x [%llx] vs [%llx] %08x len.%d (%s)\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),_crc32(0,data,len),*(long long *)data,*(long long *)buf,_crc32(0,buf,len),len,jsonstr);
                } else printf("NXT.%llu invalid hexstr.%p %ld %d\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),jsonstr,hexstr!=0?(long)strlen(hexstr):0,len);
                free_json(json);
            } else printf("NXT.%llu cant parse.(%s)\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),jsonstr);
            free_queueitem(jsonstr);
            break;
        }
        usleep(1);
    }
    if ( i == maxmicro )
        printf("NXT.%llu timeout.%d\n",(long long)acct777_nxt64bits(hn->client->H.pubkey),i);
    else
    {
        static uint64_t sum,count,max;
        sum += (i+1);
        count++;
        if ( i > max )
            max = i;
        if ( (count % 10000) == 9999 )
            printf("us.%-6d completed | ave %.1f %llu max.%llu errs.%d\n",i,(double)sum/count,(long long)count,(long long)max,errs);
    }
    if ( retval != 0 )
        errs++;
    return(retval);
}

int32_t hostnet777_testresult(struct hostnet777_server *srv,struct hostnet777_client **clients,int32_t numclients,union hostnet777 *src,union hostnet777 *dest,int32_t blind,uint8_t *data,int32_t len,void *buf,int32_t revealed)
{
    uint64_t senderbits; uint32_t timestamp,maxmicro = 100000; int32_t i,n,retval = -1; union hostnet777 hn;
    if ( dest != 0 && dest->client != 0 )
    {
        //printf("PM call block on %d %llu\n",dest->client->H.slot,(long long)dest->client->H.nxt64bits);
        if ( hostnet777_block(srv,&senderbits,&timestamp,dest,data,len,buf,maxmicro,blind,revealed) == 0 )
            retval = 0;
    }
    else if ( dest == 0 || dest->client == 0 )
    {
        if ( dest == 0 )
            dest = &hn, dest->server = 0;
        for (i=n=0; i<numclients; i++)
        {
            if ( i == 0 )
                dest->server = srv;
            else dest->client = clients[i];
            //printf("broadcast call block on %d %llu\n",i,(long long)dest->client->H.nxt64bits);
            if ( hostnet777_block(srv,&senderbits,&timestamp,dest,data,len,buf,maxmicro,blind,revealed) == 0 )
                n++;//, printf("verified.%d\n",i);
        }
        if ( n == numclients )
            retval = 0;
    }
    if ( retval != 0 )
    {
        for (i=1; i<numclients; i++)
            printf("%llu ",(long long)clients[i]->H.nxt64bits);
        printf("<<<<<<<<<<<<<<< srv.%llu ERROR.(%s)\n\n",(long long)srv->H.nxt64bits,(char *)buf);
    }// else printf("<<<<<<<<<<<<<<< PASS\n\n");
    return(retval);
}

int32_t hostnet777_testiter(struct hostnet777_server *srv,struct hostnet777_client **clients,int32_t numclients,int32_t mode,int32_t iter)
{
    int32_t s,d,blindflag,len,n,i,j,k,hexlen,cardi,destplayer,revealed,retval = -1; uint32_t rank; cJSON *json;
    union hostnet777 src,dest; uint64_t srcbits; char *cmdstr,*hex,pubstr[52*9*64+1],nrs[512],handstr[128];
    uint8_t data[32768]; struct cards777_privdata *priv; struct cards777_pubdata *dp; bits256 destpub,card,seed;
    hex = malloc(sizeof(data) * 3 + 1024);
    revealed = -1;
    rank = pubstr[0] = nrs[0] = handstr[0] = 0;
    if ( mode == 0 )
    {
        cmdstr = "test";
        cardi = destplayer = -1;
        if ( (s= (rand() % numclients)) == 0 )
            src.server = srv;
        else src.client = clients[s];
        i = s;
        srcbits = src.client->H.nxt64bits;
        if ( (d= (rand() % (numclients+1))) == 0 )
            dest.server = srv, destpub = srv->H.pubkey;
        else if ( d < numclients )
            dest.client = clients[d], destpub = clients[d]->H.pubkey;
        else dest.client = 0;
        if ( (blindflag = ((rand() & 256) != 0)) != 0 )
            srcbits = 0;
        len = (rand() % (sizeof(data)-10)) + 10;
        randombytes(data,len);
    }
    else
    {
        blindflag = 0;
        cardi = destplayer = -1;
        if ( (i= iter) < numclients )
        {
            dp = srv->clients[i].pubdata;
            priv = srv->clients[i].privdata;
            if ( i < numclients-1 )
            {
                if ( iter == 0 )
                {
                    printf("deprecated\n");
                    exit(1);
                    /*bits256 playerpubs[CARDS777_MAXPLAYERS];
                    for (i=0; i<dp->N; i++)
                        playerpubs[i] = *dp->playerpubs[i];
                    dp->hand.checkprod = cards777_initdeck(priv->outcards,dp->hand.cardpubs,dp->numcards,dp->N,playerpubs,0);*/
                    cmdstr = "pubstr";
                    srcbits = srv->H.nxt64bits;
                    len = dp->numcards*sizeof(bits256);
                    sprintf(hex,"{\"cmd\":\"%s\",\"cardi\":%d,\"dest\":%d,\"sender\":\"%llu\",\"timestamp\":\"%lu\",\"n\":%u,\"data\":\"",cmdstr,cardi,destplayer,(long long)srcbits,(long)time(NULL),len);
                    n = (int32_t)strlen(hex);
                    memcpy(data,dp->hand.cardpubs,len);
                    init_hexbytes_noT(&hex[n],data,len);
                    strcat(hex,"\"}");
                    hexlen = (int32_t)strlen(hex)+1;
                    dest.client = 0, memset(destpub.bytes,0,sizeof(destpub));
                    src.server = srv;
                    hostnet777_msg(0,destpub,&src,blindflag,hex,hexlen);
                    hostnet777_testresult(srv,clients,numclients,&src,&dest,blindflag,data,len,hex,revealed);
                }
                j = i+1, cmdstr = "encode";
            }
            else j = -1, cmdstr = "final";
            len = sizeof(bits256) * dp->N * dp->numcards;
            memcpy(data,priv->outcards,len);
        }
        else
        {
            cardi = (iter / numclients) - 1;
            dp = srv->clients[0].pubdata;
            destplayer = ((cardi + dp->button) % numclients);
            if ( cardi < numclients*2 + 5 )
            {
                i = (numclients - 1) - (iter % numclients);
                if ( i > 1 )
                    j = i - 1, cmdstr = "decode";
                else if ( i == 1 )
                    j = destplayer, cmdstr = "card";
                else //if ( i == 0 )
                {
                    j = -1;
                    i = destplayer;
                    if ( cardi < numclients*2 )
                        cmdstr = "facedown";
                    else cmdstr = "faceup";
                }
            }
            else
            {
                j = -1;
                i = (iter % numclients);
                cmdstr = "showdown";
            }
            dp = srv->clients[i].pubdata;
            priv = srv->clients[i].privdata;
            if ( strcmp(cmdstr,"showdown") == 0 )
            {
                len = 7;
                for (k=0; k<5; k++)
                    data[k] = dp->hand.community[k];
                data[k++] = priv->hole[0];
                data[k++] = priv->hole[1];
                rank = set_handstr(handstr,data,0);
            }
            else
            {
                card = priv->audits[(cardi*numclients + destplayer) * numclients];
                if ( j >= 0 )
                    card = cards777_decode(&seed,priv->xoverz,destplayer,card,priv->outcards,dp->numcards,numclients);
                else
                {
                    if ( strcmp(cmdstr,"facedown") == 0 )
                    {
                        priv->hole[cardi / numclients] = card.bytes[1];
                        priv->holecards[cardi / numclients] = card;
                        memset(card.bytes,0,sizeof(card));
                    }
                    else
                    {
                        revealed = card.bytes[1];
                        //printf("cmd.%s player.%d %llx (cardi.%d destplayer.%d) card.[%d]\n",cmdstr,i,(long long)card.txid,cardi,destplayer,card.bytes[1]);
                    }
                }
                len = sizeof(bits256);
                memcpy(data,card.bytes,len);
            }
        }
        //printf("iter.%d i.%d cardi.%d destplayer.%d j.%d\n",iter,i,cardi,destplayer,j);
        if ( i == 0 )
            src.server = srv;
        else src.client = clients[i];
        dp = srv->clients[i].pubdata;
        priv = srv->clients[i].privdata;
        if ( j < 0 )
            dest.client = 0;
        else if ( j == 0 )
            dest.server = srv, destpub = srv->H.pubkey;
        else dest.client = clients[j], destpub = clients[j]->H.pubkey;
        srcbits = src.client->H.nxt64bits;
    }
    sprintf(hex,"{\"cmd\":\"%s\",\"myslot\":%d,\"hand\":\"%s\",\"rank\":%u,\"cardi\":%d,\"dest\":%d,\"sender\":\"%llu\",\"timestamp\":\"%lu\",\"pubstr\":\"%s\",\"nrs\":\"%s\",\"n\":%u,\"data\":\"",cmdstr,i,handstr,rank,cardi,destplayer,(long long)srcbits,(long)time(NULL),pubstr,nrs,len);
    n = (int32_t)strlen(hex);
    init_hexbytes_noT(&hex[n],data,len);
    //printf("hex.%p n.%d len.%d\n",hex,n,len);
    strcat(hex,"\"}");
    //printf("HEX.[%s]\n",hex);
    if ( (json= cJSON_Parse(hex)) == 0 )
    {
        printf("error creating json\n");
        free(hex);
        return(-1);
    }
    free_json(json);
    hexlen = (int32_t)strlen(hex)+1;
    hostnet777_msg(dest.client == 0 ? 0 : dest.client->H.nxt64bits,destpub,&src,blindflag,hex,hexlen);
    //printf("d.%d %p, s.%d %p len.%d blind.%d | dest.%p src.%p srv.%p | crc %08x\n",d,dest.client,s,src.client,len,blindflag,&dest,&src,srv,_crc32(0,hex,hexlen));
    retval = hostnet777_testresult(srv,clients,numclients,&src,&dest,blindflag,data,len,hex,revealed);
    free(hex);
    return(retval);
}

void hostnet777_test(int32_t numclients,int32_t numiters,int32_t mode)
{
    void *portable_thread_create(void *funcp,void *argp);
    int32_t i,slot,modval,errs = 0; union hostnet777 *hn; struct hostnet777_server *srv; bits256 srvpubkey,srvprivkey,pubkey,privkey;
    struct hostnet777_client **clients; uint32_t starttime; uint64_t addrs[64]; struct cards777_pubdata *dp;
    srvprivkey = curve25519_keypair(&srvpubkey);
    if ( (srv= hostnet777_server(srvprivkey,srvpubkey,0,0,0,numclients)) == 0 )
    {
        printf("cant create hostnet777 server\n");
        return;
    }
    hn = calloc(1,sizeof(*hn));
    hn->server = srv;
    if ( portable_thread_create((void *)hostnet777_idler,hn) == 0 )
        printf("error launching server thread\n");
    clients = calloc(numclients+1,sizeof(*clients));
    for (i=1; i<=numclients; i++) // generate one error
    {
        privkey = curve25519_keypair(&pubkey);
        if ( (slot= hostnet777_register(srv,pubkey,-1)) >= 0 )
        {
            if ( (clients[i]= hostnet777_client(privkey,pubkey,srv->ep.endpoint,slot)) == 0 )
                printf("error creating clients[%d]\n",i);
            else
            {
                hn = calloc(1,sizeof(*hn));
                hn->client = clients[i];
                clients[i]->H.pubdata = cards777_allocpub((numclients >> 1) + 1,52,numclients);
                //dp->addrs = addrs;
                printf("slot.%d client.%p -> hn.%p %llu pubkey.%llx\n",slot,clients[i],hn,(long long)clients[i]->H.nxt64bits,(long long)clients[i]->H.pubkey.txid);
                if ( portable_thread_create((void *)hostnet777_idler,hn) == 0 )
                    printf("error launching clients[%d] thread\n",i);
            }
        }
        else
        {
            printf("hostnet777_test: error creating client.%d\n",i);
            break;
        }
        //printf("iter.%d server.%p: %d %d\n",i,srv,srv->pullsock,srv->pubsock);
        //printf("client sendmsg.%d [%p] (%d %d %d)\n",clients[i]->H.slot,clients[i],clients[i]->pushsock,clients[i]->subsock,clients[i]->my.pmsock);
    }
    dp = srv->H.pubdata = cards777_allocpub((numclients >> 1) + 1,52,numclients);
    //dp->addrs = addrs;
    addrs[0] = srv->H.nxt64bits;
    for (i=1; i<numclients; i++)
        if ( clients[i] != 0 )
            addrs[i] = clients[i]->H.nxt64bits;
    if ( mode != 0 )
        cards777_testinit(srv,numclients/2+1,clients,numclients,52);
    printf("srv.%p %llu M.%d N.%d\n",srv,(long long)srv->H.nxt64bits,numclients/2+1,numclients);
    if ( i >= numclients )
    {
        starttime = (uint32_t)time(NULL);
        modval = (numclients + numclients * (numclients*2 + 5 + 1));
        for (i=0; i<numiters; i++)
            errs += hostnet777_testiter(srv,clients,numclients,mode,i % modval);
        printf("hostnet777 numerrs %d of %d | %ld seconds, ave %.3f millis\n",errs,numiters,(long)(time(NULL) - starttime),1000. * (double)(time(NULL) - starttime)/numiters);
    }
    for (slot=1; slot<numclients; slot++)
    {
        if ( clients[slot] != 0 )
        {
            hostnet777_register(srv,clients[slot]->H.pubkey,-1);
            hostnet777_freeclient(clients[slot]);
        }
    }
    free(clients);
    hostnet777_freeserver(srv);
}

#endif
#endif