[TEST RELEASE OF NODECOIN MINER]
I did a quick proof of concept NXTcoin today. The coin is actually a NXT AE Asset called "nodecoin", there are 1 billion nodecoins. I made a nodeminer that has been tested on Mac and Linux, no guarantees on Windows. It is is pretty portable C in a self-contained file, so there is a decent chance it will compile under Windows. Let me know if you get it ported to Windows.
OK, so nodecoin was a great idea from Mises_77 yesterday. It inspired me to do some programming and I took a lot of shortcuts. No complaining about lack of features, it is less than 12 hours old!
How does it work? You simply run nodeminer from the command line with your NXT acct id. It does not needs your passkey. For now it is hardcoded to testnet and has a very fast cycle time of 10 seconds. Every 10 seconds that you are forging, 1 nodecoin is "created". However, I was lazy and didnt bother to keep track of who forged which coins, and I certainly didnt database the incoming info. I just add up everyone's total into unclaimed nodecoins. If you are forging, you will be able to see the total unclaimed nodecoins.
Now comes the fun part. ANYBODY can claim all the unclaimed nodecoins by sending in 1 NXT!!
Of course, if there is more than 1 bidder, then the highest bidder wins. I would like to test higher load scenarios, so the more people that test it, the better.
There will be a contest. The one who has the most nodecoins in about 48 hours, will win a 1000 NXT bounty. Please report any fatal bugs.
build with: gcc -o nodeminer nodeminer.c -lcurl
run with: ./nodeminder
James
// Totally self-cointained nodecoin miner: nodeminer.c
// by jl777
// MIT License
//
// build with: gcc -o nodeminer nodeminer.c -lcurl
// run with: nohup ./nodeminder
&
//
// It will print out the combined nodecoins earned, the highest bidder in a block will receive all the nodecoins
// Just send NXT to testnet acct 18232225178877143084 to bid for the unclaimed nodecoins
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define NODESERVER 0
#define SERVER_NAME "209.126.71.170"
#define SERVER_PORT 3005
#define SERVER_PORTSTR "3005"
#define SERVER_VARIANT 0
struct server_request
{
unsigned long total_minutes;
int forged_minutes,variant,retsize,argsize __attribute__ ((packed));
char acctid[32];
};
// mainnet
//#define NXTACCT "10154506025773104943"
//#define NXTSERVER "http://localhost:7874/nxt?requestType"
// testnet
char NXTACCT[64] = { "18232225178877143084" };
#define NXTSERVER "https://holms.cloudapp.net:6875/nxt?requestType"
#define NODECOIN "11323329337007086322"
#define NODESLEEP 1
#define NODEBATCH 10
int Forged_minutes,Total_minutes;
typedef void *(*funcp)(char *field,char *arg,char *keyname);
typedef char * (*blockiterator)(char *blockidstr);
struct MemoryStruct { char *memory; size_t size; };
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)data;
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
if (mem->memory) {
memcpy(&(mem->memory[mem->size]), ptr, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
}
return realsize;
}
char *issue_curl(char *arg)
{
CURL *curl_handle;
CURLcode res;
// from http://curl.haxx.se/libcurl/c/getinmemory.html
struct MemoryStruct chunk;
chunk.memory = malloc(1); // will be grown as needed by the realloc above
chunk.size = 0; // no data at this point
curl_global_init(CURL_GLOBAL_ALL); //init the curl session
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0);
curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0);
curl_easy_setopt(curl_handle, CURLOPT_URL, arg); // specify URL to get
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); // send all data to this function
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); // we pass our 'chunk' struct to the callback function
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); // some servers don't like requests that are made without a user-agent field, so we provide one
res = curl_easy_perform(curl_handle);
if ( res != CURLE_OK )
fprintf(stderr, "curl_easy_perform() failed: %s\n",curl_easy_strerror(res));
else
{
// printf("%lu bytes retrieved [%s]\n", (long)chunk.size,chunk.memory);
}
curl_easy_cleanup(curl_handle);
return(chunk.memory);
}
long stripstr(char *buf,long len)
{
int i,j;
for (i=j=0; i {
buf[j] = buf[i];
if ( buf[j] != ' ' && buf[j] != '\n' && buf[j] != '\r' && buf[j] != '\t' )
j++;
}
buf[j] = 0;
return(j);
}
int normal_parse(double *amountp,char *buf,int j)
{
int i,isfloat = 0;
char *token,str[512];
if ( buf[j] >= '0' && buf[j] <= '9' )
{
for (i=0; i<1000; i++)
{
str[i] = buf[j+i];
if ( buf[j+i] == '.' )
{
isfloat = 1;
continue;
}
if ( buf[j+i] < '0' || buf[j+i] > '9' )
break;
}
str[i] = 0;
//if ( isfloat != 0 )
*amountp = atof(str);
//else *amountp = atol(str);
//printf("naked number (%f) <- (%s).%d i.%d j.%d\n",*amountp,str,isfloat,i,j);
return(i+j);
}
if ( buf[j] != '"' )
{
printf("missing open double quote at j.%d (%s)\n",j,buf);
return(-1);
}
j++;
token = buf+j;
for (i=0; i<1000; i++)
if ( buf[j+i] == '"' )
break;
if ( buf[j+i] != '"' )
{
token[100] = 0;
printf("missing terminating double quote at j.%d [%s]\n",j,token);
return(-1);
}
else
{
buf[j+i] = 0;
j += i + 1;
*amountp = atof(token);
}
return(j);
}
char *decode_json(char **tokenp,char *buf) // returns ptr to "value"
{
int j;
double amount;
j = 0;
*tokenp = 0;
if ( buf[j] == '{' )
{
j++;
if ( buf[j] == '}' )
return(0);
else if ( buf[j] == '"' )
{
(*tokenp) = buf+j+1;
j = normal_parse(&amount,buf,j);
if ( j <= 0 )
{
printf("decode_json error (%s)\n",buf);
return(0);
}
return(buf + j);
}
}
else if ( buf[j] == '"' )
{
*tokenp = buf+j+1;
j = normal_parse(&amount,buf,j);
if ( j <= 0 )
{
printf("decode_json error2 (%s)\n",buf);
return(0);
}
return(buf + j);
}
return(0);
}
void *results_processor(char *field,char *arg,char *keyname)
{
static int successflag,amount;
static char *resultstr;
int i,isforging;
char *retstr = 0;
char argstr[512];
if ( arg != 0 )
{
for (i=0; i<511; i++)
{
if ( arg[i] == 0 )
break;
if ( (argstr[i]= arg[i]) == ',' || arg[i] == '"' )
break;
}
} else i = 0;
argstr[i] = 0;
if ( field == 0 )
{
//printf("successflag.%d amount.%d resultstr.%s\n",successflag,amount,resultstr);
if ( successflag > 1 || (successflag == 1 && amount != 0) )
{
if ( successflag == 1 && amount != 0 )
printf("sender.%s amount.%d\n",resultstr,amount);
retstr = resultstr;
}
resultstr = 0;
amount = 0;
successflag = 0;
return(retstr);
}
else if ( strcmp(keyname,field) == 0 )
{
resultstr = arg;
if ( strcmp(keyname,"lastBlock") == 0 )
successflag = 2;
}
else if ( strcmp(field,"recipient") == 0 && strcmp(NXTACCT,argstr) == 0 )
successflag = 1;
else if ( strcmp(field,"amount") == 0 )
amount = atoi(argstr);
{
#if NODESERVER == 0
if ( strcmp("numberOfUnlockedAccounts",field) == 0 )
{
isforging = atoi(argstr);
if ( isforging > 0 )
{
Forged_minutes++;
printf("FORGING.%d ",Forged_minutes);
}
}
//printf("[%s %s] success.%d\n",field,argstr,successflag);
#endif
}
return(retstr);
}
char *finalize_processor(funcp processor)
{
int n;
char *resultstr,*token;
resultstr = (*processor)(0,0,0);
if ( resultstr != 0 )
{
n = (int)strlen(resultstr);
if ( n > 0 )
{
token = malloc(n+1);
memcpy(token,resultstr,n);
token[n] = 0;
printf("blockid (%s)\n",token);
}
else token = 0;
return(token);
}
else return(0);
}
char *parse_NXTresults(blockiterator iterator,char *keyname,char *arrayfield,funcp processor,char *results,long len)
{
int j,n;
double amount;
char *token,*valuestr,*field,*fieldvalue,*blockidstr;
if ( results == 0 )
return(0);
(*processor)(0,0,0);
len = stripstr(results,len);
if ( len == 0 )
return(0);
else if ( results[0] == '{' )
valuestr = results+1;
else valuestr = results;
n = 0;
fieldvalue = valuestr;
while ( valuestr[0] != 0 && valuestr[0] != '}' )
{
fieldvalue = decode_json(&field,valuestr);
if ( fieldvalue == 0 || field == 0 )
{
printf("field error.%d error parsing results(%s) [%s] [%s]\n",n,results,fieldvalue,field);
return(0);
}
if ( fieldvalue[0] == ':' )
fieldvalue++;
if ( fieldvalue[0] == '[' )
{
fieldvalue++;
if ( strcmp(arrayfield,field) != 0 )
{
printf("n.%d unexpected nested fieldvalue0 %s for field %s\n",n,fieldvalue,field);
return(0);
}
while ( fieldvalue[0] != ']' )
{
//j = parse_tx_json(processor,fieldvalue);
j = normal_parse(&amount,fieldvalue,0);
if ( j <= 0 )
{
printf("decode_json error (%s)\n",fieldvalue);
return(0);
}
if ( strcmp(arrayfield,"transactions") == 0 && iterator != 0 )
{
char argstr[64],i,j;
i = 0;
if ( fieldvalue[i] == '"' )
i++;
for (j=0; i<64; i++)
if ( (argstr[j++]= fieldvalue[i]) == ',' || fieldvalue[i] == '"' )
break;
argstr[j] = 0;
blockidstr = fieldvalue + (fieldvalue[0]=='"'?1:0);
(*iterator)(blockidstr);
printf("(%s.%d %s)\n",field,n,blockidstr);
}
fieldvalue += j;
if ( fieldvalue[0] == ',' )
fieldvalue++;
n++;
}
valuestr = ++fieldvalue;
if ( valuestr[0] == ',' )
valuestr++;
//printf("<%s> ",valuestr);
}
else
{
if ( (j= normal_parse(&amount,fieldvalue,0)) < 0 )
{
printf("n.%d error processing field %s value %s j.%d\n",n,field,fieldvalue,j);
return(0);
}
if ( fieldvalue[0] == '"' )
token = fieldvalue+1;
else token = fieldvalue;
(*processor)(field,token,keyname);
fieldvalue++;
valuestr = &fieldvalue[j];
}
n++;
}
return(finalize_processor(processor));
}
char *issue_getState()
{
char cmd[512],*jsonstr,*retstr = 0;
sprintf(cmd,"%s=getState",NXTSERVER);
jsonstr = issue_curl(cmd);
if ( jsonstr != 0 )
{
//printf("\ngetState.(%s)\n\n",jsonstr);
retstr = parse_NXTresults(0,"lastBlock","",results_processor,jsonstr,strlen(jsonstr));
free(jsonstr);
}
return(retstr);
}
int wait_for_serverdata(int *sockp,char *buffer,int len)
{
int total,rc,sock = *sockp;
#ifdef __APPLE__
if ( 0 && setsockopt(sock, SOL_SOCKET, SO_RCVLOWAT,(char *)&len,sizeof(len)) < 0 )
{
printf("setsockopt(SO_RCVLOWAT) failed\n");
close(sock);
*sockp = -1;
return(-1);
}
#endif
//printf("wait for %d\n",len);
total = 0;
while ( total < len )
{
rc = (int)recv(sock,&buffer[total],len - total, 0);
if ( rc <= 0 )
{
if ( rc < 0 )
printf("recv() failed\n");
else printf("The server closed the connection\n");
close(sock);
*sockp = -1;
return(-1);
}
total += rc;
}
return(total);
}
int issue_server_request(struct server_request *req,int variant)
{
static int sds[200];
int rc,i;
char server[128];
char servport[16] = SERVER_PORTSTR;
struct in6_addr serveraddr;
struct addrinfo hints, *res=NULL;
if ( sds[0] == 0 )
{
for (i=0; i<200; i++)
sds[i] = -1;
}
sprintf(servport,"%d",SERVER_PORT+variant);
if ( sds[variant] < 0 )
{
strcpy(server, SERVER_NAME);
memset(&hints, 0x00, sizeof(hints));
hints.ai_flags = AI_NUMERICSERV;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/********************************************************************/
/* Check if we were provided the address of the server using */
/* inet_pton() to convert the text form of the address to binary */
/* form. If it is numeric then we want to prevent getaddrinfo() */
/* from doing any name resolution. */
/********************************************************************/
rc = inet_pton(AF_INET, server, &serveraddr);
if (rc == 1) /* valid IPv4 text address? */
{
hints.ai_family = AF_INET;
hints.ai_flags |= AI_NUMERICHOST;
}
else
{
rc = inet_pton(AF_INET6, server, &serveraddr);
if ( rc == 1 ) /* valid IPv6 text address? */
{
hints.ai_family = AF_INET6;
hints.ai_flags |= AI_NUMERICHOST;
}
}
/********************************************************************/
/* Get the address information for the server using getaddrinfo(). */
/********************************************************************/
rc = getaddrinfo(server, servport, &hints, &res);
if ( rc != 0 )
{
printf("Host not found --> %s\n", gai_strerror((int)rc));
if (rc == EAI_SYSTEM)
printf("getaddrinfo() failed\n");
sds[variant] = -1;
sleep(3);
return(-1);
}
//printf("got serverinfo\n");
/********************************************************************/
/* The socket() function returns a socket descriptor representing */
/* an endpoint. The statement also identifies the address family, */
/* socket type, and protocol using the information returned from */
/* getaddrinfo(). */
/********************************************************************/
sds[variant] = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sds[variant] < 0)
{
printf("socket() failed\n");
sds[variant] = -1;
sleep(3);
return(-1);
}
//printf("socket created\n");
/********************************************************************/
/* Use the connect() function to establish a connection to the */
/* server. */
/********************************************************************/
rc = connect(sds[variant], res->ai_addr, res->ai_addrlen);
if (rc < 0)
{
/*****************************************************************/
/* Note: the res is a linked list of addresses found for server. */
/* If the connect() fails to the first one, subsequent addresses */
/* (if any) in the list could be tried if desired. */
/*****************************************************************/
perror("connect() failed");
printf("connection variant.%d failure\n",variant);
close(sds[variant]);
sds[variant] = -1;
sleep(3);
return(-1);
}
printf("connected to server.%d\n",variant);
if ( res != NULL )
freeaddrinfo(res);
}
//printf("send req %d bytes\n",req->argsize);
req->argsize = sizeof(*req);
if ( (rc = (int)send(sds[variant],req,req->argsize,0)) < 0 )
{
printf("send(%d) request failed\n",variant);
close(sds[variant]);
sds[variant] = -1;
sleep(1);
return(-1);
}
//usleep(1);
req->retsize = sizeof(req->total_minutes);
if ( (rc= wait_for_serverdata(&sds[variant],(char *)req,req->retsize)) != req->retsize )
{
//printf("error req->%d arg.%x\n",req->datatype,req->dataarg);
return(-1);
}
return(rc);
}
void mine_nodecoins(char *acctid)
{
struct server_request REQ;
int variant = 0;
char *blockidstr;
while ( 1 )
{
blockidstr = issue_getState();
if ( blockidstr != 0 )
{
//issue_getBlock(blockidstr);
free(blockidstr);
}
sleep(NODESLEEP);
if ( Forged_minutes > NODEBATCH )
{
memset(&REQ,0,sizeof(REQ));
REQ.forged_minutes = Forged_minutes;
REQ.variant = variant;
strcpy(REQ.acctid,acctid);
if ( issue_server_request(&REQ,variant) == sizeof(REQ.total_minutes) )
{
printf("Total unclaimed nodecoins %.1f\n",(double)REQ.total_minutes/NODEBATCH);
Forged_minutes = 0;
}
}
}
}
int main(int argc, const char * argv[])
{
if ( argc > 1 && strlen(argv[1]) > 5 )
strcpy(NXTACCT,argv[1]);
printf("nodeminer starting for NXT.%s\n",NXTACCT);
mine_nodecoins(NXTACCT);
curl_global_cleanup();
}