Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented multiple players and added some stats.
- Loading branch information
Showing
7 changed files
with
326 additions
and
320 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,7 @@ | ||
TMCG=$(shell libTMCG-config --libs --cflags) -Wl,-rpath $(shell libTMCG-config --prefix)/lib | ||
|
||
all: master slave | ||
all: client | ||
|
||
master: master.cc Makefile | ||
g++ -o master $(TMCG) master.cc | ||
client: client.cc Makefile | ||
g++ -o client $(TMCG) -pthread -lboost_system client.cc | ||
|
||
slave: slave.cc Makefile | ||
g++ -o slave $(TMCG) slave.cc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,58 @@ | ||
This is a simple proof of concept to implement mental poker for two players | ||
(texas hold'em) using libTMCG. IT WILL WORK! | ||
This is a simple proof of concept to implement mental poker (texas hold'em) | ||
for multiple players using libTMCG. IT WILL WORK! | ||
|
||
It's not playable in current state, it's just a proof of concept that | ||
checks performance. | ||
|
||
It requires libTMCG from http://www.nongnu.org/libtmcg/ | ||
It also requires boost::asio to be installed system-wide. | ||
|
||
To compile, type make (or gmake) - make sure libTMCG-config is in your path. | ||
|
||
To test run, use: | ||
To test run, decide on how many players you want to simulate, let's say | ||
it's <num_players>. Open <num_players> terminal windows. Then SLOWLY | ||
start booting up a client in each terminal window like so: | ||
|
||
$ ./client <num_players> <player_id> <base_port> | ||
|
||
Where <player_id> is a number from between 0 and <num_players> - you should | ||
increment it by 1 in each terminal window. <base_port> is the base TCP port | ||
that the clients will listen on. Currently only 127.0.0.1 is supported. | ||
Each client will use <base_port> + <player_id> as the port it listens on, | ||
and will connect to all other clients' ports. Again, add new clients slowly | ||
and in order, and wait for all previous clients to display a "waiting for | ||
connection from x" message before starting client x. If you connect them | ||
too fast or out of order, it might blow up. | ||
|
||
As you start the last client, they will begin shuffling the deck and | ||
communicating encrypted data, and at the end they will all display their | ||
corresponding cards. The idea is that these cards will be used by a front-end | ||
and there will be appropriate pausing between each game step to allow players | ||
to make bets. Cards are represented as an integer between 0 and 51. | ||
|
||
See the file stats.dat for running times. It was done on an AMD Phenom X6 | ||
1090 processor running at 3.6GHz per core. The format of this file is: | ||
|
||
$ mkfifo fi fo | ||
<number_of_players> <total_runtime_in_secs> <total_transferred_data_in_mb> | ||
|
||
then in one terminal run: | ||
Total transferred data is measured by simply running tcpdump for the | ||
corresponding ports and checking the pcap file size. | ||
|
||
$ ./master > fi < fo | ||
To visualize the data, use: | ||
|
||
and in another run: | ||
$ gnuplot -p stats.gnuplot | ||
|
||
$ ./slave < fi > fo | ||
You can see the running time and total data transferred increases slightly | ||
polynomially at a first glance. | ||
|
||
On an AMD Phenom 3.6GHz it takes about 3.5 seconds for the whole run | ||
(shuffling and trasmitting the deck and revealing all cards). It transmits | ||
just below 160kB of data each way. | ||
Remember it's not optimized at all, and there's plenty of room for improvement. | ||
|
||
If it feels too slow for you, edit both .cc files and change the security | ||
parameter t from 64 to something lower (e.g. 48) - read libTMCG's docs for | ||
more info. | ||
It is also possible to trade off the running time with security. You can | ||
change the security parameter t in client.cc:117 from 64 to something lower. | ||
Read libTMCG's docs for more info. | ||
|
||
But it works! We can easily implement 2-player texas hold'em without a central | ||
server or trusted authority! | ||
Conclusion: | ||
It works! We can easily implement 2-player texas hold'em without a central | ||
server or trusted authority! More players are also possible, but there's | ||
always the problem of detecting collusion, and it's a bit slower to shuffle. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
#include <time.h> | ||
#include <iostream> | ||
#include <vector> | ||
#include <boost/asio.hpp> | ||
#include <boost/format.hpp> | ||
#include <libTMCG.hh> | ||
|
||
#define debug(a...) { fprintf(stderr, gettime().c_str()); fprintf(stderr, a); } | ||
|
||
using namespace boost::asio; | ||
|
||
std::vector<std::istream*> inputs; | ||
std::vector<std::ostream*> outputs; | ||
int num_players; | ||
int player_id; | ||
int base_port; | ||
|
||
std::string gettime() | ||
{ | ||
return (boost::format("[%u] ") % time(NULL)).str(); | ||
} | ||
|
||
void get_card(VTMF_Card &card, SchindelhauerTMCG *tmcg, | ||
BarnettSmartVTMF_dlog *vtmf) | ||
{ | ||
//debug("getting card\n"); | ||
tmcg->TMCG_SelfCardSecret(card, vtmf); | ||
|
||
for(int i = 0; i < num_players; ++i) { | ||
if(i == player_id) | ||
continue; | ||
|
||
if(!tmcg->TMCG_VerifyCardSecret(card, vtmf, *inputs[i], *outputs[i])) | ||
debug("card verification failed\n"); | ||
} | ||
debug(" got card %u\n", tmcg->TMCG_TypeOfCard(card, vtmf)); | ||
} | ||
|
||
void wait_for(int who) | ||
{ | ||
io_service ios; | ||
ip::tcp::endpoint ep(ip::tcp::v4(), base_port + player_id); | ||
ip::tcp::acceptor acceptor(ios, ep); | ||
|
||
ip::tcp::iostream *stream = new ip::tcp::iostream; | ||
debug("waiting for connection from %d\n", who); | ||
acceptor.accept(*stream->rdbuf()); | ||
debug("got connection from %d\n", who); | ||
std::string hello; | ||
std::getline(*stream, hello); | ||
if(hello != (boost::format("hello %d %d\r") % who % | ||
num_players).str()) { | ||
debug("incorrect hello %s\n", hello.c_str()); | ||
exit(1); | ||
} | ||
debug("confirmed connection from %d\n", who); | ||
inputs.push_back(stream); | ||
} | ||
|
||
void connect_to(int who) | ||
{ | ||
debug("connecting to %d\n", who); | ||
ip::tcp::iostream *stream = new ip::tcp::iostream("127.0.0.1", | ||
(boost::format("%d") % (base_port + who)).str()); | ||
if(!*stream) { | ||
debug("couldn't connect, trying again\n"); | ||
sleep(1); | ||
connect_to(who); | ||
return; | ||
} | ||
debug("sending hello %d %d\n", player_id, num_players); | ||
*stream << (boost::format("hello %d %d\r\n") % player_id % | ||
num_players).str(); | ||
stream->flush(); | ||
debug("appears connected\n"); | ||
outputs.push_back(stream); | ||
} | ||
|
||
void initialize_connections() | ||
{ | ||
for(int i=0; i<num_players; ++i) | ||
{ | ||
if(i < player_id) { | ||
connect_to(i); | ||
wait_for(i); | ||
} else if(i == player_id) { | ||
// create a dummy stream | ||
debug("creating dummy stream for myself\n"); | ||
inputs.push_back(NULL); | ||
outputs.push_back(NULL); | ||
} else { | ||
wait_for(i); | ||
connect_to(i); | ||
} | ||
} | ||
} | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
if(argc != 4) { | ||
debug("usage: %s <num_players> <player_id> <base_port>\n", argv[0]); | ||
return 1; | ||
} | ||
|
||
num_players = atoi(argv[1]); | ||
player_id = atoi(argv[2]); | ||
base_port = atoi(argv[3]); | ||
|
||
initialize_connections(); | ||
|
||
if(!init_libTMCG()) | ||
debug("init failed\n"); | ||
|
||
SchindelhauerTMCG *tmcg = new SchindelhauerTMCG(64, num_players, 6); | ||
BarnettSmartVTMF_dlog *vtmf; | ||
|
||
if(player_id == 0) { | ||
vtmf = new BarnettSmartVTMF_dlog(); | ||
|
||
if(!vtmf->CheckGroup()) | ||
debug("checkgroup failed\n"); | ||
|
||
debug("> publishing group:\n"); | ||
for(int i = 1; i < num_players; ++i) { | ||
vtmf->PublishGroup(*outputs[i]); | ||
} | ||
} else { | ||
debug("> waiting for group\n"); | ||
vtmf = new BarnettSmartVTMF_dlog(*inputs[0]); | ||
|
||
if(!vtmf->CheckGroup()) | ||
debug("checkgroup failed\n"); | ||
} | ||
debug("< done\n"); | ||
|
||
vtmf->KeyGenerationProtocol_GenerateKey(); | ||
|
||
debug("> publishing key\n"); | ||
for(int i = 0; i < num_players; ++i) { | ||
if(i != player_id) | ||
vtmf->KeyGenerationProtocol_PublishKey(*outputs[i]); | ||
} | ||
debug("< done\n"); | ||
|
||
debug("> give me keys\n"); | ||
for(int i = 0; i < num_players; i++) { | ||
if(i != player_id) { | ||
debug("> waiting for key from %d\n", i); | ||
if(!vtmf->KeyGenerationProtocol_UpdateKey(*inputs[i])) | ||
debug("wrong key\n"); | ||
debug("thanks\n"); | ||
} | ||
} | ||
debug("< done\n"); | ||
|
||
vtmf->KeyGenerationProtocol_Finalize(); | ||
|
||
// create the deck | ||
TMCG_OpenStack<VTMF_Card> deck; | ||
for (size_t type = 0; type < 52; ++type) | ||
{ | ||
VTMF_Card c; | ||
tmcg->TMCG_CreateOpenCard(c, vtmf, type); | ||
deck.push(type, c); | ||
} | ||
|
||
// shuffle the deck | ||
TMCG_Stack<VTMF_Card> s; | ||
s.push(deck); | ||
|
||
for(int i = 0; i < num_players; ++i) | ||
{ | ||
TMCG_Stack<VTMF_Card> s2; | ||
if(i == player_id) { | ||
TMCG_StackSecret<VTMF_CardSecret> ss; | ||
tmcg->TMCG_CreateStackSecret(ss, false, s.size(), vtmf); | ||
tmcg->TMCG_MixStack(s, s2, ss, vtmf); | ||
for(int i2 = 0; i2 < num_players; ++i2) { | ||
if(i2 == player_id) | ||
continue; | ||
debug("> sending stack\n"); | ||
*outputs[i2] << s2 << std::endl; | ||
debug("< done\n"); | ||
debug("> proving stack\n"); | ||
tmcg->TMCG_ProveStackEquality(s, s2, ss, false, vtmf, | ||
*inputs[i2], *outputs[i2]); | ||
debug("< done\n"); | ||
} | ||
} else { | ||
debug("> give me stack\n"); | ||
*inputs[i] >> s2; | ||
debug("thanks\n"); | ||
if(!inputs[i]->good()) | ||
debug("read or parse error\n"); | ||
debug("> verifying stack\n"); | ||
if (!tmcg->TMCG_VerifyStackEquality(s, s2, false, vtmf, | ||
*inputs[i], *outputs[i])) | ||
debug("bad stack\n"); | ||
debug("done\n"); | ||
} | ||
s = s2; | ||
} | ||
|
||
// get cards | ||
TMCG_Stack<VTMF_Card> cards; | ||
VTMF_Card c; | ||
|
||
// push 2 cards for each player and then 5 for the board | ||
debug("pushing cards\n"); | ||
for(int i = 0; i < 2 * num_players + 5; ++i) { | ||
s.pop(c); | ||
cards.push(c); | ||
} | ||
|
||
debug("giving hands\n"); | ||
for(int i = 0; i < num_players; ++i) { | ||
if(i == player_id) { | ||
debug(" getting my hand\n"); | ||
get_card(cards[i*2], tmcg, vtmf); | ||
get_card(cards[i*2 + 1], tmcg, vtmf); | ||
} else { | ||
tmcg->TMCG_ProveCardSecret(cards[i*2], vtmf, *inputs[i], | ||
*outputs[i]); | ||
tmcg->TMCG_ProveCardSecret(cards[i*2+1], vtmf, *inputs[i], | ||
*outputs[i]); | ||
} | ||
} | ||
|
||
debug("giving/getting board\n"); | ||
for(int i = 0; i < num_players; ++i) { | ||
for(int j = 0; j < 5; ++j) { | ||
int card_id = 2 * num_players + j; | ||
if(i == player_id) { | ||
get_card(cards[card_id], tmcg, vtmf); | ||
} else { | ||
tmcg->TMCG_ProveCardSecret(cards[card_id], vtmf, | ||
*inputs[i], *outputs[i]); | ||
} | ||
} | ||
} | ||
|
||
debug("revealing all hands\n"); | ||
for(int i = 0; i < num_players; ++i) { | ||
for(int j = 0; j < num_players; ++j) { | ||
if(i == player_id) { | ||
if(j == player_id) | ||
continue; | ||
debug(" getting player %d's hand\n", j); | ||
get_card(cards[j*2], tmcg, vtmf); | ||
get_card(cards[j*2 + 1], tmcg, vtmf); | ||
} else { | ||
if(j == i) | ||
continue; | ||
debug(" revealing player %d's hand to player %d\n", j, i); | ||
tmcg->TMCG_ProveCardSecret(cards[j*2], vtmf, *inputs[i], | ||
*outputs[i]); | ||
tmcg->TMCG_ProveCardSecret(cards[j*2+1], vtmf, *inputs[i], | ||
*outputs[i]); | ||
} | ||
} | ||
} | ||
|
||
return 0; | ||
} |
Oops, something went wrong.