Lotería en la Blockchain de Nxt programada en Golang

En mi primer artículo hice una introducción teórica de la blockchain, de lo que puede aportar para tu proyecto de software y los conceptos básicos para interactuar con la blockchain de Nxt en PHP.

Hoy voy a presentar un pequeño programa de lotería escrito en Go.

Requisitos previos:
Golang (testado con Go 1.6.2)
NRS 1.10.1 (https://bitbucket.org/JeanLucPicard/nxt/downloads/)

Este programa es completamente funcional y ejecuta un sorteo de lotería todos los domingos [1]. Originalmente fue escrito en PHP, las dos fuentes están disponibles para su descarga desde el Data Cloud de Nxt [2].

Lógica de la aplicación

Los boletos enviados por los usuarios en mensajes adjuntos son el suministro de datos para esta aplicación.

Un jugador de la lotería envía 10 NXT a la cuenta de la lotería adjuntando un mensaje público no encriptado con una secuencia de 5 números, entre el 0 y el 30, separados por comas. El mensaje adjunto tiene que ser público para que las recompensas sean auditables usando la blockchain.

La app hace una llamada al servidor NXT para que le proporcione todas las transacciones desde la cuenta de la lotería, clasificándola y seleccionando únicamente las transacciones válidas para así crear un trozo de mapa (conjunto multidimensional en PHP) de todas las cuentas de los jugadores y de sus cadenas de números.Esto también calcula la suma total, de Nxt, a pagar a los jugadores calculando la suma total de todos los boletos validados.

La app, una vez que ha recibido todos los datos válidos, ejecuta 3 rondas de lotería. Cada una de esas rondas recibe una porción, de la suma total disponible para los pagos, a repartir entre los ganadores. En la ronda de 5, la aplicación encuentra los usuario que han adivinado correctamente los 5 números y envía las recompensas. En la ronda 4, la aplicación hace lo mismo para los usuarios que han adivinado 4 números, el monto restante de los boletos participantes es ahora menor que la del ganador(es) de la ronda 5. Se repite lo mismo para la ronda 3.

Esta es la esencia de como funciona la aplicación.

Un poco más sobre el funcionamiento interno

Para cada una de las tres rondas, la lotería genera secuencias de 5 números y las compara con las cadenas de números de los boletos hasta que encuentra uno o más ganadores. Se puede decir que la lotería “fuerza” encontrar una secuencia ganadora del boleto(s).

Con un número limitado de usuarios esto parece ser la única manera sensata para ejecutar una lotería y no tener que recoger y guardar un premio gordo durante meses y/o años.

Vamos a echar un vistazo a la función que genera la secuencia de los 5 números y devuelve una matriz de ellos a la función de llamada. Esta función se invoca un promedio de cientos de miles de veces para encontrar la secuencia de 5 números en una de las entradas cuando tenemos un número muy limitado de participantes. Se tarda una fracción de segundo. En PHP se necesita un poquito más de tiempo (uno o dos segundos), aunque el rendimiento de PHP 7 es realmente bueno.

func genFive(seed string) [5]int {
   var r [5]int
   seedInt, _ := strconv.Atoi(seed)
   d := false
   for a := offset; a < offset+5; a++ { 
      rand.Seed(int64(seedInt + offset)) 
      var dup [31]int 
      d = false 
      r[0] = rand.Intn(31) 
      r[1] = rand.Intn(31) 
      r[2] = rand.Intn(31) 
      r[3] = rand.Intn(31) 
      r[4] = rand.Intn(31) 
      for _, v := range r { 
         dup[v]++ 
      } 
      for k, _ := range dup { 
         if dup[k] > 1 {
            d = true
         }
      }
      offset = offset + 5
      if d == false {
         return r
      }
   }
   return r
}

Una característica importante de la lotería en la blockchain es que tiene que ser completamente transparente.
Todo el mundo debe poder validar que los resultados de la lotería no han sido alterados. Una solución lógica y simple a esto es generar secuencias de números con una semilla determinista.

El problema con las semillas deterministas es que, si se sabe de antemano, las secuencias de números se puede predecir y , a se podría llegar a hacer trampas en la lotería. Para hacer frente a este problema volvemos de nuevo a la Blockchain de NXT, para encontrar una fuente de semilla con la función getSeed().

func getSeed() (string, string) {
 type BlockchainStatus struct {
    NumberOfBlocks int `json:"numberOfBlocks"`
 }
 var status BlockchainStatus
 if seedBlockOutput, b := 
sendQuery("requestType=getBlockchainStatus", 
true); 
b != false { if err := 
json.Unmarshal([]byte(seedBlockOutput), &status); 
err != nil {
     fmt.Println(err)
   }
  }
 seedBlockHeight := 
strconv.Itoa(status.NumberOfBlocks - 11)

 type BlockId struct {
  Block string `json:"block"`
 }
 var block BlockId
 if seedBlockId, b := 
sendQuery("requestType=getBlockId&height="
+seedBlockHeight, true); b != false {
if err := json.Unmarshal([]byte(seedBlockId), 
&block); err != nil {
         fmt.Println(err)
    }
  }
 seed := block.Block[len(block.Block)-5:]
 return seed, seedBlockHeight
}

La app se ejecuta cada domingo a las 18:00 UTC.

Lo primero que esto hace en la función getSeed() es ir a buscar la identificación del bloque que se generó 10 bloques antes del inicio de la aplicación (como se puede ver en la copia local de la blockchain en el nodo de la lotería) y obtener los últimos 5 dígitos de la ID del bloque como semilla. Debido a la latencia de la red y las reorganizaciones ocasionales de la blockchain (de 1 a 3 bloques) el nodo de la lotería puede que no vea la misma información que los otros nodos. El número 10 para obtener el bloque de la semilla fue escogido por la razón que debemos estar razonablemente seguros que este bloque no será reorganizado.

Puede afirmarse que existe la posibilidad teórica de que pueda predecirse el identificador del bloque. En mi opinión, las posibilidades de que esto suceda son muy bajas, pero dejo a los lectores que lo debatan y decidan por ellos mismos.

Ahora que la App tiene su propia semilla, puede realizar su función de manera que los usuarios no necesiten confiar en el organizador de la lotería.

El código fuente “Go” no incluye la rutina de verificación de los resultados anteriores.
El código fuente “PHP” lo tiene, es totalmente funcional y se puede utilizar para verificar, de manera independiente, todos los resultados anteriores con las semillas deterministas de la blockchain.

Para “Go” yo uso la función de enviar y retornar solicitudes al servidor Nxt.

func sendQuery(Query string, Active bool) 
(output string, b bool) {
   output = ""
   b = false
   if Active == false {
      output = "Function disabled"
      return
   }
   body := strings.NewReader(Query)
   req, err := http.NewRequest("POST", 
"http://127.0.0.1:7876/nxt", body)
   if err != nil {
      output = fmt.Sprintf("%s", err)
      return
   }
   req.Header.Set("Content-Type", 
"application/x-www-form-urlencoded")

   resp, err := http.DefaultClient.Do(req)
   if err != nil {
      output = fmt.Sprintf("%s", err)
      return
   }
   bo, err := ioutil.ReadAll(resp.Body)
   defer resp.Body.Close()
   output = fmt.Sprintf("%s", bo)
   match, _ := 
regexp.MatchString(".*errorDescription.*", 
output)
   if match == true {
      fileHandle, _ := 
os.OpenFile("./error.log", os.O_APPEND, 0666)
      writer := bufio.NewWriter(fileHandle)
      defer fileHandle.Close()
      fmt.Fprintln(writer, output)
      writer.Flush()
      return
   }
   b = true
   return
}

Los resultados son recibidos como una cadena JSON y necesitan estar ordenados con una estructura apropiada.

validPlayers := make([]map[string]string, 0)

lotteryAccount := "NXT-YXC4-RB92-F6MQ-2ZRA6"

type Attachment struct {
   Message       string `json:"message"`
   MessageIsText bool   `json:"messageIsText"`
}

type Transaction struct {
   Timestamp   int        `json:"timestamp"`
   AmountNQT   string     `json:"amountNQT"`
   ID          string     `json:"transaction"`
   SenderRS    string     `json:"senderRS"`
   RecipientRS string     `json:"recipientRS"`
   Attached    Attachment `json:"attachment"`
}

type Response struct {
   Transactions []Transaction 
`json:"transactions"`
}
Query := 
"requestType=getBlockchainTransactions&account=" +
lotteryAccount + 
"&type=0&subtype=0&executedOnly=true"

if v, a := sendQuery(Query, true); a == true {
   var transactions Response
   if err := json.Unmarshal([]byte(v), 
&transactions); err != nil {
      fmt.Println(err)
   }

 p := 0
 for k, _ := range transactions.Transactions {
    // code to check tickets for validity.
    // if transaction satisfies all criteria 
    // add it to the slice of valid tickets.
		
    validPlayers = append(validPlayers, 
make(map[string]string))
    validPlayers[p][txSender] = lotteryNumbers
    p++
			
   }
}

Ahora que “validPlayers” tiene todas las entradas correctas podemos iniciar el juego.

process() recibe un número entero (5, 4, o 3) y otros parámetros, incluyendo validPlayers y ejecuta tres rondas de la lotería. Se hace una llamada a la función getWinners(), que se llama genFive() para generar secuencias de números hasta que se encuentre al menos un ganador. getWinners() devuelve los resultados a process() y este es el encargado de: enviar la recompensa, eliminar el boleto ganador y devolver las entradas restantes a main() para rondas posteriores. Hay una función auxiliar denominada preparePlayers() que recrea validPlayers sin los espacios vacíos liberados por las entradas eliminadas.

Animo a todos los programadores que programen en el blockchain NXT. Es muy sencillo gracias al enlace API de todas sus funcionalidades del núcleo. https://nxtwiki.org/wiki/The_Nxt_API

Mi próxima aplicación será probablemente una aplicación de votación, con registros de votos inmutables y guardados en la blockchain. ¿Cree usted que una aplicación como esta puede resultar útil en el mundo moderno? Por cierto, Nxt tiene incorporado su propio sistema de votación. Es muy fácil olvidar lo que Nxt tiene, porque dispone de muchas características que son accesibles a través de la API, que se encuentra amablemente programada por los desarrolladores del núcleo de Nxt para favorecer su uso. Puedes ‘mínar’ tus primeras monedas NXT para enviar las transacciones al proyecto del nodo de la suerte que se ejecuta en un nodo público, acude a nxtforum.org y encontrarás la manera.
Por favor, dejen sus sugerencias y comentarios.

 


1. Lotería en nxtforum.org.

2. Para acceder al Nxt Data Cloud (Nube de datos de Nxt), descargue e instale el NRS (Nxt Reference Software 1.10.1) y busque la palabra clave lottery. Puedes descargar el código fuente desde cualquier servidor Nxt con las API abierta, por ejemplo:

Go-> http://23.94.134.161:7876/nxt?requestType=downloadTaggedData&transaction=7872865106538381099&retrieve=true PHP: http://23.94.134.161:7876/nxt?requestType=downloadTaggedData&transaction=13031806327722095646&retrieve=true

DE VUELTA AL POST.

Deja un comentario