forked from hush/hush3
jl777
5 years ago
committed by
GitHub
24 changed files with 1403 additions and 166 deletions
@ -0,0 +1,352 @@ |
|||
#include "gamescc.h" |
|||
|
|||
/*
|
|||
./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" |
|||
{ |
|||
"playerid": 250, |
|||
"seed": 1398876319979341887, |
|||
"rng": 14565767519458298868, |
|||
"lastrng": 15075236803740723044, |
|||
"maxrngs": 10000, |
|||
"result": "success" |
|||
} |
|||
|
|||
./c cclib rngnext 17 \"[14565767519458298868]\" |
|||
{ |
|||
"seed": 14565767519458297856, |
|||
"rng": 4253087318999719449, |
|||
"result": "success" |
|||
} |
|||
|
|||
The idea is for a game to start with a near future blockhash, so the lobby gets players signed up until just prior to the designated height. then that blockhash can be used to create a stream of rngs. |
|||
|
|||
the same initial rng can be used for all players, if the identical starting condition is required. up to 255 different initial rng can be derived from a single blockhash. (Actually any number is possible, for simplicity rng rpc limits to 255). |
|||
|
|||
you will notice maxrngs and lastrng, the lastrng is the rng value that will happen after maxrng iterations of calling rngnext with the current rng. This allows making time based multiplayer games where all the nodes can validate all the other nodes rng, even without realtime synchronization of all user input events. |
|||
|
|||
Every time period, all players would set their rng value to the lastrng value. The only thing to be careful of is it not exceed the maxrng calls to rngnext in a single time period. otherwise the same set of rng numbers will be repeated. |
|||
*/ |
|||
|
|||
CScript games_opret(uint8_t funcid,CPubKey pk) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_GAMES; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk); |
|||
return(opret); |
|||
} |
|||
|
|||
uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) |
|||
{ |
|||
std::vector<uint8_t> vopret; uint8_t e,f; |
|||
GetOpReturnData(scriptPubKey,vopret); |
|||
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_GAMES ) |
|||
{ |
|||
return(f); |
|||
} |
|||
return(0); |
|||
} |
|||
|
|||
CScript games_eventopret(uint32_t timestamp,CPubKey pk,std::vector<uint8_t> sig,std::vector<uint8_t> payload) |
|||
{ |
|||
CScript opret; uint8_t evalcode = EVAL_GAMES; |
|||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'E' << timestamp << pk << sig << payload); |
|||
return(opret); |
|||
} |
|||
|
|||
uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector<uint8_t> &sig,std::vector<uint8_t> &payload,std::vector<uint8_t> vopret) |
|||
{ |
|||
uint8_t e,f; |
|||
timestamp = 0; |
|||
if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> timestamp; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) |
|||
{ |
|||
return(f); |
|||
} |
|||
fprintf(stderr,"ERROR e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); |
|||
return(0); |
|||
} |
|||
|
|||
UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) |
|||
{ |
|||
CTransaction tx; |
|||
if ( rawtx.size() > 0 ) |
|||
{ |
|||
result.push_back(Pair("hex",rawtx)); |
|||
if ( DecodeHexTx(tx,rawtx) != 0 ) |
|||
{ |
|||
if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) |
|||
RelayTransaction(tx); |
|||
result.push_back(Pair("txid",tx.GetHash().ToString())); |
|||
result.push_back(Pair("result","success")); |
|||
} else result.push_back(Pair("error","decode hex")); |
|||
} else result.push_back(Pair("error","couldnt finalize CCtx")); |
|||
return(result); |
|||
} |
|||
|
|||
uint64_t _games_rngnext(uint64_t initseed) |
|||
{ |
|||
uint16_t seeds[4]; int32_t i; |
|||
seeds[0] = initseed; |
|||
seeds[1] = (initseed >> 16); |
|||
seeds[2] = (initseed >> 32); |
|||
seeds[3] = (initseed >> 48); |
|||
seeds[0] = (seeds[0]*GAMES_RNGMULT + GAMES_RNGOFFSET); |
|||
seeds[1] = ((seeds[0] ^ seeds[1])*GAMES_RNGMULT + GAMES_RNGOFFSET); |
|||
seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*GAMES_RNGMULT + GAMES_RNGOFFSET); |
|||
seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*GAMES_RNGMULT + GAMES_RNGOFFSET); |
|||
return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); |
|||
} |
|||
|
|||
UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); int32_t n; uint64_t seed; |
|||
if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) |
|||
{ |
|||
seed = jdouble(jitem(params,0),0); |
|||
result.push_back(Pair("seed",seed)); |
|||
seed = _games_rngnext(seed); |
|||
result.push_back(Pair("rng",seed)); |
|||
result.push_back(Pair("result","success")); |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","not enough params")); |
|||
} |
|||
return(result); |
|||
} |
|||
|
|||
UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint64_t seed=0,initseed; bits256 hash; |
|||
if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 1 || n == 2) ) |
|||
{ |
|||
hash = jbits256(jitem(params,0),0); |
|||
if ( n == 2 ) |
|||
{ |
|||
playerid = juint(jitem(params,1),0); |
|||
if ( playerid >= 0xff ) |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","playerid too big")); |
|||
return(result); |
|||
} |
|||
} |
|||
playerid++; |
|||
for (i=0; i<8; i++) |
|||
{ |
|||
if ( ((1 << i) & playerid) != 0 ) |
|||
seed ^= (uint64_t)hash.uints[i] << ((i&1)*32); |
|||
} |
|||
initseed = seed; |
|||
seed = _games_rngnext(initseed); |
|||
result.push_back(Pair("playerid",(int64_t)(playerid - 1))); |
|||
result.push_back(Pair("seed",initseed)); |
|||
result.push_back(Pair("rng",seed)); |
|||
for (i=0; i<GAMES_MAXRNGS; i++) |
|||
seed = _games_rngnext(seed); |
|||
result.push_back(Pair("lastrng",seed)); |
|||
result.push_back(Pair("maxrngs",GAMES_MAXRNGS)); |
|||
result.push_back(Pair("result","success")); |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","not enough params")); |
|||
} |
|||
return(result); |
|||
} |
|||
|
|||
UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
return(result); |
|||
} |
|||
|
|||
UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) |
|||
{ |
|||
UniValue result(UniValue::VOBJ); |
|||
return(result); |
|||
} |
|||
|
|||
int32_t games_eventsign(uint32_t ×tamp,std::vector<uint8_t> &sig,std::vector<uint8_t> payload,CPubKey pk) |
|||
{ |
|||
static secp256k1_context *ctx; |
|||
size_t siglen = 74; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature signature; int32_t len,verifyflag = 1; uint8_t privkey[32]; uint256 hash; uint32_t t; |
|||
if ( ctx == 0 ) |
|||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); |
|||
if ( ctx != 0 ) |
|||
{ |
|||
Myprivkey(privkey); |
|||
len = payload.size(); |
|||
payload.resize(len + 4); |
|||
if ( timestamp == 0 ) |
|||
{ |
|||
timestamp = (uint32_t)time(NULL); |
|||
verifyflag = 0; |
|||
} |
|||
t = timestamp; |
|||
payload[len++] = t, t >>= 8; |
|||
payload[len++] = t, t >>= 8; |
|||
payload[len++] = t, t >>= 8; |
|||
payload[len++] = t; |
|||
vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len); |
|||
if ( verifyflag == 0 ) |
|||
{ |
|||
if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) |
|||
{ |
|||
sig.resize(siglen); |
|||
if ( secp256k1_ecdsa_signature_serialize_der(ctx,&sig[0],&siglen,&signature) > 0 ) |
|||
{ |
|||
if ( siglen != sig.size() ) |
|||
sig.resize(siglen); |
|||
return(0); |
|||
} |
|||
else return(-3); |
|||
} else return(-2); |
|||
} |
|||
else |
|||
{ |
|||
if ( secp256k1_ec_pubkey_parse(ctx,&pubkey,pk.begin(),33) > 0 ) |
|||
{ |
|||
if ( secp256k1_ecdsa_signature_parse_der(ctx,&signature,&sig[0],sig.size()) > 0 ) |
|||
{ |
|||
if ( secp256k1_ecdsa_verify(ctx,&signature,(uint8_t *)&hash,&pubkey) > 0 ) |
|||
return(0); |
|||
else return(-4); |
|||
} else return(-3); |
|||
} else return(-2); |
|||
} |
|||
} else return(-1); |
|||
} |
|||
|
|||
int32_t games_payload(CPubKey pk,uint32_t timestamp,std::vector<uint8_t> payload) |
|||
{ |
|||
uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; |
|||
if ( (len= payload.size()) > 36 ) |
|||
{ |
|||
len -= 36; |
|||
for (i=0; i<32; i++) |
|||
((uint8_t *)&gametxid)[i] = payload[len+i]; |
|||
eventid = (uint32_t)payload[len+32]; |
|||
eventid |= (uint32_t)payload[len+33] << 8; |
|||
eventid |= (uint32_t)payload[len+34] << 16; |
|||
eventid |= (uint32_t)payload[len+35] << 24; |
|||
for (i=0; i<len; i++) |
|||
fprintf(stderr,"%02x",payload[i]); |
|||
fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); |
|||
return(0); |
|||
} else return(-1); |
|||
} |
|||
|
|||
UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) |
|||
{ |
|||
static uint256 lastgametxid; static uint32_t numevents; |
|||
UniValue result(UniValue::VOBJ); std::vector<uint8_t> sig,payload,vopret; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t eventid,timestamp = 0; uint256 gametxid; |
|||
if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 1 && n <= 3 ) |
|||
{ |
|||
if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) |
|||
{ |
|||
if ( n >= 2 ) |
|||
gametxid = juint256(jitem(params,1)); |
|||
else gametxid = zeroid; |
|||
if ( gametxid != lastgametxid ) |
|||
{ |
|||
lastgametxid = gametxid; |
|||
numevents = 1; |
|||
eventid = 0; |
|||
} |
|||
if ( n == 3 ) |
|||
{ |
|||
eventid = juint(jitem(params,2),0); |
|||
if ( numevents <= eventid ) |
|||
numevents = eventid+1; |
|||
} else eventid = numevents++; |
|||
len = payload.size(); |
|||
payload.resize(len + 4 + 32); |
|||
for (i=0; i<32; i++) |
|||
payload[len++] = ((uint8_t *)&gametxid)[i]; |
|||
x = eventid; |
|||
payload[len++] = x, x >>= 8; |
|||
payload[len++] = x, x >>= 8; |
|||
payload[len++] = x, x >>= 8; |
|||
payload[len++] = x; |
|||
mypk = pubkey2pk(Mypubkey()); |
|||
if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) |
|||
{ |
|||
GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); |
|||
games_payload(mypk,timestamp,payload); |
|||
komodo_sendmessage(4,8,"events",vopret); |
|||
result.push_back(Pair("gametxid",gametxid.GetHex())); |
|||
result.push_back(Pair("eventid",(int64_t)eventid)); |
|||
result.push_back(Pair("timestamp",(int64_t)timestamp)); |
|||
result.push_back(Pair("result","success")); |
|||
result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","signing ereror")); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","couldnt parsehexdata")); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
result.push_back(Pair("result","error")); |
|||
result.push_back(Pair("error","not enough params")); |
|||
} |
|||
return(result); |
|||
} |
|||
|
|||
void komodo_netevent(std::vector<uint8_t> message) |
|||
{ |
|||
int32_t i,retval,lag,lagerr=0; uint32_t timestamp,now; CPubKey pk; std::vector<uint8_t> sig,payload; char str[67]; |
|||
if ( games_eventdecode(timestamp,pk,sig,payload,message) == 'E' ) |
|||
{ |
|||
now = (uint32_t)time(NULL); |
|||
lag = now - timestamp; |
|||
if ( lag < -3 || lag > 3 ) |
|||
{ |
|||
fprintf(stderr,"LAG ERROR "); |
|||
lagerr = lag; |
|||
} |
|||
if ( (retval= games_eventsign(timestamp,sig,payload,pk)) != 0 ) |
|||
fprintf(stderr,"SIG ERROR.%d ",retval); |
|||
else if ( lagerr == 0 ) |
|||
{ |
|||
if ( games_payload(pk,timestamp,payload) == 0 ) // first time this is seen
|
|||
{ |
|||
if ( (rand() % 10) == 0 ) |
|||
{ |
|||
fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); |
|||
komodo_sendmessage(2,2,"events",message); |
|||
} |
|||
} |
|||
} |
|||
for (i=0; i<payload.size(); i++) |
|||
fprintf(stderr,"%02x",payload[i]); |
|||
fprintf(stderr," payload, got pk.%s siglen.%d lag.[%d]\n",pubkey33_str(str,(uint8_t *)&pk),(int32_t)sig.size(),lag); |
|||
} |
|||
else |
|||
{ |
|||
for (i=0; i<message.size(); i++) |
|||
fprintf(stderr,"%02x",message[i]); |
|||
fprintf(stderr," got RAW message[%d]\n",(int32_t)message.size()); |
|||
} |
|||
} |
|||
|
|||
bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) |
|||
{ |
|||
return(true); |
|||
} |
|||
|
|||
|
@ -0,0 +1,61 @@ |
|||
#ifndef H_GAMESCC_H |
|||
#define H_GAMESCC_H |
|||
|
|||
#define ENABLE_WALLET |
|||
extern CWallet* pwalletMain; |
|||
|
|||
#include "CCinclude.h" |
|||
#include "secp256k1.h" |
|||
|
|||
std::string MYCCLIBNAME = (char *)"gamescc"; |
|||
|
|||
#define EVAL_GAMES (EVAL_FAUCET2+1) |
|||
#define GAMES_TXFEE 10000 |
|||
|
|||
#define GAMES_RNGMULT 11109 |
|||
#define GAMES_RNGOFFSET 13849 |
|||
#define GAMES_MAXRNGS 10000 |
|||
|
|||
#define MYCCNAME "games" |
|||
|
|||
#define RPC_FUNCS \ |
|||
{ (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ |
|||
{ (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \ |
|||
{ (char *)MYCCNAME, (char *)"create", (char *)"game,minplayers,maxplayers,buyin,numblocks", 5, 5, ' ', EVAL_GAMES }, \ |
|||
{ (char *)MYCCNAME, (char *)"info", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, \ |
|||
{ (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ |
|||
{ (char *)MYCCNAME, (char *)"register", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, |
|||
|
|||
bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); |
|||
UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); |
|||
UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); |
|||
UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); |
|||
UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); |
|||
UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); |
|||
UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); |
|||
|
|||
#define CUSTOM_DISPATCH \ |
|||
if ( cp->evalcode == EVAL_GAMES ) \ |
|||
{ \ |
|||
if ( strcmp(method,"rng") == 0 ) \ |
|||
return(games_rng(txfee,cp,params)); \ |
|||
else if ( strcmp(method,"rngnext") == 0 ) \ |
|||
return(games_rngnext(txfee,cp,params)); \ |
|||
else if ( strcmp(method,"create") == 0 ) \ |
|||
return(games_create(txfee,cp,params)); \ |
|||
else if ( strcmp(method,"info") == 0 ) \ |
|||
return(games_info(txfee,cp,params)); \ |
|||
else if ( strcmp(method,"register") == 0 ) \ |
|||
return(games_register(txfee,cp,params)); \ |
|||
else if ( strcmp(method,"events") == 0 ) \ |
|||
return(games_events(txfee,cp,params)); \ |
|||
else \ |
|||
{ \ |
|||
result.push_back(Pair("result","error")); \ |
|||
result.push_back(Pair("error","invalid gamescc method")); \ |
|||
result.push_back(Pair("method",method)); \ |
|||
return(result); \ |
|||
} \ |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,7 @@ |
|||
#!/bin/sh |
|||
gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp |
|||
cp gamescc.so ../libcc.so |
|||
cd .. |
|||
make |
|||
cd cc |
|||
|
Loading…
Reference in new issue