Cervezas, apuestas y la blockchain de Nxt

Supongamos que tienes un grupo de amigos con los que te reúnes un un bar de deportes para tomar una cerveza, ver eventos deportivos y hacer apuestas. Pues es posible hacer apuestas utilizando la blockchain de Nxt utilizando una sencilla aplicación Python que lo automatice todo.

En mi primer artículo presenté los conceptos básicos de la blockchain. Tal y como vimos, el principal valor de la cadena de bloques es la inmutabilidad de los datos contenidos en las transacciones almacenadas en cada bloque, a partir de los cuales se construye la blockchain.

Hoy quería mostrar como construir una aplicación de apuestas en python de manera sencilla pero funcional. Para esta aplicación es básico que los datos sean inmutables, pero en caso de que sea necesario que una apuesta se pueda cancelar, se puede modificar la app para incluir esta funcionalidad adicional.

Prerrequisitos:
NRS (Software de Referencia Nxt) 1.10.2: https://bitbucket.org/JeanLucPicard/nxt/downloads/Python 3 (o Python 2 con una ligera modificación). Una cerveza o dos (opcional).

Lógica de la aplicación

Un usuario emite una apuesta (transfiere una determinada cantidad de monedas NXT) a la cuenta de apuestas y le añade un adjunto público y sin encriptar. En el adjunto se debería incluir el identificador de la apuesta, dos puntos, y un número indicando su elección en la apuesta correspondiente. Pongamos un ejemplo: el bar de eventos deportivos crea una porra sobre qué equipo saldrá victorioso en un partido de fútbol. Le asigna un Identificador (ID) al partido, por ejemplo “30”.

Las opciones son:
“1” – gana el equipo de casa “2” – gana el equipo visitante. “3” – empatan.

Para apostar que gana el equipo de casa, el jugador debería enviar una transacción con una determinada cantidad de NXT, adjuntando un mensaje público sin encriptar (para que sea auditable) con el siguiente contenido 30:1

Cuando llegue el momento, el gestor de la apuesta ejecuta la app para localizar a los ganadores y realizar los pagos. El gestor puede quedarse una pequeña comisión por el servicio prestado.

Implementación

El servidor Nxt recibe las peticiones en el puerto 7876. Si por defecto lo ejecutas en el mismo equipo en el que ejecutas el código python debes enviar tus peticiones a http://127.0.0.1:7876/nxt

Para nuestra app necesitaremos importar unos pocos módulos y escribir la función para hacer consultas que se usará unas cuantas veces en la ejecución de la app.

import sys, json, re

def sendQuery(Query):
  from urllib import parse, request
  params = parse.urlencode(Query)
  headers = {"Content-type": "application/x-www-form-urlencoded", 
"Accept": "text/plain"}
  req = request.Request("http://127.0.0.1:7876/nxt", 
params.encode('utf-8'))
  response = request.urlopen(req)
  return response.read()
	
myAccount = "NXT-AAAA-BBBB-CCCC-DDDDD"
totalAmount = 0
serviceFee = 0.05
players = []
secretPhrase = "bla bla bla"

La función devuelve una cadena JSON que necesita ser decodificado, con transacciones y adjuntos que necesitan ser analizados. También se inician unas determinadas variables

A continuación debes pedirle a la cuenta con las apuestas que busque en todas las transacciones entrantes.

Esta es la solicitud que puedes enviar al servidor Nxt para ejecutar esto:Query = {‘requestType’: ‘getBlockchainTransactions’,

'account': myAccount, 'type': '0', 'subtype': '0', 'executedOnly': 'true'}

decode JSON and load it to a variable for further processing.

output = sendQuery(Query)
data = json.loads(output.decode('utf-8'))

Puesto que ahora ya contamos con todos los datos, ejecutamos un loop en la tabla para deshacernos de las transacciones que no necesitamos. Por ejemplo, no necesitamos procesar las transacciones salientes, así que continuamos nuestro loop desde el siguiente objeto

for i in data['transactions']:
  txAmount = i['amountNQT']
  txSender = i['senderRS']
  if txSender == myAccount:
      continue
  txRecipient = i['recipientRS']
  if txRecipient != myAccount:
      continue

A continuación extraemos los adjuntos, nos aseguramos de que no están encriptados y quitamos los espacios en blanco

  line = i['attachment']['message']
  lineIsText = i['attachment']['messageIsText']
  if line.strip() != "" and lineIsText == True:
    line = line.strip()

El siguiente paso es hacer concordar el mensaje enviado con la expresión regular. Tiene que satisfacer el formato “Número:Número”, el primer número puede contener hasta 3 dígitos.

    matchLine = re.match(r'(\d{1,3}:\d{1})(.*)', line, re.M|re.I)

Si el mensaje adjunto tiene un formato correcto, continuamos analizando…

La aplicación para las apuestas acepta hasta un máximo de 3 argumentos en la línea de comandos: identificador de juego, el número elegido que ha ganado, y un tercer parámetro opcional entre comillas para mostrar la combinación ganadora en el mensaje con el pago.

Por ejemplo, el anfitrión de la apuesta debe ejecutarlo del siguiente modo:

python3 bet.py 30 1 “El equipo local gana”

La aplicación analizará las transacciones con id 30 y premiará a los jugadores que eligieron ‘1’.
El tercer argumento se podría omitir: python3 bet.py 30 1

A continuación debemos calcular la cantidad de dinero que ha recibido nuestro, haciendo un listado diccionario de jugadores que adivinaron el número correcto, y calculando la suma total de dinero a distribuir una vez que el anfitrión ha recibido su comisión opcional.

    if matchLine:
      if matchLine.group(1).split(':')[0] == sys.argv[1]:
        totalAmount += int(txAmount)
        if matchLine.group(1).split(':')[1] == sys.argv[2]:
          d = {txSender: matchLine.group(1).split(':')[1], 
'amount': txAmount}
          players.append(dict(d))
afterFee = float(totalAmount - totalAmount * serviceFee)

Tenemos ganadores en la lista ‘players’ de los diccionarios, tenemos dinero para distribuir en el apartado ‘afterFee’, ahora dinos los pagos de premios procesados.

sumCorrect = 0
for i in players:
  for key in i:
    if i[key] == sys.argv[2]:
      sumCorrect += float(i['amount'])
for i in players:
  for key in i:
    if i[key] == sys.argv[2]:
      t = float(i['amount']) * (afterFee / sumCorrect)
      if t > afterFee:
        strSendAmount = str(int(afterFee))
      else:
        strSendAmount = str(int(t))
      if len(sys.argv) > 3:
        reply = sys.argv[3]
      else:
        reply = sys.argv[2]
      message = "Payout for correct bet in poll " 
+ sys.argv[1] + "; your reply: " + reply
      Query = {'requestType': 'sendMoney', 'recipient': key, 
      'amountNQT': strSendAmount, 'secretPhrase': secretPhrase, 
      'feeNQT': '100000000', 'deadline': '1440', 'broadcast': 'true', 
'message': message, 'messageIsText': 'true', 'messageIsPrunable': 'true'}
      sendQuery(Query)

No es necesario decir que la app se puede optimizar para procesar errores, cancelar apuestas u, opcionalmente, permitir otros parámetros en los mensajes adjuntos. Los mensajes adjuntos se pueden emitir desde el cliente Nxt (NRS) o desde un monedero online que permita enviar adjuntos

Para realizar transacciones en la blockchain de Nxt necesitas monedas NXT, que puedes adquirir directamente cambiando Bitcoin por Nxt o participando en el proyecto del “Nodo afortunado” ejecutando un nodo público de Nxt (Nxt server).

Para saber como participar en el proyecto del “Nodo afortunado”, por favor visita https://nxtforum.org.

Hay más de un centenar de llamadas API que se pueden solicitar al servidor Nxt. Una lista completa de parámetros obligatorios y opcionales así como ejemplos está disponible en https://nxtwiki.org/wiki/The_Nxt_API.

¡Salud!

blockchain-betting

View this in: English

Deja un comentario