Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implemented multiple players and added some stats.

  • Loading branch information...
commit b64775c6311db195ba57227c94d4e6e9825fdc1f 1 parent ef92e4b
@goblin authored
View
8 Makefile
@@ -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
View
60 README
@@ -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.
View
267 client.cc
@@ -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;
+}
View
150 master.cc
@@ -1,150 +0,0 @@
-#include <stdio.h>
-#include <iostream>
-#include <libTMCG.hh>
-
-#define debug(a...) fprintf(stderr, a)
-
-void get_card(VTMF_Card &card, SchindelhauerTMCG *tmcg,
- BarnettSmartVTMF_dlog *vtmf)
-{
- debug("getting card\n");
- tmcg->TMCG_SelfCardSecret(card, vtmf);
- if(!tmcg->TMCG_VerifyCardSecret(card, vtmf, std::cin, std::cout))
- debug("card verification failed\n");
- debug("my card is %u\n", tmcg->TMCG_TypeOfCard(card, vtmf));
-}
-
-int main()
-{
- if(!init_libTMCG())
- debug("init failed\n");
-
- SchindelhauerTMCG *tmcg = new SchindelhauerTMCG(64, 2, 6);
- BarnettSmartVTMF_dlog *vtmf = new BarnettSmartVTMF_dlog();
-
- if(!vtmf->CheckGroup())
- debug("checkgroup failed\n");
-
- debug("> publishing group:\n");
- vtmf->PublishGroup(std::cout);
- debug("< done\n");
-
- vtmf->KeyGenerationProtocol_GenerateKey();
-
- debug("> publishing key\n");
- vtmf->KeyGenerationProtocol_PublishKey(std::cout);
- debug("< done\n");
-
- debug("> give me key\n");
- if(!vtmf->KeyGenerationProtocol_UpdateKey(std::cin))
- debug("wrong key\n");
- debug("thanks\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);
-
- // send the shuffle
- {
- TMCG_Stack<VTMF_Card> s2;
- TMCG_StackSecret<VTMF_CardSecret> ss;
- tmcg->TMCG_CreateStackSecret(ss, false, s.size(), vtmf);
- tmcg->TMCG_MixStack(s, s2, ss, vtmf);
- debug("> sending stack\n");
- std::cout << s2 << std::endl;
- debug("< done\n> proving stack\n");
- tmcg->TMCG_ProveStackEquality(s, s2, ss, false, vtmf,
- std::cin, std::cout);
- debug("< done\n");
- s = s2;
- }
-
- // verify a shuffle
- {
- TMCG_Stack<VTMF_Card> s2;
- debug("> give me stack\n");
- std::cin >> s2;
- debug("thanks\n");
- if(!std::cin.good())
- debug("read or parse error\n");
- debug("> verifying stack\n");
- if (!tmcg->TMCG_VerifyStackEquality(s, s2, false, vtmf,
- std::cin, std::cout))
- debug("bad stack\n");
- debug("done\n");
- s = s2;
- }
-
- // get cards
- TMCG_Stack<VTMF_Card> my_hand;
- TMCG_Stack<VTMF_Card> his_hand;
- TMCG_Stack<VTMF_Card> board;
- VTMF_Card c;
-
- // first, two for me
- s.pop(c);
- my_hand.push(c);
- s.pop(c);
- my_hand.push(c);
-
- // then, two for him
- s.pop(c);
- his_hand.push(c);
- s.pop(c);
- his_hand.push(c);
-
- // finally, 5 for the table
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
-
- // get my cards
- get_card(my_hand[0], tmcg, vtmf);
- get_card(my_hand[1], tmcg, vtmf);
-
- // give him his cards
- tmcg->TMCG_ProveCardSecret(his_hand[0], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(his_hand[1], vtmf, std::cin, std::cout);
-
- // get the board cards
- get_card(board[0], tmcg, vtmf);
- get_card(board[1], tmcg, vtmf);
- get_card(board[2], tmcg, vtmf);
- get_card(board[3], tmcg, vtmf);
- get_card(board[4], tmcg, vtmf);
-
- // give him board cards
- tmcg->TMCG_ProveCardSecret(board[0], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[1], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[2], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[3], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[4], vtmf, std::cin, std::cout);
-
- // get his cards
- get_card(his_hand[0], tmcg, vtmf);
- get_card(his_hand[1], tmcg, vtmf);
-
- // give him my cards
- tmcg->TMCG_ProveCardSecret(my_hand[0], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(my_hand[1], vtmf, std::cin, std::cout);
-
- return 0;
-}
View
149 slave.cc
@@ -1,149 +0,0 @@
-#include <stdio.h>
-#include <iostream>
-#include <libTMCG.hh>
-
-#define debug(a...) fprintf(stderr, a)
-
-void get_card(VTMF_Card &card, SchindelhauerTMCG *tmcg,
- BarnettSmartVTMF_dlog *vtmf)
-{
- debug("getting card\n");
- tmcg->TMCG_SelfCardSecret(card, vtmf);
- if(!tmcg->TMCG_VerifyCardSecret(card, vtmf, std::cin, std::cout))
- debug("card verification failed\n");
- debug("my card is %u\n", tmcg->TMCG_TypeOfCard(card, vtmf));
-}
-
-int main()
-{
- if(!init_libTMCG())
- debug("init failed\n");
-
- SchindelhauerTMCG *tmcg = new SchindelhauerTMCG(64, 2, 6);
-
- debug("> give me group\n");
- BarnettSmartVTMF_dlog *vtmf = new BarnettSmartVTMF_dlog(std::cin);
- debug("thanks\n");
-
- if(!vtmf->CheckGroup())
- debug("invalid group\n");
-
- vtmf->KeyGenerationProtocol_GenerateKey();
-
- debug("> give me key\n");
- if(!vtmf->KeyGenerationProtocol_UpdateKey(std::cin))
- debug("wrong key\n");
- debug("thanks\n");
-
- debug("> publishing key\n");
- vtmf->KeyGenerationProtocol_PublishKey(std::cout);
- 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);
-
- // verify a shuffle
- {
- TMCG_Stack<VTMF_Card> s2;
- debug("> give me stack\n");
- std::cin >> s2;
- debug("thanks\n");
- if(!std::cin.good())
- debug("read or parse error\n");
- debug("> verifying stack\n");
- if (!tmcg->TMCG_VerifyStackEquality(s, s2, false, vtmf,
- std::cin, std::cout))
- std::cerr << "Verification failed!" << std::endl;
- debug("done\n");
- s = s2;
- }
-
- // send the shuffle
- {
- TMCG_Stack<VTMF_Card> s2;
- TMCG_StackSecret<VTMF_CardSecret> ss;
- tmcg->TMCG_CreateStackSecret(ss, false, s.size(), vtmf);
- tmcg->TMCG_MixStack(s, s2, ss, vtmf);
- debug("> sending stack\n");
- std::cout << s2 << std::endl;
- debug("< done\n> proving stack\n");
- tmcg->TMCG_ProveStackEquality(s, s2, ss, false, vtmf,
- std::cin, std::cout);
- debug("< done\n");
- s = s2;
- }
-
- // get cards
- TMCG_Stack<VTMF_Card> my_hand;
- TMCG_Stack<VTMF_Card> his_hand;
- TMCG_Stack<VTMF_Card> board;
- VTMF_Card c;
-
- // first, two for him
- s.pop(c);
- his_hand.push(c);
- s.pop(c);
- his_hand.push(c);
-
- // then, two for me
- s.pop(c);
- my_hand.push(c);
- s.pop(c);
- my_hand.push(c);
-
- // finally, 5 for the table
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
- s.pop(c);
- board.push(c);
-
- // give him his cards
- tmcg->TMCG_ProveCardSecret(his_hand[0], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(his_hand[1], vtmf, std::cin, std::cout);
-
- // get my cards
- get_card(my_hand[0], tmcg, vtmf);
- get_card(my_hand[1], tmcg, vtmf);
-
- // give him board cards
- tmcg->TMCG_ProveCardSecret(board[0], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[1], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[2], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[3], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(board[4], vtmf, std::cin, std::cout);
-
- // get the board cards
- get_card(board[0], tmcg, vtmf);
- get_card(board[1], tmcg, vtmf);
- get_card(board[2], tmcg, vtmf);
- get_card(board[3], tmcg, vtmf);
- get_card(board[4], tmcg, vtmf);
-
- // give him my cards
- tmcg->TMCG_ProveCardSecret(my_hand[0], vtmf, std::cin, std::cout);
- tmcg->TMCG_ProveCardSecret(my_hand[1], vtmf, std::cin, std::cout);
-
- // get his cards
- get_card(his_hand[0], tmcg, vtmf);
- get_card(his_hand[1], tmcg, vtmf);
-
- return 0;
-}
View
11 stats.dat
@@ -0,0 +1,11 @@
+2 3 2
+3 7 5
+4 13 11
+5 22 17
+6 30 26
+7 44 36
+8 56 48
+9 72 61
+10 89 78
+11 106 94
+12 128 113
View
1  stats.gnuplot
@@ -0,0 +1 @@
+plot "stats.dat" using 1:2 title "running time in secs", "stats.dat" using 1:3 title "total data transferred", "stats.dat" using 1:(column(3) / (column(1))) title "data transferred per peer"
Please sign in to comment.
Something went wrong with that request. Please try again.