TRABAJO FINAL DE GRADO · D/Dª Jorge Fernández Molines, autor del Trabajo de Fin de Título Block...
Transcript of TRABAJO FINAL DE GRADO · D/Dª Jorge Fernández Molines, autor del Trabajo de Fin de Título Block...
TRABAJO FINAL DE GRADO
Block Lotto
Autor: Jorge Fernández Molines Tutor: José Juan Hernández Cabrera
TFT04
Ver.12 21/05/18
D/Dª Jorge Fernández Molines, autor del Trabajo de Fin de Título Block Lotto, correspondiente a la titulación Grado en Ingeniería Informática, en colaboración con la empresa/proyecto (indicar en su caso) S O L I C I T A que se inicie el procedimiento de defensa del mismo, para lo que se adjunta la documentación requerida. Asimismo, con respecto al registro de la propiedad intelectual/industrial del TFT, declara que:
[ ] Se ha iniciado o hay intención de iniciarlo (defensa no pública). [X] No está previsto.
Y para que así conste firma la presente.
Las Palmas de Gran Canaria, a 04 de enero de 2019.
El estudiante
Fdo.: ____________________________
DIRECTOR DE LA ESCUELA DE INGENIERÍA INFORMÁTICA
SOLICITUD DE DEFENSA DE TRABAJO DE FIN DE TÍTULO
A rellenar y firmar obligatoriamente por el/los tutor/es
En relación a la presente solicitud, se informa:
[X] Positivamente [ ] Negativamente
(la justificación en caso de informe
negativo deberá incluirse en el
TFT05)
Fdo.: ____________________________
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
Contenido 1. Introducción .............................................................................................................................1
1.1 ¿Qué es Blockchain? ..........................................................................................................2
1.2 ¿Qué es Bitcoin? ................................................................................................................3
1.3 ¿Qué es Lightning Network? ..............................................................................................3
1.3.1 Transferencias con Lightning Network Daemon .........................................................4
2. Motivación y objetivos. ............................................................................................................6
3. Justificación de las competencias .............................................................................................7
4. Aportación socioeconómica .....................................................................................................9
5. Análisis .....................................................................................................................................9
5.1 Aplicaciones que hacen uso de Lightning Network ............................................................9
5.2 Descripción del proyecto .................................................................................................11
5.3 Problemas con Lightning Network ...................................................................................11
5.4 Características ..................................................................................................................13
5.5 Historias de usuario .........................................................................................................13
5.6 Riesgos .............................................................................................................................14
5.7 Licencias de software .......................................................................................................14
6 Diseño .....................................................................................................................................15
6.1 Diagrama de casos de uso ................................................................................................15
6.2 Diagramas de secuencia ...................................................................................................15
6.2.1 Crear cartera .............................................................................................................16
6.2.2 Borrar cartera ...........................................................................................................17
6.2.3 Desbloquear cartera .................................................................................................17
6.2.4 Restaurar cartera ......................................................................................................18
6.2.5 Obtener saldo de la cartera ......................................................................................18
6.2.6 Obtener saldo de los canales ....................................................................................19
6.2.7 Comprar boleto.........................................................................................................20
6.2.8 Ver notificaciones .....................................................................................................22
6.2.9 Marcar notificaciones ...............................................................................................22
6.2.10 Transferencias entre usuarios .................................................................................23
6.2.11 Listado de movimientos ..........................................................................................25
6.2.12 Listar todos los sorteos pendientes ........................................................................26
6.2.13 Cerrar canales para obtener su saldo .....................................................................27
6.2.14 Crear sorteo ............................................................................................................28
6.2.15 Eliminar sorteo .......................................................................................................28
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
6.2.16 Comenzar sorteo ....................................................................................................28
6.2.17 Pagar premio ..........................................................................................................29
6.2.18 Celebrar un sorteo y generar otro del mismo tipo..................................................30
6.3 Diagrama de despliegue ...................................................................................................31
6.4 Diagrama de la base de datos ..........................................................................................32
7. Desarrollo ...............................................................................................................................32
7.1 Metodología .....................................................................................................................32
7.2 Planificación inicial y final ................................................................................................33
7.3 Configuración de los servidores .......................................................................................33
7.3.1 Configuración del servidor Urano .............................................................................34
7.3.2 Configuración del servidor Jupiter. ...........................................................................35
7.3.2.1 Instalación del servicio de base de datos Mysql ................................................35
7.3.2.2 Instalación de los nodos Bitcoin y Lightning Network. ......................................36
7.4. Problemas encontrados con Lightning Network .............................................................36
7.5 Explicación del código ......................................................................................................36
7.5.1 pom.xml ....................................................................................................................36
7.5.2 Main.java ..................................................................................................................39
7.5.3 Interfaz .....................................................................................................................40
7.5.4 Fichero de configuración ..........................................................................................40
7.5.5 BlockLottoService .....................................................................................................41
7.5.5.1 Crear cartera......................................................................................................41
7.5.5.2 Restaurar cartera ...............................................................................................42
7.5.5.3 Desbloquear cartera ..........................................................................................44
7.5.5.4 Consultar saldo de la cartera .............................................................................44
7.5.5.5 Comprar boletos ................................................................................................45
7.5.5.6 Consultar saldo en los canales abiertos. ............................................................56
7.5.5.7 Recuperar saldo de los canales abiertos. ...........................................................57
7.5.5.8 Historial de movimientos...................................................................................58
7.5.5.9 Realizar transferencias a otros usuarios ............................................................59
7.5.5.10 Consultar notificaciones ..................................................................................60
7.5.5.11 Marcar notificaciones como leídas ..................................................................61
7.5.5.12 Lista de todos los sorteos ................................................................................62
7.5.5.13 Lista de los sorteos pendientes de celebración. ..............................................62
7.5.5.14 Lista de los sorteos con participación ..............................................................63
7.5.5.15 Listar de los sorteos ya celebrados con participación......................................63
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
7.5.6 Sistema de gestión de sorteos. .................................................................................64
7.5.6.1 Quartz Listener ..................................................................................................64
7.5.6.2 BlockDrawListener .............................................................................................66
7.5.6.3 StartDrawJob .....................................................................................................67
7.5.6.4 CloseMasterChannelJob ....................................................................................70
7.5.7 TDD ...........................................................................................................................70
7.5.7.1 ManageRaffleTest..............................................................................................71
7.5.7.2 MockLightningAccessorTest ..............................................................................73
8. Conclusiones y trabajos futuros .............................................................................................78
8.1 Resultado .........................................................................................................................78
8.2 Consecución de los objetivos ...........................................................................................79
8.3 Trabajos futuros ...............................................................................................................79
9. Herramientas utilizadas ..........................................................................................................80
9.1 Hardware .........................................................................................................................80
9.1.1 Ordenador portátil personal .....................................................................................80
9.1.2 Servidores .................................................................................................................80
9.2 Software ...........................................................................................................................80
9.2.1 Windows 10 Home ...................................................................................................80
9.2.2 IntelliJ IDEA Ultimate 2018.3 ....................................................................................80
9.2.3 Trello .........................................................................................................................80
9.2.4 SoapUI by SmartBear ................................................................................................80
9.2.5 Github .......................................................................................................................80
9.2.6 MySQL Workbench ...................................................................................................80
9.2.7 Github Desktop .........................................................................................................80
10. Referencias ...........................................................................................................................81
Anexo I: Manual de usuario del cliente ......................................................................................82
1. Creación de cartera ............................................................................................................82
2. Restauración de cartera .....................................................................................................83
3. Desbloqueo de cartera .......................................................................................................83
4. Consulta de saldo ...............................................................................................................84
5. Compra de boletos .............................................................................................................84
6. Consulta de saldo en los canales ........................................................................................84
7. Extraer fondos de los canales.............................................................................................85
8.Movimientos de pago .........................................................................................................85
9. Transferencias a otra cuenta ..............................................................................................85
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
10. Ver notificaciones ............................................................................................................86
11. Marcar notificación como leída........................................................................................86
12. Listar todos los sorteos ....................................................................................................86
13. Listar los sorteos pendientes ...........................................................................................87
14. Listar los sorteos pendientes en los que participo ...........................................................87
15. Listar todos los sorteos en los que he participado ...........................................................88
Anexo II: Manual de usuario del administrador. ........................................................................88
1. Configuración de los sorteos ..............................................................................................88
1.1 Base de datos...............................................................................................................88
1.2 Quartz ..........................................................................................................................89
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
1
1. Introducción Actualmente son muchas las noticias que aparecen en los medios de comunicación relacionadas
con el mundo de la blockchain. Es innegable que esta tecnología ha llegado para quedarse y
poco a poco se está volviendo más indispensable gracias a las ventajas que esta proporciona.
En este proyecto de fin de grado me centraré en el protocolo Lightning Network que presenta
sustanciosas mejoras con respecto a las características ofrecidas por la red Bitcoin por si sola.
Este novedoso protocolo aún se encuentra en fase de desarrollo por lo que no se recomienda
su uso en la red principal de bitcoin, debido a inconsistencias encontradas a la hora de ejecutar
diferentes funciones.
Se plantea el desarrollo de un producto software de lotería en el cual el usuario podrá participar
en sorteos comprando previamente boletos y en caso de ser el ganador recibir el premio. Los
pagos se realizan mediante una blockchain, concretamente bitcoin, haciendo a su vez uso del
protocolo lightning network, lo que posibilita los pagos instantáneos.
El objetivo de este proyecto es desarrollar el LottoService e integrarlo con el LottoWallet, que
se desarrollará como objetivo de otro TFT. Por tanto, este proyecto se realiza de forma conjunta
con otro estudiante, Cynthia J. Afonso García. El otro proyecto TFT constará de una interfaz
desarrollada en Android que se comunicará con el servicio a través del LottoService, para facilitar
de este modo al cliente, el uso de la aplicación.
Los clientes se comunicarán haciendo uso de una aplicación instalada en su teléfono móvil. Esta
establecerá una conexión con el servicio a través de un web service que también ha sido
desarrollado para este proyecto.
Ilustración 1 - Esquema BLockLotto
Este proyecto ha sido tutorizado por D. José Juan Hernández Cabrera, doctor titular en la ULPGC
del área de conocimiento de Ciencias de la Computación e Inteligencia Artificial.
Antes de continuar conviene aclarar algunos conceptos como son Blockchain, Bitcoin y Lightning
Network.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
2
1.1 ¿Qué es Blockchain? Es una tecnología la cual nos permite sin necesidad de intermediarios, cualquier tipo de
transacción entre dos o más personas de una manera fiable y segura. Para entendernos diremos
que la blockchain es como un libro de contabilidad en el que se irán añadiendo apuntes de los
distintos movimientos que se vayan produciendo entre las distintas carteras digitales, agrupados
en lo que llamamos bloques.
Las carteras digitales son interfaces con las que el cliente puede interactuar con la red
blockchain. Con ellas los usuarios pueden realizar transacciones, ver su saldo y administrar su
identidad digital. Estas carteras digitales poseen identificadores que las hacen únicas.
Para que todo el sistema funcione, es necesario que una cantidad indeterminada de
computadoras resuelvan un problema matemático del protocolo de consenso, para así autorizar
la adición de los bloques de transacciones que generan las carteras digitales.
En este protocolo de consenso, se recompensa a los nodos a cambio de crear un bloque. Pueden
existir varias cadenas de consenso, pero solamente la cadena más larga, determinará si la
transacción queda validada o no. Al crear un bloque, el nodo, incluye una transacción de
creación de monedas en ese mismo bloque. Este nodo puede escoger la dirección a la que serán
enviadas esas monedas. Para poder crear el bloque, el nodo debe resolver un problema hash.
La dificultad de resolución de este problema varía según la cantidad total de procesamiento que
se encuentra en esa red blockchain en un momento dado. De esta manera blockchain se protege
de posibles ataques a la cadena de bloques.
Por otro lado, nos encontramos los nodos, que se encargan tanto de hacer cumplir las normas
como de guardar una copia exacta de la red blockchain. Dentro de este apartado debo de hacer
mención de la descentralización. Las blockchains pueden ser o no descentralizadas. Como
ejemplo de una red descentralizada tenemos la criptomoneda bitcoin basada en blockchain. En
contra tenemos, por ejemplo, las redes internas de los bancos que trabajan con sus propias
redes blockchain. Una red descentralizada impide que el poder para controlar y aprobar o
desaprobar transacciones, recaiga en una única parte. Esto es, el poder se encuentra distribuido
entre muchas partes que deben de llegar a un acuerdo para poder realizar cambios en las reglas
que legislan el funcionamiento de la blockchain. De esta manera en una red descentralizada, un
nodo no puede aceptar o desestimar transacciones unilateralmente.
Con todo esto, tenemos que la tecnología blockchain nos garantiza que todos los registros de
transacciones sean legítimos e inmutables. Cada dato o transacción posee una huella digital
única, esto es, no podemos repetir ni modificar lo anteriormente incluido en la blockchain.
Son varios los usos que se le está dando a las cadenas de bloques. Algunos de los más
extendidos: Criptomonedas, registro de documentos, operaciones comerciales, trazabilidad de
suministros y contratos inteligentes.
Referencias: [1]
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
3
1.2 ¿Qué es Bitcoin? Bitcoin es una moneda virtual basada en la tecnología blockchain. Fue la primera criptomoneda
creada con dicha tecnología. Su creador, anónimo, es conocido por el pseudónimo de “Satoshi
Nakamoto”.
Posee las siguientes características:
• Permite realizar micropagos: Un bitcoin puede dividirse en un número determinado de
satoshis, concretamente en la proporción un bitcoin es igual a cien millones de satoshis. Esto
permite que podamos realizar pagos por una cantidad mínima de 546 satoshis o lo que es
lo mismo que 0,00000546 Bitcoins. A la unidad Satoshi se le llama así en honor al creador
de la moneda.
• Posibilidad de realizar grandes transacciones rápidamente y sin complicaciones.
• Protección contra la inflación: El número total de bitcoins que podrá crear el sistema es de
21 millones. Al no existir una generación infinita de monedas, no se produce la inflación o
pérdida de valor de la moneda.
• Descentralizado: No puede ser controlado por un ente ya que se necesita el acuerdo de una
gran mayoría de nodos para cambiar las reglas de uso de esta moneda.
• Permite las transacciones internacionales.
• Es seguro.
• Fungible: Se puede intercambiar por bienes de un valor similar.
Algunos de los problemas encontrados en Bitcoin:
• Escalabilidad: Existe una limitación en el número de transacciones que pueden ser
procesadas en un intervalo específico de tiempo. Esto sucede a causa de un aumento
considerable de usuarios y con ello transacciones.
• Programación limitada: Al tratarse de una criptomoneda de primera generación, no está del
todo preparada para la creación de smart contracts.
• Lentitud a la hora de realizar las transacciones: De media se debe esperar unos 10 minutos
hasta que la transacción se confirme. Esto impide que podamos usar bitcoin para realizar
micro transacciones en nuestras compras del día a día.
• Comisiones elevadas por transacción.
Referencias: [2] [3] [4] [5] [6]
1.3 ¿Qué es Lightning Network? Es un protocolo creado por Joseph Poon y Thaddeus Dryja, que se ejecuta sobre la red bitcoin y
da solución a algunos problemas anteriormente mencionados.
Principales características:
• Permite los pagos instantáneos. Ya no es necesaria la validación de cada transacción. Con
ello nos ahorraremos los tiempos de validación que hacían impracticable las micro
transacciones en las compras del día a día. Por ejemplo, si vamos a una cafetería y queremos
comprar un café usando el protocolo lightning network, veremos como la transacción
finaliza instantáneamente, obteniendo el café de forma también instantánea. Al contrario,
si esta transacción la hiciéramos pagando únicamente con la red bitcoin, tardaríamos en
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
4
obtener la confirmación entre diez minutos y una hora, dependiendo de lo congestionada
que estuviera la red. De este modo el dueño de la tienda, no nos daría el producto hasta que
obtuviera la confirmación.
• Mejora enormemente la escalabilidad de la red, gracias a que permite un aumento
considerable del número de transacciones efectuadas por segundo.
• Las comisiones en la red Lightning Network son más bajas. Esto permite realizar micro
transacciones y no tener que pagar grandes cantidades de comisiones. En cambio, en la red
bitcoin, dependiendo de la cantidad de dinero a transferir, podría darse la circunstancia de
que las comisiones fueran iguales a la cantidad de dinero que se quiere enviar.
Referencias: [8]
1.3.1 Transferencias con Lightning Network Daemon
En este apartado se explicará cómo funcionan las transferencias en Lightning Network.
En primer lugar, es necesaria la creación de una cartera Bitcoin sobre un nodo Lightning para
poder realizar transferencias instantáneas. De hecho, ambas carteras deben de ser creadas
sobre un nodo Lightning. Se debe establecer una conexión entre ambas carteras, a esto Lightning
lo llama peer. Una vez las carteras ya están conectadas, se puede abrir un canal para comenzar
a realizar transferencias. Cuando se abre el canal, la cartera que lo abre puede añadir fondos a
ambas partes. Lightning Network Daemon no permite por el momento que ambas carteras
inserten fondos en un mismo canal, como tampoco permite insertar fondos después de que el
canal haya sido creado. Para la creación del canal es necesario el minado de 6 bloques. En ese
momento se registra en la blockchain. Teniendo en cuenta que el minado de un bloque en la red
Bitcoin tarda un promedio de 10 minutos, tenemos que esperar una hora por la creación del
canal. Sin embargo, una vez creado el canal, se podrán realizar tantas transferencias
instantáneas como se quieran, siempre y cuando se dispongan de suficientes fondos dentro del
canal. Hay que señalar que, en la creación de un canal, se cobra una comisión de 9050 satoshis.
En este ejemplo vemos como Mary y Adrián son dos nodos de Lightning Network.
Ilustración 2 - Diagrama de pago directo 1
Mary ha abierto un canal, y ha metido 3 Bitcoins dentro de este. Vamos a obviar las comisiones
de 9050 satoshis para simplificar el ejemplo.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
5
Ilustración 3 - Diagrama de pago directo 2
Mary y Adrián podrán realizar tantas transacciones como quieran de manera instantánea hasta
el punto en el que el canal queda de esta manera.
Ilustración 4 - Diagrama de pago directo 3
En cualquier momento cualquier de los dos nodos puede elegir cerrar el canal para recuperar el
saldo en su parte del canal. Para efectuar el cierre del canal, es necesario el minado de un
bloque. Por lo que el cerrado de canales tampoco es un proceso instantáneo, tardaría en
cerrarse unos 10 minutos. Es en este momento cuando se registra la transferencia en la
Blockchain.
Situación una vez cerrado el canal.
Ilustración 5 - Diagrama de pago directo 4
También es posible realizar pagos a través de canales ajenos al nuestro. Partimos de la base que
hay unos nodos ya creados y que tienen canales abiertos entre ellos.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
6
Ilustración 6 - Diagrama de pago con multi-hop 1
Adrián quiere pagarle 3 Bitcoins a Mary, pero no tiene una conexión directa con ella. Esto no
tiene importancia ya que Jorge puede hacer de intermediario. No se necesita su aprobación para
utilizarlo de intermediario ya que Jorge antes y después de la transferencia, seguirá teniendo la
misma cantidad total de fondos en sus canales.
Ilustración 7 - Diagrama de pago con multi-hop 2
Vemos como Adrián ha perdido 3 Bitcoins, Jorge se ha quedado con los 6 que tenía en un
principio, pero distribuidos de manera diferente y Mary ha ganado 3 hasta tener 5 Bitcoins.
A este procedimiento de realizar pagos a través de un tercero se le llama multi-hop. En este
ejemplo solo ha habido un intermediario, pero podrían haber sido muchos más.
Referencias: [10] [11]
2. Motivación y objetivos. Como se ha comentado anteriormente, blockchain es una tecnología en auge que no he tenido
oportunidad de estudiar en el grado. Es por ello por lo que me parece interesante profundizar
más en esta materia, teniendo en cuenta la cantidad de campos donde se podría aplicar esta
tecnología.
Podemos encontrar aplicaciones en distintos sectores:
• Sanidad:
o Se pueden reducir los fraudes en pagos en el sector sanitario.
o Controlar la disponibilidad de historiales médicos y así mantener la privacidad.
• Sector financiero:
o Transferencias más rápidas que con las monedas del sistema tradicional.
o Estas transferencias se pueden realizar a cualquier hora del día.
• Servicios legales:
o En los contratos inteligentes, no es necesario el intermediario ya que es el mismo
contrato el que vigila que la ley de este se cumpla.
• Entidades gubernamentales:
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
7
o El suministro de información entre el ciudadano y la entidad ocurre en tiempo
real.
o Se reduce la corrupción y se aumenta la transparencia.
Por otro lado, la reciente aparición del protocolo Lightning Network, como anteriormente
comentamos, corrige los problemas de escalabilidad e inmediatez en los pagos con Bitcoin. Me
parece muy interesante su estudio para entender mejor su funcionamiento y quien sabe, quizás
aplicar lo que pueda aprender en este proyecto en un futuro.
Los objetivos del presente proyecto son:
• Conocer mejor la blockchain, bitcoin y Lightning Network a través de un caso práctico.
• Poner en práctica lo aprendido en el grado. Esta es la primera oportunidad que tengo de
enfrentarme a un proyecto tras haber estudiado el Grado en Ingeniería Informática. Me
gustaría poner en práctica todo lo aprendido para de esta manera mejorar y poder
convertirme en un buen profesional.
Referencias: [18]
3. Justificación de las competencias Se han justificado las siguientes competencias.
CII01 - Capacidad para diseñar, desarrollar, seleccionar y evaluar aplicaciones y sistemas
informáticos, asegurando su fiabilidad, seguridad y calidad, conforme a principios éticos y a la
legislación y normativa vigente.
Competencia justificada ya que este proyecto se ha llevado a cabo pasando por fases de análisis,
diseño, implementación y verificación a través de TDD para verificar su correcto funcionamiento.
CII02 - Capacidad para planificar, concebir, desplegar y dirigir proyectos, servicios y sistemas
informáticos en todos los ámbitos, liderando su puesta en marcha y su mejora continua y
valorando su impacto económico y social.
Competencia justificada ya que, para desarrollar este proyecto, se ha tenido que planificar el
mismo para que la integración con la parte del LottoService sea una realidad. Se han establecido
unas condiciones entre los dos proyectos para no encontrarnos problemas cuando el
LottoWallet haga uso del LottoService.
CII03 - Capacidad para comprender la importancia de la negociación, los hábitos de trabajo
efectivos, el liderazgo y las habilidades de comunicación en todos los entornos de desarrollo
de software
Competencia justificada ya que, para poder desarrollar este proyecto, he tenido que ponerme
de acuerdo con la desarrolladora de la otra parte del proyecto, Cynthia J. Afonso García, en los
aspectos que atañen a ambas.
CII08 - Capacidad para analizar, diseñar, construir y mantener aplicaciones de forma robusta,
segura y eficiente, eligiendo el paradigma y los lenguajes de programación más adecuados
Competencia justificada ya que se ha elegido el lenguaje de programación Java, el cual es
adecuado para el desarrollo del proyecto. Por otro lado, se ha desarrollado el proyecto desde
cero, con sus fases de análisis y diseño.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
8
CII011 - Conocimiento y aplicación de las características, funcionalidades y estructura de los
Sistemas Distribuidos, las Redes de Computadores e Internet y diseñar e implementar
aplicaciones basadas en ellas.
Competencia justificada ya que el LottoWallet se comunica con este proyecto, el LottoService a
través de internet para hacer peticiones al web service.
CII013 - Conocimiento y aplicación de las herramientas necesarias para el almacenamiento,
procesamiento y acceso a los Sistemas de información, incluidos los basados en web
Competencia justificada ya que en el proyecto se hace uso de una base de datos Mysql para el
correcto almacenamiento de la información.
CII016 - Conocimiento y aplicación de los principios, metodologías y ciclos de vida de la
ingeniería de software.
Competencia justificada ya que con el desarrollo de este proyecto se hace uso de la metodología
iterativa. Por otro lado, en el apartado de diseño se hace uso de diagramas que facilitan el diseño
del producto. Lo anteriormente comentado, es todo propio de la ingeniería de software.
IS01 - Capacidad para desarrollar, mantener y evaluar servicios y sistemas software que
satisfagan todos los requisitos del usuario y se comporten de forma fiable y eficiente, sean
asequibles de desarrollar y mantener y cumplan normas de calidad, aplicando las teorías,
principios, métodos y prácticas de la ingeniería del software.
Competencia justificada ya que este proyecto trata sobre el desarrollo de un servicio web, el
cual cumple todos los requisitos del usuario. Se pueden ver las historias de usuario en el punto
de historias de usuario.
IS02 - Capacidad para valorar las necesidades del cliente y especificar los requisitos software
para satisfacer estas necesidades, reconciliando objetivos en conflicto mediante la búsqueda
de compromisos aceptables dentro de las limitaciones derivadas del coste, del tiempo, de la
existencia de sistemas ya desarrollados y de las propias organizaciones.
Competencia justificada ya que, para el desarrollo de este proyecto, se ha tenido que seguir un
proceso de análisis, donde se puede ver las aplicaciones que hacen uso de Lightning Network y
como se comentó en el punto anterior las historias de usuario.
IS03 - Capacidad de dar solución a problemas de integración en función de las estrategias,
estándares y tecnologías disponibles.
Competencia justificada ya que para la creación del web service, primero se especificó junto al
proyecto de la LottoWallet cuales serían los parámetros que devolverían los distintos métodos
del servicio web.
IS04 - Capacidad de identificar y analizar problemas y diseñar, desarrollar, implementar,
verificar y documentar soluciones software sobre la base de un conocimiento adecuado de las
teorías, modelos y técnicas actuales.
Competencia justificada ya que como se explicó en el apartado de problemas con Lightning
Network, se tuvo que buscar una solución a un problema con una tecnología demasiado
temprana.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
9
IS05 - Capacidad de identificar, evaluar y gestionar los riesgos potenciales asociados que
pudieran presentarse.
Competencia justificada con el apartado riesgos.
IS06 - Capacidad para diseñar soluciones apropiadas en uno o más dominios de aplicación
utilizando métodos de la ingeniería del software que integren aspectos éticos, sociales, legales
y económicos.
Competencia justificada con todo lo anteriormente dicho.
4. Aportación socioeconómica Este proyecto simula el uso del protocolo Lightning en un pequeño caso de uso como es un
sorteo. Pero Lightning está directamente relacionado con Bitcoin y a su vez Bitcoin con la
Blockchain. Es innegable que hoy en día estamos viviendo una gran revolución con todo lo
relacionado con las criptomonedas, además de las diferentes aplicaciones que se les está dando
alrededor de todo el mundo a la tecnología Blockchain.
Bitcoin fue la primera moneda generada por medio de la tecnología Blockchain y hoy en día
sigue siendo la moneda referencia. Se le achacan algunos problemas como son la lentitud a la
hora de verificar los pagos y por otro lado el bajo número de transacciones que se pueden
realizar por segundo. Lightning Network llega para dar solución a estos dos problemas. Dota a
Bitcoin de inmediatez en los pagos una vez se haya abierto un canal y por otro lado permite un
altísimo número de transacciones por segundo.
En resumen, Lightning tiene un alto nivel socio económico ya que potenciará el uso de Bitcoin
como moneda para realizar micropagos en las compras del día a día.
Referencias: [8]
5. Análisis
5.1 Aplicaciones que hacen uso de Lightning Network Actualmente están apareciendo bastantes desarrollos que hacen uso del protocolo lightning
network. Podemos destacar las tres siguientes implementaciones:
• LND: Lightning Network Daemon.
• Eclair: Implementación realizada en Scala.
• C-lightning: Implementación en C++.
A continuación, se mostrarán algunos ejemplos:
• Lightning Desktop App por Lightning Labs: Se trata de una cartera de bitcoin compatible con
Lightning Network. Aplicación de escritorio multiplataforma desarrollada con LND. [19]
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
10
Ilustración 8 - Lightning Desktop App
• Y’alls: Plataforma en la que podemos leer y escribir artículos. Nos permite leer artículos
después de haber pagado una cierta cantidad. Este pago se puede realizar usando el
protocolo Lightning Network. [20]
Ilustración 9 - Y'alls
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
11
• lnd.fun: Panel para web masters que permite controlar sus nodos Lightning Network. [21]
Ilustración 10 - lnd.fund
5.2 Descripción del proyecto Como se ha comentado anteriormente, en este proyecto se desarrollará un servicio web el cual
permitirá al cliente crearse una cartera bitcoin compatible con Lightning Network y poder así,
realizar compras de boletos instantáneamente para participar en un sorteo determinado.
También el cliente podrá realizar transferencias a otras carteras Lightning haciendo uso de
canales ajenos al emisor si existiera una ruta válida.
El sistema celebrará el sorteo en el día y hora especificados y entregará el premio al ganador de
este. Los sorteos se bloquearán 15 minutos antes de su celebración para no permitir la compra
de boletos. De igual modo se bloquearán si no quedaran boletos disponibles. Por otro lado, si a
la hora de celebrarse un sorteo, ningún usuario hubiera comprado boletos para este, el sorteo
será cancelado. Acto seguido de celebrar o cancelar un sorteo, el sistema creará un sorteo del
mismo tipo para dentro de una semana a la misma hora.
El sistema ganará aproximadamente un 20% de todos los fondos destinados a comprar boletos
en cada sorteo, por lo que el 80% restante irá destinado al premio. También el sistema
recolectará todos los fondos de sus canales Lightning abiertos, el día uno de cada mes. De esta
manera el sistema recupera sus ganancias.
Como se explicará en el siguiente punto, se ha pasado de realizar el proyecto usando el
protocolo Lightning Network a una simulación de este con ayuda de una base de datos.
5.3 Problemas con Lightning Network He encontrado diversos problemas en el transcurso del desarrollo de este proyecto ya que
Lightning Network es un protocolo todavía en construcción y por ello inestable en muchos casos.
El proyecto usado para el desarrollo es Lightning Network Daemon, este es el primer daemon
de Lightning, escrito en Go y desarrollado por Lightning Labs. Para poder desarrollar en Java es
necesario escribir un proyecto en java que sea capaz de comunicarse con LND a través de gRPC.
Remote Procedure Calls o lo que es lo mismo gRPC, es un sistema creado por Google en código
abierto y capaz de realizar llamadas de procedimientos remotos. Es capaz de generar enlaces de
cliente y servidor multiplataforma en varios idiomas. Seguí la guía que se detalla en esta página
del proyecto LND [16], pero no conseguí que funcionara. Al ver que me daba problemas me puse
en contacto con los desarrolladores de LND, a través de su canal oficial en Slack [13]. Los
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
12
desarrolladores de Lightning Labs, me dijeron que estaba dando problemas puntuales en la
generación con java. Según me explicaron el problema estaba relacionado con la configuración
de los ficheros proto y que lo solucionarían en la siguiente versión. Después de esto descarté
usar LND por que no podía esperar que sacaran una nueva versión y opté por buscar alguna otra
solución.
Preguntando en Slack me dijeron que podría probar con la librería Lightningj [12]. Esta librería
facilita el uso de Lightning Network Daemon en desarrollos con el lenguaje Java. Realicé unas
pruebas para ver si podía crear carteras tanto de manera asíncrona como síncrona. Conseguí
crearlas en los dos modos, pero luego en una segunda llamada al procedimiento de creación,
para crear una segunda cartera, este fallaba. Al no conseguir solucionar el problema, me puse
en contacto con el desarrollador de esa librería, no pudiendo tampoco encontrar la solución.
Continué preguntando en Slack sobre estos errores y no conseguí ninguna respuesta que
pudiera ayudarme.
Casi todos los desarrollos que se realizan sobre Lightning Network están desarrollados en
JavaScript o Python. A causa de esto, en la red se puede encontrar información de errores
puntuales relacionados con Lightning Network Daemon para esos lenguajes, pero no para Java.
No he conseguido encontrar un proyecto realizado en Java que pudiera ayudarme de algún
modo. Por otro lado, la documentación de LND está únicamente redactada para JavaScript y
Python.
Leyendo los comentarios de otros usuarios en Slack y viendo los problemas publicados por
usuarios en el Github de LND, pude constatar que este proyecto, aun siendo uno de los más
avanzados relacionados con Lightning Network, le fatal bastante maduración, tanto en
funcionalidades como en lo que a estabilidad se refiere. En Slack son muchos los usuarios que
reportan errores como por ejemplo que existen ocasiones en las que los canales cerrados no
devuelven los fondos a sus respectivos dueños, quedándose en el sistema canales zombis con
enormes cantidades de bitcoins. Todos estos desarrollos usan la simnet o testnet para realizar
las pruebas. LND no recomienda el uso del Daemon en la mainnet por los muchos errores que
ocurren actualmente. Hay recalcar que Lightning Network Daemon está ahora mismo en su
versión 0.5.1 Beta.
Habiéndole dedicado bastante tiempo en intentar solucionar todos estos problemas y
encontrarme en un punto muerto debido a la escasa documentación que existe para el
desarrollo de Lightning Network Daemon en Java, hablé con mi tutor del TFG explicándole mi
problema. Me dijo que a partir de ese momento trabajara en una solución simulada de cómo
funciona Lightning Network Daemon con ayuda de una base de datos.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
13
5.4 Características Las características de este proyecto son:
• Web Service.
• Servicio de loterías.
• Simulación del protocolo Lightning Network.
• Permite las transferencias entre carteras Lightning.
• Desarrollo en Java.
5.5 Historias de usuario A continuación, expondré las historias de usuario del proyecto.
• Como usuario quiero crear una cartera para poder operar con ella.
• Como usuario quiero eliminar mi cartera para borrarla del sistema.
• Como usuario quiero restaurar mi cartera para recuperar mis fondos.
• Como usuario quiero desbloquear mi cartera para poder operar con ella.
• Como usuario quiero realizar transferencias a otras carteras para transferir dinero a otra
cuenta.
• Como usuario quiero comprar boletos para poder participar en sorteos.
• Como usuario quiero cerrar todos mis canales para recuperar mis fondos.
• Como usuario quiero listar todos los sorteos en los que he participado para ver mi historial
de sorteos.
• Como usuario quiero listar todos los sorteos pendientes para poder comprar boletos de esos
sorteos.
• Como usuario quiero listar todos los sorteos ya celebrados para ver el historial de sorteos.
• Como usuario quiero listar los sorteos en los que esté participando para ver la información
de dicho sorteo.
• Como usuario quiero ver las notificaciones para saber si he sido el ganador de algún sorteo.
• Como usuario quiero marcar como leída una notificación para que aparezca con otro diseño.
• Como usuario quiero poder comprobar mi saldo para saber cuánto dinero tengo en mi
cartera.
• Como usuario quiero saber cuánto dinero hay en mis canales para poder decidir si
recuperarlo.
• Como sistema quiero crear sorteos para añadirlos al listado de sorteos pendientes.
• Como sistema quiero cancelar sorteos para eliminarlos del listado de pendientes.
• Como sistema quiero celebrar sorteos para conseguir un ganador.
• Como sistema quiero pagar premios para premiar al ganador de un sorteo.
• Como sistema quiero cerrar todos mis canales para recuperar el dinero que hay en ellos.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
14
5.6 Riesgos Se han detectado los posibles siguientes riesgos:
• Desconocimiento del desarrollo con Blockchain, Lightning Network y Bitcoin: Al no tener
experiencia en desarrollo con estas tecnologías y utilizarlas por primera vez en el desarrollo
de este proyecto, se puede generar un retraso considerable. Riesgo de tipo tecnológico
(Técnico).
o Plan de contingencia: Realizar cursos relacionados con estas nuevas tecnologías.
• Cambio en la legislación: si se produjera un cambio en la legislación y se prohibiera el
Bitcoin, no podría seguir ofreciendo el servicio. Riesgo de tipo Regulatorio (Externo)
o Plan de contingencia: Se debería contratar un abogado que fuera un experto en lo
referente a lo legal en este tipo de tecnologías.
• Calidad del producto final: Al no tener experiencia en el desarrollo de proyectos orientados
a servicios web, podría suceder que la calidad de este no fuera todo lo buena como sería
deseable. Riesgo relacionado con la calidad (Técnico)
o Plan de contingencia: Asesoramiento de un profesional del sector que muestre el
camino a seguir para una correcta ejecución del desarrollo.
5.7 Licencias de software Para la realización del proyecto he utilizado los siguientes programas con licencia.
• IntelliJ IDEA Ultimate 2018.3: Licencia a mi nombre otorgada para propósitos educacionales.
• Office 365: Licencia otorgada a través de la ULPGC.
• Windows 10: Licencia otorgada a través de la ULPGC.
Los demás programas utilizados son versiones de prueba o software gratuito.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
15
6 Diseño
6.1 Diagrama de casos de uso Acorde a las historias de usuario ya comentadas en el punto 3.5, tenemos el siguiente diagrama
de casos de uso.
Ilustración 11 - Diagrama de casos de uso
6.2 Diagramas de secuencia En este punto mostraré cuales son las interacciones entre objetos en el sistema para cada una
de las funcionalidades del proyecto.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
16
6.2.1 Crear cartera
Ilustración 12 - Diagrama de secuencia - Crear cartera
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
17
6.2.2 Borrar cartera
Ilustración 13 - Diagrama de secuencia - Borrar cartera
6.2.3 Desbloquear cartera
Ilustración 14 - Diagrama de secuencia - Desbloquear cartera
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
18
6.2.4 Restaurar cartera
Ilustración 15 - Diagrama de secuencia - Restaurar cartera
6.2.5 Obtener saldo de la cartera
Ilustración 16 - Diagrama de secuencia - Obtener saldo de cartera
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
19
6.2.6 Obtener saldo de los canales
Ilustración 17 - Diagrama de secuencia - Obtener saldo de los canales
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
20
6.2.7 Comprar boleto
Ilustración 18 - Diagrama de secuencia - Comprar boleto 1
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
21
Ilustración 19 - Diagrama de secuencia - Comprar boleto 2
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
22
6.2.8 Ver notificaciones
Ilustración 20 - Diagrama de secuencia - Ver notificaciones
6.2.9 Marcar notificaciones
Ilustración 21 - Diagrama de secuencia - Marcar notificaciones
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
23
6.2.10 Transferencias entre usuarios
Ilustración 22 - Diagrama de secuencia - Transferencia entre usuarios 1
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
24
Ilustración 23 - Diagrama de secuencia - Transferencia entre usuarios 2
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
25
6.2.11 Listado de movimientos
Ilustración 24 - Diagrama de secuencia - Listado de movimientos
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
26
6.2.12 Listar todos los sorteos pendientes
Ilustración 25 – Diagrama de secuencia - Listar todos los sorteos pendientes
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
27
6.2.13 Cerrar canales para obtener su saldo
Ilustración 26 - Diagrama de secuencia - Cerrar canales para obtener su saldo
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
28
6.2.14 Crear sorteo
Ilustración 27 - Diagrama de secuencia - Crear sorteo
6.2.15 Eliminar sorteo
Ilustración 28 - Diagrama de secuencia - Eliminar sorteo
6.2.16 Comenzar sorteo
Ilustración 29 - Diagrama de secuencia - Comenzar sorteo
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
29
6.2.17 Pagar premio
Ilustración 30 - Diagrama de secuencia - Pagar premio
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
30
6.2.18 Celebrar un sorteo y generar otro del mismo tipo
Ilustración 31 - Diagrama de secuencia - Celebrar un sorteo y generar otro del mismo tipo
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
31
6.3 Diagrama de despliegue Como se puede ver en el siguiente diagrama el sistema se compone de dos servidores. Ambos
tendrán instalado el sistema operativo Ubuntu 18.10 x64 y tendrán 1GB de RAM. Urano tendrá
instalado el Apache Tomcat/ 9.0.12 y Jupiter la base de datos Mysql junto con el nodo Bitcoin y
el Lightning Network Daemon. La conexión desde el web service hasta los dispositivos móviles
se hará a través del protocolo http
Ilustración 32 - Diagrama de despliegue
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
32
6.4 Diagrama de la base de datos Como se puede ver en el diagrama se ha intentado evitar la redundancia de datos en las tablas.
Se han establecido diferentes claves foráneas para que, a la hora de eliminar carteras o sorteos,
se eliminen en cascada las tuplas relacionadas con esos datos.
Ilustración 33 - Diagrama de la base de datos
7. Desarrollo
7.1 Metodología Para el desarrollo del proyecto se ha hecho uso de metodologías ágiles. Estas metodologías son
técnicas que agilizan el desarrollo de software, favoreciendo a la toma de decisiones, donde los
requisitos y soluciones evolucionan con el tiempo según las necesidades.
La metodología elegida para el desarrollo del proyecto ha sido la iterativa. A continuación, se
muestran cuáles han sido las iteraciones realizadas en este proyecto:
1. Creación, eliminación y desbloqueo de una cartera.
2. Restauración de una cartera.
3. Creación de sorteos.
4. Compra de boletos
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
33
5. Celebración de sorteos y pago de premios.
6. Realización de transferencias.
7. Listar sorteos
8. Comprobar saldo de la cartera y de los canales. Además de cerrar todos los canales para
recuperar el saldo.
9. Programación semanal relacionada con el bloqueo, celebración y creación de sorteos.
Además de la retirada de fondos de los canales de la cartera maestra para recuperar
fondos.
7.2 Planificación inicial y final En el siguiente cuadro podemos ver cuál fue la planificación inicial del proyecto.
Ilustración 34 - Tabla de planificación
El “estudio previo / Análisis” me llevó bastante más de lo esperado ya que me encontré con los
problemas anteriormente mencionados, relacionados con Lightning Network. De 125 horas
estimadas, superé las 200 horas. Los demás apartados se han acercado bastante a sus
estimaciones. En general se puede afirmar que no acerté con la estimación ya que lo problemas
encontrados en la primera fase, hicieron que superara por mucho el tiempo establecido.
7.3 Configuración de los servidores Para el desarrollo de este proyecto he contado con dos servidores contratados a la empresa
Digitalocean. Estos servidores están hospedados en Londres. Tienen como sistema operativo el
Linux 18.10 x64 y 1GB de RAM. Opté por contratar con esta compañía ya que gracias al student
pack de Github, regalan 50$ para gastarlos en sus servicios. Además, anteriormente les he dado
uso en otros proyectos y me han respondido bastante bien.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
34
7.3.1 Configuración del servidor Urano
Este es el servidor que dará el servicio de servidor web. Para ello instalaremos en el mismo el
Apache Tomcat 9.0.12. Los pasos seguir para su instalación son los siguientes.
Comandos:
Instalar Java sudo apt update sudo apt install default-jdk
Crear el usuario Tomcat sudo groupadd tomcat sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat
Instalar Tomcat cd /tmp curl -O <dirección del paquete apache-tomcat-9.0.12.tar.gz> sudo mkdir /opt/tomcat sudo tar xzvf apache-tomcat-9*tar.gz -C /opt/tomcat --strip-components=1
Actualizar los permisos cd /opt/tomcat sudo chgrp -R tomcat /opt/tomcat sudo chmod -R g+r conf sudo chmod g+x conf sudo chown -R tomcat webapps/ work/ temp/ logs/
Editamos el fichero de configuración de Tomcat para ejecutarlo como un servicio. sudo nano /etc/systemd/system/tomcat.service
Pegamos el contenido y guardamos. [Unit] Description=Apache Tomcat Web Application Container After=network.target [Service] Type=forking Environment=JAVA_HOME=/usr/lib/jvm/java-1.11.0-openjdk-amd64 Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid Environment=CATALINA_HOME=/opt/tomcat Environment=CATALINA_BASE=/opt/tomcat Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC' Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom' ExecStart=/opt/tomcat/bin/startup.sh ExecStop=/opt/tomcat/bin/shutdown.sh User=tomcat Group=tomcat UMask=0007 RestartSec=10 Restart=always [Install] WantedBy=multi-user.target sudo systemctl daemon-reload sudo systemctl start tomcat sudo systemctl status tomcat
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
35
Ahora podemos comprobar si el servicio está correctamente instalado. http://server_domain_or_IP:8080
Configuramos el servicio para que se inicie con cada inicio del servidor. sudo systemctl enable tomcat
Configuración de la interfaz web para controlar el servidor Tomcat. Editamos el admin y la contraseña sudo nano /opt/tomcat/conf/tomcat-users.xml
<tomcat-users . . .> <user username="admin" password="password" roles="manager-gui,admin-gui"/> </tomcat-users>
Para poder acceder desde el exterior a la interfaz es necesario desactivar una
restricción que trae por defecto Tomcat en las últimas versiones. En los dos siguientes archivos comentamos la línea que restringe la conexión desde el
exterior del servidor. sudo nano /opt/tomcat/webapps/manager/META-INF/context.xml sudo nano /opt/tomcat/webapps/host-manager/META-INF/context.xml <Context antiResourceLocking="false" privileged="true" > <!--<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />--> </Context>
Ya tenemos instalado el servicio Tomcat en nuestro servidor, ahora lo único que
tenemos que hacer es acceder al mismo e incluir el fichero .war generado desde Intellij para que el servicio esté disponible para ser usado sin ningún problema.
7.3.2 Configuración del servidor Jupiter. Este servidor es el encargado de alojar la base de datos Mysql y por otro lado también es el
servidor donde se instalaron los nodos de Bitcoin y Lightning Network.
7.3.2.1 Instalación del servicio de base de datos Mysql
En la instalación de Mysql tuve que seguir los siguientes pasos. sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation
Por último, tuve que ajustar los privilegios de autenticación de usuario. sudo mysql SELECT user,authentication_string,plugin,host FROM mysql.user;
Con este comando, establecemos una contraseña para el usuario root. ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
Para que los cambios tengan efecto. FLUSH PRIVILEGES;
Salimos de mysql para autenticarnos como root. exit
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
36
mysql -u root -p
Creamos un nuevo usuario y establecemos sus permisos CREATE USER 'sammy'@'localhost' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON *.* TO 'sammy'@'localhost' WITH GRANT OPTION;
Después de esto salimos de mysql y comprobamos que el servicio funciona. exit systemctl status mysql.service
7.3.2.2 Instalación de los nodos Bitcoin y Lightning Network.
En un primer momento para realizar las pruebas con el protocolo Lightning Network, tuve que
instalar tanto el nodo Bitcoin como el de Lightning Network.
Escribí el siguiente script para automatizar la instalación y así poder hacer instalaciones limpias
rápidamente.
#!/bin/bash echo 1/7 sudo apt-get install golang-1.10-go export GOPATH=~/gocode ln -s /usr/local/bin/go /bin/go export PATH=$PATH:$GOPATH/bin echo 2/7 go get -u github.com/golang/dep/cmd/dep echo 3/7 go get -d github.com/lightningnetwork/lnd cd $GOPATH/src/github.com/lightningnetwork/lnd echo 4/7 make && make install cd $GOPATH/src/github.com/lightningnetwork/lnd echo 5/7 git pull echo 6/7 make && make install echo 7/7 make btcd ln -s $GOPATH/bin/lnd /bin/lnd ln -s $GOPATH/bin/btcd /bin/btcd
7.4. Problemas encontrados con Lightning Network Una vez entrado en materia, surgieron varios problemas a los cuales ni yo, ni los desarrolladores
del daemon utilizado (LND) supimos darle respuesta. Por todo ello se optó de acuerdo con mi
tutor, José Juan Hernández Cabrera, realizar un mock tanto de la blockchain como del protocolo
lightning network a través de una base de datos.
7.5 Explicación del código El proyecto está desarrollado en el lenguaje de programación Java y usa de la herramienta
Maven. Es muy eficaz para configurar el proyecto, así como también facilita la importación de
las librerías necesarias para su correcto funcionamiento.
Los pasos que seguí en el desarrollo se detallan en el punto Metodología.
7.5.1 pom.xml
A continuación, se muestra cual es el contenido del fichero pom.xml.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
37
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.jorgefernandez.tft</groupId> <artifactId>BlockLotto</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>BlockLotto Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties>
El proyecto se compila en la versión 1.7 del JDK.
<dependencies> <!-- TEST --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- JERSEY --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.10.19</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-json</artifactId> <version>1.19.4</version> </dependency> <dependency> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-guice</artifactId> <version>1.19.4</version> </dependency> <!-- BBDD --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</version> </dependency>
Se incluyen dependencias para ejecutar las siguientes funcionalidades:
• junit: Permite la realización de pruebas para comprobar si el código funciona
correctamente.
• jersey-guice: Permite la creación del servicio web e inyección de la interfaz.
• mysql-connector-java: Permite la comunicación entre el proyecto y la base de datos Mysql.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
38
<!-- ENCRYPT --> <dependency> <groupId>org.mindrot</groupId> <artifactId>jbcrypt</artifactId> <version>0.4</version> </dependency> <!-- GENRANDOM --> <dependency> <groupId>org.bitbucket.dollar</groupId> <artifactId>dollar</artifactId> <version>1.0-beta3</version> </dependency> <!-- QUARTZ --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.5</version> </dependency> <!-- NEEDED --> <dependency> <groupId>com.googlecode.json-simple</groupId> <artifactId>json-simple</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.11</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.4</version> </dependency> </dependencies>
• jbcrypt: Permite encriptar las contraseñas almacenadas en la base de datos.
• dollar: Permite generar cadenas aleatorias de caracteres.
• quartz: Permite programar eventos como pueden ser el momento de celebración de los
sorteos.
• En el último apartado aparecen librerías necesarias para que el proyecto no de errores o
advertencias.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
39
<build> <finalName>BlockLotto</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.0.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.20.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.0</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> </project>
7.5.2 Main.java
En la clase Main se configura el proyecto con Jersey y Guice para Inyectar la interfaz
LightningAccessor.class a MockLightningAccessor.class. A su vez hacemos uso también de
Jackson para poder utilizar la función de @Consume y @Produce que se usará para poder recibir
peticiones en nuestro servidor web pasando un Json y devolver también un Json con la
respuesta. Esto lo veremos en el apartado 5.3.3.4.
package com.jorgefernandez.tft.blocklotto.service; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Singleton; import com.google.inject.servlet.GuiceServletContextListener; import com.jorgefernandez.tft.blocklotto.resources.lightning.LightningAccessor; import com.jorgefernandez.tft.blocklotto.resources.lightning.MockLightningAccessor; import com.sun.jersey.guice.JerseyServletModule; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; import org.codehaus.jackson.jaxrs.JacksonJsonProvider; public class Main extends GuiceServletContextListener { @Override protected Injector getInjector() { return Guice.createInjector(new JerseyServletModule() { @Override protected void configureServlets() { bind(LightningAccessor.class).to(MockLightningAccessor.class); serve("/*").with(GuiceContainer.class); bind(BlockLottoService.class); bind(JacksonJsonProvider.class).in(Singleton.class); } }); } }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
40
7.5.3 Interfaz
En este apartado se muestra los métodos que forman parte de la interfaz de Lightning Network.
package com.jorgefernandez.tft.blocklotto.resources.lightning; import com.jorgefernandez.tft.blocklotto.resources.lightning.objects.*; import java.util.List; public interface LightningAccessor { Wallet createWallet(Wallet wallet); Wallet unlockWallet(Wallet wallet); boolean deleteWallet(Wallet wallet); WalletBalance getWalletBalance(String codWallet); boolean createPeer(String nodeA, String nodeB); boolean deletePeer(String nodeA, String nodeB); Channel openChannel(String walletA, String walletB, long amountA, long amountB); void closeChannel(String channelPoint); Invoice createInvoice(String codWallet, long amount); String sendPaymentWithPayReq(String payReq, String codWallet, String concept); Wallet restoreWallet(Wallet wallet); WalletBalance withdrawFunds(String walletID); List<PaymentMoves> getAllPaymentMovesByWallet(Wallet wallet); BalanceInChannels getTotalBalanceInChannels(Wallet wallet); Message transferCoin(String id, String id1, long amount); long calculateChannelCapacity(String codWallet, long amount); List<ChannelRoute> getPaymentRouteChannel(String codWalletInitial, String codWalletFinal, long amount); }
7.5.4 Fichero de configuración
En el fichero de configuración guardamos tanto el nombre de la cartera que hará las funciones
de venta de boletos y pago de premios, como también se establece la ruta donde está ubicado
el fichero donde están almacenadas las palabras para la generación de la frase de restauración
de la cartera. Para tener acceso a estas variables desde cualquier parte del proyecto, se usa el
patrón Singleton.
package com.jorgefernandez.tft.blocklotto.resources.tools; import java.util.HashMap; import java.util.Map; public class Configuration { private static Configuration single_instance = null; public String masterWallet = "master"; //Dirección del archivo words.txt public String path = "C:\\Users\\Jorge\\Documents\\GitHub\\BlockLotto\\"; //Windows //public String path = "/opt/tomcat/webapps/"; //Linux private Configuration() { } public static Configuration getInstance() { if (single_instance == null) single_instance = new Configuration(); return single_instance; } }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
41
7.5.5 BlockLottoService
Se puede observar cómo se inyecta la interfaz en la clase.
package com.jorgefernandez.tft.blocklotto.service; import com.google.inject.Inject; import com.google.inject.Singleton; import com.jorgefernandez.tft.blocklotto.resources.bbdd.MysqlDB; import com.jorgefernandez.tft.blocklotto.resources.lightning.LightningAccessor; import com.jorgefernandez.tft.blocklotto.resources.lightning.objects.*; import com.jorgefernandez.tft.blocklotto.resources.raffles.ListRaffles; import com.jorgefernandez.tft.blocklotto.resources.raffles.ManageRaffles; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import java.util.ArrayList; import java.util.List; @Singleton @Path("/") public class BlockLottoService { private LightningAccessor lightningAccessor; @Inject public BlockLottoService(LightningAccessor lightningAccessor) { this.lightningAccessor = lightningAccessor; }
//New Wallet @POST @Path("/nw") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public Wallet newWallet(Wallet wallet) { return lightningAccessor.createWallet(wallet); } //Wallet Balance @POST @Path("/wb") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public WalletBalance walletBalance(Wallet wallet) { return lightningAccessor.getWalletBalance(wallet.getId()); } //Buy Raffle @POST @Path("/br") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public BuyRaffle buyRaffle(BuyRaffle buyRaffle) { return new ManageRaffles().buyTickets(buyRaffle); }
Como muestra, se muestran solo tres métodos de ejemplo. Se puede observar cómo se utiliza
el método POST para la comunicación con los mismos, su dirección y el tipo de fichero de
entrada y salida. En este caso se usa Json como fichero de intercambio de datos.
A continuación, se explicará el código necesario para la ejecución de cada método.
7.5.5.1 Crear cartera
Con este método creamos la cartera necesaria para poder operar con el sistema. El usuario tiene
que pasar un Json con un campo “pass” de al menos 8 caracteres.
//New Wallet @POST @Path("/nw") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public Wallet newWallet(Wallet wallet) { return lightningAccessor.createWallet(wallet); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
42
Se encripta la contraseña con la librería BCrypt y se generan las cadenas de caracteres aleatorias
de id, pub y address. La generación es aleatoria ya que este proyecto es una simulación. Se
realiza una comprobación en la base de datos de si la cadena generada ya existe en la misma.
En caso de existir, se vuelve a generar una nueva cadena.
Después de esto se genera un listado aleatorio de 24 palabras las cuales son extraídas del fichero
words.txt. Una vez generadas, obtenemos el hash de la cadena y eso es lo que encriptamos e
introducimos en la base de datos. Se obtiene el hash de la cadena de 24 palabras, ya que la
librería Bcrypt únicamente encripta los primeros 64 caracteres y en muchas ocasiones la string,
supera las 120.
@Override public Wallet createWallet(Wallet wallet) { if (wallet.getPass().length() >= 8) { float initAmount = 1f; String pass = BCrypt.hashpw(wallet.getPass(), BCrypt.gensalt()); String id = initLNDNode(nextFreePort()); String pub = createPUBString(66); String address = createAddressString(34); List<String> words = generateRandomWords(); String encryptedWords = BCrypt.hashpw(wordsToString(words), BCrypt.gensalt()); new MysqlDB().addWallet(id, pub, address, initAmount, pass, encryptedWords); wallet.setId(id); wallet.setPub(pub); wallet.setAddress(address); wallet.setSeedWords(words); wallet.setAmount(initAmount); wallet.setUnlock(true); } return wallet; }
Por último, se inserta la información de la cartera en la base de datos y se rellena la información
en el objeto wallet. Este objeto es devuelto al usuario en formato Json.
public void addWallet(String walletID, String pub, String address, float amount, String pass, String backupWords) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO wallets(id,pub,address,amount,pass,backupWords,initialAmount) " + "VALUES (?,?,?,?,?,?,?)"); p.setString(1, walletID); p.setString(2, pub); p.setString(3, address); p.setFloat(4, amount); p.setString(5, pass); p.setString(6, backupWords); p.setFloat(7, amount); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
7.5.5.2 Restaurar cartera
Permite recuperar una cartera siempre y cuando tengamos las 24 palabras generadas en el
momento de la creación de esta. El usuario tiene que pasar un Json con un campo “pass” de al
menos 8 caracteres y otro campo “seedWords” con un listado de 24 palabras.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
43
//Restore Wallet @POST @Path("/rw") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public Wallet restoreWallet(Wallet wallet) { return lightningAccessor.restoreWallet(wallet); }
Comprobamos en todas las carteras alojadas en el sistema, si alguna coincide en las 24 palabras
de generación que ha introducido el usuario.
public Wallet restoreWallet(Wallet wallet) { MysqlDB mysqlDB = new MysqlDB(); ResultSet rs = mysqlDB.getAllWallets(); try { while (rs.next()) { if (BCrypt.checkpw(wordsToString(wallet.getSeedWords()), rs.getString("backupWords"))) { wallet.setId(rs.getString("id")); wallet.setPub(rs.getString("pub")); wallet.setAddress(rs.getString("address")); wallet.setAmount(rs.getFloat("amount")); wallet.setUnlock(true); mysqlDB.updateWalletPass(rs.getString("id"), BCrypt.hashpw(wallet.getPass(), BCrypt.gensalt())); break; } } } catch (SQLException e) { e.printStackTrace(); } finally { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } return wallet; }
public ResultSet getAllWallets() { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.wallets.* " + "FROM blocklotto.wallets"); return p.executeQuery(); } } catch (SQLException e) { e.printStackTrace(); } return null; }
Pasamos las palabras suministradas, a una string de palabras separadas por espacios y
generamos el hash de esa cadena de caracteres.
public static String wordsToString(List<String> words) { StringBuilder s = new StringBuilder(); for (String temp : words) { s.append(temp).append(" "); } return getHashText(s.toString().trim()); }
public static String getHashText(String s) { return org.apache.commons.codec.digest.DigestUtils.sha256Hex(s); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
44
public void updateWalletPass(String walletID, String pass) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.wallets SET pass=? " + "WHERE blocklotto.wallets.id=?"); p.setString(1, pass); p.setString(2, walletID); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
7.5.5.3 Desbloquear cartera
Permite desbloquear una cartera después de que el nodo Lightning se haya reiniciado. El usuario
tiene que pasar un Json con un campo “pass” de al menos 8 caracteres y otro campo “id” con el
identificador de su cartera.
//Unlock Wallet @POST @Path("/uw") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public Wallet unlockWallet(Wallet wallet) { return lightningAccessor.unlockWallet(wallet); }
Desbloqueamos la cartera si la contraseña suministrada coincide con la que está almacenada en
él sistema.
@Override public Wallet unlockWallet(Wallet wallet) { ResultSet rs = new MysqlDB().getWalletByWalletID(wallet.getId()); try { if (rs.next()) { if (BCrypt.checkpw(wallet.getPass(), rs.getString("pass"))) { wallet.setPub(rs.getString("pub")); wallet.setAddress(rs.getString("address")); wallet.setUnlock(true); } } } catch (SQLException e) { e.printStackTrace(); }finally{ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } return wallet; }
public ResultSet getWalletByWalletID(String walletID) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.wallets.* " + "FROM blocklotto.wallets " + "WHERE blocklotto.wallets.id=?"); p.setString(1, walletID); return p.executeQuery(); } } catch (SQLException e) { e.printStackTrace(); } return null; }
7.5.5.4 Consultar saldo de la cartera
Permite la consulta de saldo de la cartera. El usuario tiene que pasar un Json con un campo “id”
con la id de su cartera.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
45
//Wallet Balance @POST @Path("/wb") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public WalletBalance walletBalance(Wallet wallet) { return lightningAccessor.getWalletBalance(wallet.getId()); }
Se consulta la información del wallet a través del método “getWalletByWalletID” y se almacena
en el objeto walletBalance que es devuelto al usuario.
@Override public WalletBalance getWalletBalance(String codWallet) { WalletBalance walletBalance = new WalletBalance(); MysqlDB mysqlDB = new MysqlDB(); ResultSet rs = mysqlDB.getWalletByWalletID(codWallet); try { while (rs.next()) { walletBalance.setTotal_balance(rs.getDouble("amount")); walletBalance.setConfirmed_balance(rs.getDouble("amount")); walletBalance.setUnconfirmed_balance(0); } } catch (SQLException e) { e.printStackTrace(); } return walletBalance; }
7.5.5.5 Comprar boletos
Permite al usuario la compra de boletos para un sorteo específico. El usuario tiene que pasar un
Json con un campo “id” con el identificador de su cartera, un campo “codRaffle” con el
identificador del sorteo y por último un campo “number” con el número de boletos que quiere
comprar.
//Buy Raffle @POST @Path("/br") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public BuyRaffle buyRaffle(BuyRaffle buyRaffle) { return new ManageRaffles().buyTickets(buyRaffle); }
public BuyRaffle buyTickets(BuyRaffle buyRaffle) { MysqlDB mysqlDB = new MysqlDB(); MockLightningAccessor mockLightning = new MockLightningAccessor(); buyRaffle.setMessage("Error"); if (mysqlDB.checkRaffleisBlocked(buyRaffle.getCodRaffle())) return buyRaffle; long l = mysqlDB.getPriceByBuyedTickets(buyRaffle.getCodRaffle(), buyRaffle.getNumber()); if (mysqlDB.getRemainingTickets(buyRaffle.getCodRaffle()) >= buyRaffle.getNumber()) { Invoice invoice = mockLightning.createInvoice(masterWallet, l); if (!mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), buyRaffle.getId(), "Compra de boletos").isEmpty()) { for (int i = 0; i < buyRaffle.getNumber(); i++) { mysqlDB.addBuyedTickets(buyRaffle.getCodRaffle(), createRaffleNumberString(5), buyRaffle.getId()); } updateRafflePrize(buyRaffle.getCodRaffle(), l); buyRaffle.setMessage("Transaction completed"); buyRaffle.setTickets(mysqlDB.getTicketsNumbersByRaffleAndWallet(buyRaffle.getCodRaffle(), buyRaffle.getId())); } } return buyRaffle; }
En primer lugar, se comprueba si el sorteo está bloqueado.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
46
public boolean checkRaffleisBlocked(int codRaffle) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.raffles.* " + "FROM blocklotto.raffles " + "WHERE blocklotto.raffles.id=? AND blocklotto.raffles.blocked=0"); p.setInt(1, codRaffle); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { return false; } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return true; }
Se obtiene el importe que hay que pagar para comprar los boletos de ese tipo de sorteo.
public int getPriceByBuyedTickets(int codRaffle, int number) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.raffleTypes.price " + "FROM blocklotto.raffleTypes " + "INNER JOIN blocklotto.raffles " + "WHERE blocklotto.raffleTypes.id = blocklotto.raffles.type " + "AND blocklotto.raffles.id =?"); p.setInt(1, codRaffle); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { return rs.getInt("price") * number; } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return 0; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
47
Comprobamos la disponibilidad del número de boletos que se quieren comprar.
public int getRemainingTickets(int idRaffle) { Connection connection = connection(); PreparedStatement p = null; ResultSet rs = null; try { if (connection != null) { p = connection.prepareStatement("SELECT COUNT(blocklotto.raffleNumbers.number) AS nTickets, blocklotto.raffleTypes.maxTickets " + "FROM blocklotto.raffleNumbers " + "INNER JOIN blocklotto.raffleTypes " + "INNER JOIN blocklotto.raffles " + "WHERE blocklotto.raffleNumbers.codRaffle = blocklotto.raffles.id " + "AND blocklotto.raffles.type = blocklotto.raffleTypes.id " + "AND blocklotto.raffles.id=?"); p.setInt(1, idRaffle); p.executeQuery(); rs = p.getResultSet(); if (rs.next()) { return rs.getInt("maxTickets") - rs.getInt("nTickets"); } } } catch (SQLException e) { e.printStackTrace(); } finally { try { rs.close(); p.close(); connection.close(); } catch (SQLException e) { e.printStackTrace(); } } return 0; }
Se crea la petición por parte de la cartera maestra del importe que cuestan los boletos.
@Override public Invoice createInvoice(String codWallet, long amount) { Invoice invoice = new Invoice(); if (codWallet != null) { String payReq = createPayReqString(189); int i = new MysqlDB().getCountPaymentsRequestByCodWallet(codWallet) + 1; if (new MysqlDB().addPaymentRequest(codWallet, payReq, amount, 0, i) > 0) { invoice.setPay_req(payReq); invoice.setAdd_index(i); } } return invoice; }
Lightning Network lleva el control de la cantidad de requerimientos de pago que efectúa una
misma cartera. Se obtiene el total de requerimientos de pago que ha efectuado la cartera master
para así posteriormente cuando se cree el nuevo requerimiento, añadamos uno al contador.
public int getCountPaymentsRequestByCodWallet(String codWallet) { int maxIndex = 0; try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT MAX(blocklotto.paymentsRequest.addIndex) AS maxIndex " + "FROM blocklotto.paymentsRequest " + "WHERE blocklotto.paymentsRequest.codWalletRequest=?"); p.setString(1, codWallet); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { maxIndex = rs.getInt("maxIndex"); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return maxIndex; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
48
public int addPaymentRequest(String codWallet, String payRequest, long amount, int payed, int addIndex) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO " + "blocklotto.paymentsRequest(payReq, codWalletRequest, amount, payed, addIndex) " + "VALUES(?,?,?,?,?)", Statement.RETURN_GENERATED_KEYS); p.setString(1, payRequest); p.setString(2, codWallet); p.setLong(3, amount); p.setInt(4, payed); p.setInt(5, addIndex); int i = p.executeUpdate(); p.close(); connection.close(); return i; } } catch (SQLException e) { e.printStackTrace(); } return -1; }
Una vez creado el requerimiento, ya se puede realizar el pago.
@Override public String sendPaymentWithPayReq(String payReq, String walletA, String concept) { MysqlDB mysqlDB = new MysqlDB(); String walletB = mysqlDB.getWalletByPayReq(payReq); long amount = mysqlDB.getAmountByPayReq(payReq); List<ChannelRoute> channelRoutes = new ArrayList<>(); if (!walletA.equals(masterWallet)) channelRoutes = getPaymentRouteChannel(walletA, walletB, amount); String fundingTxID = ""; if (channelRoutes.size() > 0) { for (ChannelRoute channelRoute : channelRoutes) { mysqlDB.updateChannelBalance(channelRoute.getFundingTxID(), amount, channelRoute.isReverse()); fundingTxID = channelRoute.getFundingTxID(); } } else { long l = calculateChannelCapacity(walletA, amount); if (l != 0) { createPeer(walletA, walletB); if (walletA.equals(masterWallet)) { fundingTxID = openChannel(walletA, walletB, Math.round(amount * 1.01f), amount).getFunding_txid(); } else { fundingTxID = openChannel(walletA, walletB, l, amount).getFunding_txid(); } } else { return ""; } } mysqlDB.addPaymentsSend(payReq, walletA); mysqlDB.addPaymentMovement(walletA, walletB, amount, getCurrentTime(), concept); mysqlDB.setPayedInvoice(payReq); return fundingTxID; }
Se obtiene el identificador de la cartera que requirió el pago. En este caso se trata de la cartera
master.
public String getWalletByPayReq(String payReq) { String wallet = ""; try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.paymentsRequest.codWalletRequest " + "FROM blocklotto.paymentsRequest " + "WHERE blocklotto.paymentsRequest.payReq=?"); p.setString(1, payReq); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { wallet = rs.getString("codWalletRequest"); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return wallet; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
49
Se obtiene el importe a pagar del requerimiento de pago.
public long getAmountByPayReq(String payReq) { long amount = 0; try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.paymentsRequest.amount " + "FROM blocklotto.paymentsRequest " + "WHERE blocklotto.paymentsRequest.payReq=?"); p.setString(1, payReq); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { amount = rs.getLong("amount"); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return amount; }
Se calcula la ruta de canales más corta para realizar el pago. Para ello se utiliza un método
recursivo. En caso de no encontrar una ruta válida, se creará un nuevo canal directo entre el
usuario y el receptor del pago, en este caso el master.
public List<ChannelRoute> getPaymentRouteChannel(String codWalletInitial, String codWalletFinal, long amount) { List<ChannelRoute> channelRoutes = new ArrayList<>(); List<ChannelRoute> channelRoutes2 = new ArrayList<>(); List<ChannelRoute> channelRoutes3 = new ArrayList<>(); List<ChannelRoute> channelRoutes4 = new ArrayList<>(); if (new MysqlDB().getChannelRoute(codWalletInitial, codWalletFinal, amount, channelRoutes, new ArrayList<>(), "ORDER BY blocklotto.channels.codWalletRemote ASC").size() > 1) if (new MysqlDB().getChannelRoute(codWalletInitial, codWalletFinal, amount, channelRoutes2, new ArrayList<>(), "ORDER BY blocklotto.channels.codWalletLocal DESC").size() > 1) if (new MysqlDB().getChannelRoute(codWalletInitial, codWalletFinal, amount, channelRoutes3, new ArrayList<>(), "ORDER BY blocklotto.channels.codWalletLocal ASC").size() > 1) new MysqlDB().getChannelRoute(codWalletInitial, codWalletFinal, amount, channelRoutes4, new ArrayList<>(), "ORDER BY blocklotto.channels.codWalletRemote DESC"); if (channelRoutes2.size() <= channelRoutes.size() && channelRoutes2.size() != 0) channelRoutes = channelRoutes2; if (channelRoutes3.size() <= channelRoutes.size() && channelRoutes3.size() != 0) channelRoutes = channelRoutes3; if (channelRoutes4.size() <= channelRoutes.size() && channelRoutes4.size() != 0) channelRoutes = channelRoutes4; return channelRoutes; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
50
El método recursivo parece tan complicado, ya que los canales son bidireccionales. Se
comprueba si la walletIni es local o remota en cada canal. Esto es importante ya que la walletIni
de ser la cartera local se tiene que comprobar el saldo local y si fuera la cartera remota, se
comprobaría el saldo remoto.
public List<ChannelRoute> getChannelRoute(String walletIni, String walletFin, long amount, List<ChannelRoute> channelRoutes, List<String> fundingList, String order) { if (!walletIni.equals(walletFin)) { Connection connection = connection(); PreparedStatement p = null; ResultSet rs = null; try { if (connection != null) { p = connection.prepareStatement("SELECT blocklotto.channels.* FROM blocklotto.channels WHERE blocklotto.channels.active=1 AND (blocklotto.channels.codWalletLocal=? OR blocklotto.channels.codWalletRemote=?) " + order); p.setString(1, walletIni); p.setString(2, walletIni); rs = p.executeQuery(); while (rs.next()) { if (!fundingList.contains(rs.getString("fundingTxID"))) { fundingList.add(rs.getString("fundingTxID")); if (walletIni.equals(rs.getString("codWalletLocal"))) { if (rs.getLong("balanceLocal") >= amount) { if (rs.getString("codWalletRemote").equals(walletFin)) { channelRoutes.add(new ChannelRoute(rs.getString("codWalletLocal"), rs.getString("codWalletRemote"), rs.getString("fundingTxID"), false)); return channelRoutes; } else { if (!channelRoutes.contains(new ChannelRoute(rs.getString("codWalletLocal"), rs.getString("codWalletRemote"), rs.getString("fundingTxID"), false))) { channelRoutes.add(new ChannelRoute(rs.getString("codWalletLocal"), rs.getString("codWalletRemote"), rs.getString("fundingTxID"), false)); } channelRoutes = getChannelRoute(rs.getString("codWalletRemote"), walletFin, amount, channelRoutes, fundingList, order); if (channelRoutes.size() > 0) { if (channelRoutes.get(channelRoutes.size() - 1).getWalletFinal().equals(walletFin)) { return channelRoutes; } } } } } else { if (rs.getLong("balanceRemote") >= amount) { if (rs.getString("codWalletLocal").equals(walletFin)) { channelRoutes.add(new ChannelRoute(rs.getString("codWalletRemote"), rs.getString("codWalletLocal"), rs.getString("fundingTxID"), true)); return channelRoutes; } else { if (!channelRoutes.contains(new ChannelRoute(rs.getString("codWalletRemote"), rs.getString("codWalletLocal"), rs.getString("fundingTxID"), true))) { channelRoutes.add(new ChannelRoute(rs.getString("codWalletRemote"), rs.getString("codWalletLocal"), rs.getString("fundingTxID"), true)); } channelRoutes = getChannelRoute(rs.getString("codWalletLocal"), walletFin, amount, channelRoutes, fundingList, order); if (channelRoutes.size() > 0) { if (channelRoutes.get(channelRoutes.size() - 1).getWalletFinal().equals(walletFin)) { return channelRoutes; } } } } } } } if (channelRoutes.size() > 0) { channelRoutes.remove(channelRoutes.size() - 1); return channelRoutes; } } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null) { rs.close(); } if (p != null) { p.close(); } if (connection != null) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } } return channelRoutes; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
51
Se actualizan los saldos internos de todos los canales implicados en la transacción.
public void updateChannelBalance(String fundingTxID, long amount, boolean reverse) { try { ResultSet rs = getChannelByfundingTxID(fundingTxID); Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.channels SET " + "blocklotto.channels.balanceLocal=?," + "blocklotto.channels.balanceRemote=?," + "blocklotto.channels.numUpdates=? " + "WHERE blocklotto.channels.fundingTxID=?"); if (rs != null) while (rs.next()) { if (reverse) { p.setLong(1, rs.getLong("balanceLocal") + amount); p.setLong(2, rs.getLong("balanceRemote") - amount); } else { p.setLong(1, rs.getLong("balanceLocal") - amount); p.setLong(2, rs.getLong("balanceRemote") + amount); } p.setInt(3, (rs.getInt("numUpdates") + 2)); p.setString(4, fundingTxID); p.execute(); } p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
private ResultSet getChannelByfundingTxID(String fundingTxID) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.channels.* " + "FROM blocklotto.channels " + "WHERE blocklotto.channels.fundingTxID=?"); p.setString(1, fundingTxID); return p.executeQuery(); } } catch (SQLException e) { e.printStackTrace(); } return null; }
Se calcula la capacidad que debe tener el canal. Para ello se tiene en cuenta que la creación de
un canal viene siempre ligada al pago de una comisión de 9050 satoshis. Por otro lado, también
hay que tener en cuenta que, al realizar el pago, se debe quedar en la parte del canal del que
paga al menos un uno por ciento de la capacidad del canal. Lightning Network Daemon actúa de
esta manera.
Se ha estipulado para un mayor aprovechamiento del protocolo Lightning, que cuando vayamos
a comprar boletos y o bien no tengamos suficiente saldo en nuestros canales o bien sea la
primera vez que compremos y el importe no supere los 2.000.000 de satoshis, el canal tenga esa
capacidad de satoshis. Así en el futuro, el pago, en caso de suficiente saldo, se realizará a través
de ese mismo canal. En esto se basa el pago a través de Lightning. Sería bueno que siempre
hubiera un canal con suficiente saldo hacia el receptor, para poder realizar el pago
instantáneamente.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
52
public long calculateChannelCapacity(String codWallet, long amount) { long l = convertBTCtoSatoshis(getWalletBalance(codWallet).getTotal_balance()); amount = Math.round((amount * 1.01f) + 9050); if (l >= amount) { if (amount >= 2000000L) { return amount; } else { if (l < 2000000L) { return amount; } else { return 2000000L; } } } return 0L; }
public static long convertBTCtoSatoshis(Double d) { return Math.round(d * 100000000); }
Para poder realizar un pago es necesario que exista una conexión entre los dos nodos. En caso
de no existir la conexión, esta se crea.
@Override public boolean createPeer(String nodeA, String nodeB) { if (!nodeA.equals(nodeB)) return new MysqlDB().addPeer(nodeA, nodeB); return false; }
public boolean addPeer(String nodeA, String nodeB) { if (!checkPeer(nodeA, nodeB)) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO peers(codWalletA,codWalletB) VALUES (?,?)"); p.setString(1, nodeA); p.setString(2, nodeB); p.execute(); p.close(); connection.close(); } return true; } catch (SQLException e) { e.printStackTrace(); } } return false; }
public boolean checkPeer(String nodeA, String nodeB) { Connection connection = connection(); try { if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT * FROM blocklotto.peers " + "WHERE (blocklotto.peers.codWalletA=? AND blocklotto.peers.codWalletB=?) " + "OR (blocklotto.peers.codWalletA=? AND blocklotto.peers.codWalletB=?)"); p.setString(1, nodeA); p.setString(2, nodeB); p.setString(3, nodeB); p.setString(4, nodeA); boolean b = checkIfExist(p); p.close(); connection.close(); return b; } } catch (SQLException e) { e.printStackTrace(); } return false; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
53
private boolean checkIfExist(PreparedStatement p) { try { p.execute(); ResultSet rs = p.getResultSet(); if (rs.next()) { return true; } rs.close(); p.close(); } catch (SQLException e) { e.printStackTrace(); } return false; }
En caso de no haber encontrado una ruta ya existente válida para la transferencia, es el
momento de abrir el canal y añadir los fondos al mismo
@Override public Channel openChannel(String walletLocal, String walletRemote, long balanceLocal, long balanceRemote) { MysqlDB mysqlDB = new MysqlDB(); Channel openChannel = new Channel(); if (mysqlDB.checkPeer(walletLocal, walletRemote)) { String fundingTxID = createFundingTxIDString(64); openChannel.setFunding_txid(fundingTxID); if (mysqlDB.addChannel(fundingTxID, walletLocal, walletRemote, balanceLocal, balanceRemote)) mysqlDB.updateAmountWallet(walletLocal, balanceLocal, false); return openChannel; } else { openChannel.setErrorMessage("Error: Must exist a connection between nodes."); } return openChannel; }
public boolean addChannel(String fundingTxID, String localWallet, String remoteWallet, long balanceLocal, long balanceRemote) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO " + "blocklotto.channels(fundingTxID,active,codWalletLocal,codWalletRemote,capacity,balanceLocal,balanceRemote,numUpdates) " + "VALUES(?,?,?,?,?,?,?,?)"); p.setString(1, fundingTxID); p.setBoolean(2, true); p.setString(3, localWallet); p.setString(4, remoteWallet); p.setLong(5, balanceLocal); p.setLong(6, (balanceLocal - balanceRemote - 9050)); p.setLong(7, balanceRemote); p.setLong(8, (balanceRemote != 0) ? 2 : 0); p.execute(); p.close(); connection.close(); return true; } } catch (SQLException e) { e.printStackTrace(); } return false; }
Se actualiza el saldo de la cartera
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
54
public void updateAmountWallet(String walletID, long f, boolean add) { try { double i = add ? getAmountByWalletID(walletID) + convertSatoshistoBTC(f) : getAmountByWalletID(walletID) - convertSatoshistoBTC(f); Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.wallets " + "SET " + " blocklotto.wallets.amount =? " + "WHERE " + " blocklotto.wallets.id =?"); p.setDouble(1, i); p.setString(2, walletID); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
public double getAmountByWalletID(String walletID) { double amount = 0d; try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.wallets.amount " + "FROM blocklotto.wallets " + "WHERE blocklotto.wallets.id=?"); p.setString(1, walletID); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { amount = rs.getDouble("amount"); } p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return amount; }
public static double convertSatoshistoBTC(long f) { return (double) f / 100000000; }
Se registra en el sistema el pago enviado a la cartera master.
public void addPaymentsSend(String payReq, String codWallet) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO blocklotto.paymentsSend(codPayReq,codWallet) VALUES(?,?)"); p.setString(1, payReq); p.setString(2, codWallet); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
Se registra en el sistema el movimiento.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
55
public void addPaymentMovement(String fromWallet, String toWallet, long amount, String date, String concept) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO " + "blocklotto.paymentMoves(fromWallet, toWallet, amount, date,concept) " + "VALUES(?,?,?,?,?)"); p.setString(1, fromWallet); p.setString(2, toWallet); p.setFloat(3, amount); p.setString(4, date); p.setString(5, concept); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
Se marca como pagado el requerimiento de pago.
public void setPayedInvoice(String payReq) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.paymentsRequest " + "SET blocklotto.paymentsRequest.payed=1 " + "WHERE blocklotto.paymentsRequest.payReq=?"); p.setString(1, payReq); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
Se registra en el sistema la compra de los boletos.
public void addBuyedTickets(int idRaffle, String number, String codWallet) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO " + "blocklotto.raffleNumbers(codRaffle,number,codWallet) " + "VALUES(?,?,?)"); p.setInt(1, idRaffle); p.setString(2, number); p.setString(3, codWallet); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
Una vez comprados los boletos, se actualiza el premio del sorteo. Este va cambiando de forma
dinámica conforme se vayan comprando boletos. De esta manera la empresa no pierde dinero
ya que no preestablece un premio desde el principio, con el peligro que conlleva solo se compren
algunos boletos. En la actualidad cuando se crea un sorteo, se estipula un premio inicial de cinco
veces el precio de un boleto. De esta manera se anima a la compra desde el principio.
private void updateRafflePrize(int idRaffle, long prize) { MysqlDB mysqlDB = new MysqlDB(); long l = mysqlDB.getPrizeRaffle(idRaffle) + Math.round(prize * 0.8); new MysqlDB().updateRafflePrize(idRaffle, l); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
56
public long getPrizeRaffle(int idRaffle) { long prize = 0L; try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.raffles.award " + "FROM blocklotto.raffles " + "WHERE blocklotto.raffles.id=?"); p.setInt(1, idRaffle); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { prize = rs.getLong("award"); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return prize; }
public void updateRafflePrize(int idRaffle, long prize) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.raffles " + "SET blocklotto.raffles.award=? " + "WHERE blocklotto.raffles.id=?"); p.setLong(1, prize); p.setInt(2, idRaffle); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
public List<String> getTicketsNumbersByRaffleAndWallet(int idRaffle, String codWallet) { List<String> ticketsList = new ArrayList<>(); try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.raffleNumbers.number " + "FROM blocklotto.raffleNumbers " + "WHERE blocklotto.raffleNumbers.codRaffle =? " + "AND blocklotto.raffleNumbers.codWallet =?"); p.setInt(1, idRaffle); p.setString(2, codWallet); p.executeQuery(); ResultSet rs = p.getResultSet(); while (rs.next()) { ticketsList.add(rs.getString("number")); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return ticketsList; }
7.5.5.6 Consultar saldo en los canales abiertos.
Permite al usuario consultar el saldo en su parte del canal de todos los canales abiertos. El
usuario tiene que pasar un Json con un campo “id” con el identificador de su cartera. Al cerrar
esos canales, el usuario recuperará todo ese saldo y se sumará a lo que tenga en su wallet.
//Total Balance in Channels @POST @Path("/tbc") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public BalanceInChannels totalBalanceInChannels(Wallet wallet) { return lightningAccessor.getTotalBalanceInChannels(wallet); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
57
Se comprueba si el identificador de la cartera está en la parte local o remota del canal. De ser
así se suma el saldo de dicha parte.
public BalanceInChannels getTotalBalanceInChannels(Wallet wallet) { MysqlDB mysqlDB = new MysqlDB(); ResultSet rs = mysqlDB.getChannelsByCodWallet(wallet.getId()); BalanceInChannels balanceInChannels = new BalanceInChannels(); int amount = 0; try { while (rs.next()) { if (wallet.getId().equals(rs.getString("codWalletLocal"))) { amount += rs.getInt("balanceLocal"); } else { amount += rs.getInt("balanceRemote"); } } } catch (SQLException e) { e.printStackTrace(); } balanceInChannels.setBalance(amount); return balanceInChannels; }
public ResultSet getChannelsByCodWallet(String codWallet) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.channels.* " + "FROM blocklotto.channels " + "WHERE (blocklotto.channels.codWalletLocal=? OR blocklotto.channels.codWalletRemote=?) " + "AND blocklotto.channels.active=1"); p.setString(1, codWallet); p.setString(2, codWallet); return p.executeQuery(); } } catch (SQLException e) { e.printStackTrace(); } return null; }
7.5.5.7 Recuperar saldo de los canales abiertos.
Permite al usuario recuperar todos sus fondos de sus canales. El usuario tiene que pasar un Json
con un campo “id” con el identificador de su cartera.
@Override public WalletBalance withdrawFunds(String walletID) { MysqlDB mysqlDB = new MysqlDB(); ResultSet rs = mysqlDB.getChannelsByCodWallet(walletID); try { while (rs.next()) { closeChannel(rs.getString("fundingTxID")); } } catch (SQLException e) { e.printStackTrace(); } return getWalletBalance(walletID); }
@Override public void closeChannel(String channelPoint) { MysqlDB mysqlDB = new MysqlDB(); mysqlDB.setChannelDeactive(channelPoint); mysqlDB.returnBalanceChannelToWallets(channelPoint); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
58
Se cierra el canal con lo que ya no está operativo.
public void setChannelDeactive(String channelPoint) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.channels " + "SET blocklotto.channels.active=0 " + "WHERE blocklotto.channels.fundingTxID=?"); p.setString(1, channelPoint); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
Se actualizan los saldos de las carteras incluidas en el canal.
public void returnBalanceChannelToWallets(String channelPoint) { ResultSet rs = getChannelByfundingTxID(channelPoint); try { if (rs != null) { while (rs.next()) { updateAmountWallet(rs.getString("codWalletLocal"), (rs.getLong("balanceLocal")), true); updateAmountWallet(rs.getString("codWalletRemote"), (rs.getLong("balanceRemote")), true); } rs.close(); } } catch (SQLException e) { e.printStackTrace(); } }
7.5.5.8 Historial de movimientos
Permite al usuario ver el historial de movimientos. El usuario tiene que pasar un Json con un
campo “id” con el identificador de su cartera.
//Payment moves @POST @Path("/pm") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public List<PaymentMoves> paymentMoves(Wallet wallet) { return lightningAccessor.getAllPaymentMovesByWallet(wallet); }
public List<PaymentMoves> getAllPaymentMovesByWallet(Wallet wallet) { List<PaymentMoves> paymentMoves = new ArrayList<>(); MysqlDB mysqlDB = new MysqlDB(); ResultSet rs = mysqlDB.getPaymentsMovesByWallet(wallet.getId()); long historicalBalance = 0L; boolean firstTimeInCalcHistoricalBalance = true; try { while (rs.next()) { PaymentMoves payment = new PaymentMoves(); historicalBalance = calcHistoricalBalance(wallet, rs, historicalBalance, firstTimeInCalcHistoricalBalance); firstTimeInCalcHistoricalBalance = false; setPaymentInformation(wallet, paymentMoves, rs, historicalBalance, payment); } } catch ( SQLException e1) { e1.printStackTrace(); } Collections.reverse(paymentMoves); return paymentMoves; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
59
Se obtienen todos los movimientos de una cartera y se calcula el saldo histórico en cada uno de
esos movimientos. Al final se devuelve al usuario un listado de movimientos de pago.
public ResultSet getPaymentsMovesByWallet(String codWallet) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.paymentMoves.*, blocklotto.wallets.initialAmount " + "FROM blocklotto.paymentMoves " + "INNER JOIN blocklotto.wallets ON " + "(blocklotto.wallets.id = blocklotto.paymentMoves.fromWallet OR blocklotto.wallets.id = blocklotto.paymentMoves.toWallet) " + "WHERE blocklotto.wallets.id =?"); p.setString(1, codWallet); return p.executeQuery(); } } catch (SQLException e) { e.printStackTrace(); } return null; }
private long calcHistoricalBalance(Wallet wallet, ResultSet rs, long historicalBalance, boolean aux) throws SQLException { if (aux) { historicalBalance = Tools.convertBTCtoSatoshis(rs.getDouble("initialAmount")); } if (wallet.getId().equals(rs.getString("fromWallet"))) { historicalBalance = historicalBalance - (new BigDecimal(rs.getInt("amount")).longValue()); } else { historicalBalance = historicalBalance + (new BigDecimal(rs.getInt("amount")).longValue()); } return historicalBalance; }
private void setPaymentInformation(Wallet wallet, List<PaymentMoves> paymentMoves, ResultSet rs, long historicalBalance, PaymentMoves payment) throws SQLException { if (wallet.getId().equals(rs.getString("fromWallet"))) { payment.setFrom(wallet.getId()); payment.setTo(rs.getString("toWallet")); payment.setAmount(rs.getInt("amount") * -1); payment.setAction("send"); } else { payment.setFrom(rs.getString("fromWallet")); payment.setTo(wallet.getId()); payment.setAmount(rs.getInt("amount")); payment.setAction("receive"); } payment.setConcept(rs.getString("concept")); payment.setHistoricalAmount(historicalBalance); payment.setDate(rs.getString("date")); paymentMoves.add(payment); }
7.5.5.9 Realizar transferencias a otros usuarios
Permite al usuario realizar transferencias a otros usuarios con carteras Lightning. Como
Lightning permite las transferencias a través de canales ajenos siempre y cuando tengamos
conexión con ellos y tengamos también saldo suficiente, se simula esta característica. El usuario
tiene que pasar un Json con un campo “id” con el identificador de su cartera, un campo
“toWallet” con el pub de la cartera a la que quiere hacer la transferencia y por último un campo
“amount” con la cantidad de satoshis que se quieren transferir.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
60
Se comprueba que el pub pertenezca a una cartera.
//Payment to other account @POST @Path("/tc") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public Message transferCoin(TransferCoin transferCoin) { String id = new MysqlDB().getWalletIDByPUB(transferCoin.getToWallet()); if (!id.isEmpty()) { return lightningAccessor.transferCoin(transferCoin.getId(), id, transferCoin.getAmount()); } else { Message message = new Message(); message.setMessage("Error"); return message; } }
Se procede a realizar el pago llamando al método “sendPaymentWithPayReq”. Este método ya
se explicó en el apartado Comprar boletos.
public Message transferCoin(String WalletA, String walletB, long amount) { Message message = new Message(); message.setMessage("Error"); if (!WalletA.equals(walletB)) { Invoice invoice = createInvoice(walletB, amount); String channel = sendPaymentWithPayReq(invoice.getPay_req(), WalletA, "Transfer"); if (channel.length() == 64) { message.setMessage("Transfer complete"); } } return message; }
7.5.5.10 Consultar notificaciones
Permite al usuario consultar las notificaciones generadas en caso de haber ganado un premio.
El usuario tiene que pasar un Json con un campo “id” con el identificador de su cartera.
@POST @Path("/notifications") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public List<Notification> notifications(Wallet wallet) { return new ManageRaffles().getNotificationsByWalletID(wallet.getId()); }
Se listan todas las notificaciones relacionadas con una cartera y se devuelve al usuario este
listado con formato Json.
public List<Notification> getNotificationsByWalletID(String codWallet) { List<Notification> notifications = new ArrayList<>(); ResultSet rs = new MysqlDB().getNotificationsByWallet(codWallet); try { while (rs.next()) { Notification notification = new Notification( rs.getInt("id"), rs.getString("codWallet"), rs.getString("date"), rs.getString("winningNumber"), rs.getString("name"), rs.getLong("award"), rs.getInt("readed")); notifications.add(notification); } } catch (SQLException e) { e.printStackTrace(); } return notifications; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
61
public ResultSet getNotificationsByWallet(String codWallet) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT a.id, a.codWallet, b.date, b.winningNumber, b.award, c.name, a.readed\n" + "FROM blocklotto.notifications AS a " + "INNER JOIN blocklotto.raffles AS b ON a.codRaffle = b.id " + "INNER JOIN blocklotto.raffleTypes AS c ON b.type = c.id " + "WHERE a.codWallet =? " + "ORDER BY b.date DESC"); p.setString(1, codWallet); return p.executeQuery(); } } catch (SQLException e) { e.printStackTrace(); } return null; }
7.5.5.11 Marcar notificaciones como leídas
Permite al usuario marcar como leídas las notificaciones. El usuario tiene que pasar un Json con
un campo “id” con el identificador de la notificación.
@POST @Path("/nr") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public Message MarkNotifications(Notification notification) { new MysqlDB().setNotificationReaded(notification.getId()); Message message = new Message(); message.setMessage((new MysqlDB().checkNotificationReaded(notification.getId())) ? "Readed" : "Error"); return message; }
Se actualiza la notificación a leída.
public void setNotificationReaded(int id) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.notifications " + "SET blocklotto.notifications.readed=1 " + "WHERE blocklotto.notifications.id=?"); p.setInt(1, id); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
public boolean checkNotificationReaded(int id) { Connection connection = connection(); try { if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.notifications.id " + "FROM blocklotto.notifications " + "WHERE blocklotto.notifications.id=? AND blocklotto.notifications.readed=1"); p.setInt(1, id); boolean b = checkIfExist(p); p.close(); connection.close(); return b; } } catch (SQLException e) { e.printStackTrace(); } return false; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
62
7.5.5.12 Lista de todos los sorteos
Permite al usuario listar todos los sorteos. El usuario tiene que pasar un Json con un campo “id”
con el identificador de su cartera.
@POST @Path("/listad") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public List<Raffle> allDraws(Wallet wallet) { return new ListRaffles().getAllRaffles(wallet); }
Se realiza la consulta y se rellena una lista de objetos Raffle que es devuelta al usuario.
public List<Raffle> getAllRaffles(Wallet wallet) { return new MysqlDB().getListRaffles("SELECT \n" + " blocklotto.raffles.id,\n" + " blocklotto.raffleTypes.price,\n" + " blocklotto.raffles.award,\n" + " blocklotto.raffleTypes.maxTickets AS totalParticipations,\n" + " blocklotto.raffleTypes.name,\n" + " blocklotto.raffles.date,\n" + " blocklotto.raffleTypes.info AS information,\n" + " blocklotto.raffleStates.name AS state,\n" + " blocklotto.raffles.winningNumber,\n" + " blocklotto.raffles.blocked\n" + "FROM\n" + " blocklotto.raffles\n" + " INNER JOIN\n" + " blocklotto.raffleTypes ON blocklotto.raffles.type = blocklotto.raffleTypes.id\n" + " INNER JOIN\n" + " blocklotto.raffleStates ON blocklotto.raffles.state = blocklotto.raffleStates.id\n" + "ORDER BY blocklotto.raffles.date DESC", wallet.getId()); }
public List<Raffle> getListRaffles(String sql, String codWallet) { List<Raffle> raffles = new ArrayList<>(); ListRaffles listRaffles = new ListRaffles(); ConnectionControl connectionControl = executeQuery(sql); ResultSet rs = connectionControl.getResultSet(); try { while (rs.next()) { raffles.add(new Raffle( rs.getInt("id"), rs.getInt("price"), rs.getInt("award"), listRaffles.getAmountTicketsBuyedByWalletID(rs.getInt("id"), codWallet), rs.getInt("totalParticipations"), listRaffles.currentParticipation(rs.getInt("id")), rs.getString("name"), rs.getString("date"), rs.getString("information"), listRaffles.getAmountTicketsBuyedByWalletID(rs.getInt("id"), codWallet) * rs.getInt("price"), rs.getString("state"), rs.getString("winningNumber"), (rs.getInt("blocked") == 1) ? 1 : (getRemainingTickets(rs.getInt("id")) == 0) ? 1 : 0)); } } catch (SQLException e) { e.printStackTrace(); } finally { closeConnection(connectionControl); } return raffles; }
7.5.5.13 Lista de los sorteos pendientes de celebración.
Permite al usuario listar todos los sorteos. El usuario tiene que pasar un Json con un campo “id”
con el identificador de su cartera.
@POST @Path("/listud") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public List<Raffle> upcomingDraws(Wallet wallet) { return new ListRaffles().getUpcomingRaffles(wallet); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
63
Se consultan los sorteos pendientes.
public List<Raffle> getUpcomingRaffles(Wallet wallet) { return new MysqlDB().getListRaffles("SELECT \n" + " blocklotto.raffles.id,\n" + " blocklotto.raffleTypes.price,\n" + " blocklotto.raffles.award,\n" + " blocklotto.raffleTypes.maxTickets AS totalParticipations,\n" + " blocklotto.raffleTypes.name,\n" + " blocklotto.raffles.date,\n" + " blocklotto.raffleTypes.info AS information,\n" + " blocklotto.raffleStates.name AS state,\n" + " blocklotto.raffles.winningNumber,\n" + " blocklotto.raffles.blocked\n" + "FROM\n" + " blocklotto.raffles\n" + " INNER JOIN\n" + " blocklotto.raffleTypes ON blocklotto.raffles.type = blocklotto.raffleTypes.id\n" + " INNER JOIN\n" + " blocklotto.raffleStates ON blocklotto.raffles.state = blocklotto.raffleStates.id\n" + "WHERE\n" + " blocklotto.raffleStates.name='Pending'\n" + "ORDER BY blocklotto.raffles.date ASC", wallet.getId()); }
Posteriormente se rellenará el listado de objetos Raffle como se hizo en el punto Listar todos los
sorteos.
7.5.5.14 Lista de los sorteos con participación
Permite al usuario listar todos los sorteos pendientes en los cuales el usuario ha comprado algún
boleto. El usuario tiene que pasar un Json con un campo “id” con el identificador de su cartera.
Únicamente se mostrarán los sorteos en los que se haya realizado alguna compra.
@POST @Path("/listdp") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public List<Raffle> drawsPendingByWalletID(Wallet wallet) { List<Raffle> list = new ArrayList<>(); for (Raffle raffle : new ListRaffles().getUpcomingRaffles (wallet)) { if (raffle.getAmountTicket() > 0) { list.add(raffle); } } return list; }
7.5.5.15 Listar de los sorteos ya celebrados con participación
Permite al usuario listar todos los sorteos ya celebrados en los cuales el usuario haya participado.
El usuario tiene que pasar un Json con un campo “id” con el identificador de su cartera.
@POST @Path("/listpast") @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) public List<Raffle> pastDrawsByWalletID(Wallet wallet) { List<Raffle> list = new ArrayList<>(); for (Raffle raffle : new ListRaffles().getPastDrawsByWalletID(wallet)) { if (raffle.getAmountTicket() > 0) { list.add(raffle); } } return list; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
64
Se consultan los sorteos celebrados.
public List<Raffle> getPastDrawsByWalletID(Wallet wallet) { return new MysqlDB().getListRaffles("SELECT \n" + " blocklotto.raffles.id,\n" + " blocklotto.raffleTypes.price,\n" + " blocklotto.raffles.award,\n" + " blocklotto.raffleTypes.maxTickets AS totalParticipations,\n" + " blocklotto.raffleTypes.name,\n" + " blocklotto.raffles.date,\n" + " blocklotto.raffleTypes.info AS information,\n" + " blocklotto.raffleStates.name AS state,\n" + " blocklotto.raffles.winningNumber,\n" + " blocklotto.raffles.blocked\n" + "FROM\n" + " blocklotto.raffles\n" + " INNER JOIN\n" + " blocklotto.raffleTypes ON blocklotto.raffles.type = blocklotto.raffleTypes.id\n" + " INNER JOIN\n" + " blocklotto.raffleStates ON blocklotto.raffles.state = blocklotto.raffleStates.id\n" + "WHERE\n" + " blocklotto.raffleStates.name='Celebrated'\n" + "ORDER BY blocklotto.raffles.date DESC", wallet.getId()); }
7.5.6 Sistema de gestión de sorteos.
Para gestionar los sorteos se hace uso de la libraría Quartz. Esta librería permite programar
eventos que se pueden repetir en el tiempo.
Usos de Quartz en el proyecto:
• Bloqueo del sorteo 15 minutos antes de su celebración. De esta manera no se
podrán comprar más boletos a partir de ese momento.
• Celebración del sorteo al día y hora estipulados con el consecuente pago de
premios. En caso de no haber participantes en un sorteo, este se cancelará.
Creación del nuevo sorteo para dentro de una semana a la misma hora.
• Recuperación de fondos de la cartera maestra el primer día de cada mes a las
5 de la mañana. De esta manera el sistema recupera las ganancias de los
canales abiertos.
7.5.6.1 Quartz Listener
Para el correcto funcionamiento de Quartz se deben establecer lo siguiente:
• JobDetail: El trabajo por realizar desarrollado en una nueva clase.
• Trigger: El lanzador del trabajo al cual se le establecerá una condición de
ejecución dada por un cron compatible con Quartz.
• Scheduler: Un programador que enlaza el JobDetail con el Trigger.
En el código se puede ver que los bloqueos están programados para las 21.45 de los jueves,
viernes, sábados y domingos. Los sorteos se celebrarán para las 22.00 de esos mismos días. Y
por último la recuperación de fondos de la cartera master que se ejecutará a las 05.00 del primer
día de cada mes.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
65
Se debe tener en cuenta que, en la base de datos, en la tabla raffleTypes, se establece el día y la
hora del sorteo. Si coincide el día y hora del sorteo con el lanzamiento de la tarea de celebración
de sorteo, comenzará el procedimiento de sorteo.
Ilustración 35 - Tabla raffleTypes
package com.jorgefernandez.tft.blocklotto.resources.quartz; import org.quartz.*; import org.quartz.ee.servlet.QuartzInitializerListener; import org.quartz.impl.StdSchedulerFactory; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.annotation.WebListener; @WebListener public class QuartzListener extends QuartzInitializerListener { @Override public void contextInitialized(ServletContextEvent sce) { super.contextInitialized(sce); ServletContext ctx = sce.getServletContext(); StdSchedulerFactory factory = (StdSchedulerFactory) ctx.getAttribute(QUARTZ_FACTORY_KEY); try { String cronBlock = "0 45 21 ? * THU,FRI,SAT,SUN *"; String cronDraw = "0 00 22 ? * THU,FRI,SAT,SUN *"; String cronCloseMasterChannel = "0 0 5 1 1/1 ? *"; //String cronBlock = "0 0/1 * 1/1 * ? *"; //String cronDraw = "0 0/1 * 1/1 * ? *"; //String cronCloseMasterChannel = "0 0/1 * 1/1 * ? *"; Scheduler scheduler = factory.getScheduler(); JobKey jobKeyA = new JobKey("startDrawJobDetail", "group1"); JobKey jobKeyB = new JobKey("blockDrawJobDetail", "group1"); JobKey jobKeyC = new JobKey("closeMasterChannelJobDetail", "group1"); JobDetail startDrawJobDetail = JobBuilder.newJob(StartDrawJob.class).withIdentity(jobKeyA).build(); JobDetail blockDrawJobDetail = JobBuilder.newJob(BlockDrawJob.class).withIdentity(jobKeyB).build(); JobDetail closeMasterChannelJobDetail = JobBuilder.newJob(CloseMasterChannelJob.class).withIdentity(jobKeyC).build(); Trigger startDrawTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").withSchedule( CronScheduleBuilder.cronSchedule(cronDraw)).startNow().build(); Trigger blockDrawTrigger = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1").withSchedule( CronScheduleBuilder.cronSchedule(cronBlock)).startNow().build(); Trigger closeMasterChannelTrigger = TriggerBuilder.newTrigger().withIdentity("trigger3", "group1").withSchedule( CronScheduleBuilder.cronSchedule(cronCloseMasterChannel)).startNow().build(); scheduler.start(); scheduler.scheduleJob(startDrawJobDetail, startDrawTrigger); scheduler.scheduleJob(blockDrawJobDetail, blockDrawTrigger); scheduler.scheduleJob(closeMasterChannelJobDetail, closeMasterChannelTrigger); } catch (Exception e) { ctx.log("There was an error scheduling the job.", e); } } }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
66
7.5.6.2 BlockDrawListener
Tarea que bloquea el sorteo 15 minutos antes de su celebración.
package com.jorgefernandez.tft.blocklotto.resources.quartz; import com.jorgefernandez.tft.blocklotto.resources.bbdd.MysqlDB; import com.jorgefernandez.tft.blocklotto.resources.tools.Tools; import org.quartz.Job; import org.quartz.JobExecutionContext; import java.util.Calendar; import java.util.Date; public class BlockDrawJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) { Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); int i = Tools.getRaffleByDayAndHour(calendar); if (i != 0) { new MysqlDB().setRaffleBlocked(i); } } }
Se comprueba que exista un sorteo programado para este día y esta hora. De ser así se devuelve
el identificador del sorteo.
public static int getRaffleByDayAndHour(Calendar calendar) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH"); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); return new MysqlDB().getRaffleByDayAndHour(calendar.get(Calendar.DAY_OF_WEEK), Integer.valueOf(simpleDateFormat.format(calendar.getTime()))); }
public int getRaffleByDayAndHour(int day, int hour) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT a.id " + "FROM blocklotto.raffles as a " + "INNER JOIN blocklotto.raffleTypes as b " + "WHERE a.type=b.id " + "AND (b.day=? AND b.hour=?) " + "AND a.state=1 " + "ORDER BY a.id ASC " + "limit 1"); p.setInt(1, day); p.setInt(2, hour); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { return rs.getInt("id"); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return 0; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
67
Por último, se establece como bloqueado ese sorteo.
public void setRaffleBlocked(int idRaffle) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.raffles " + "SET blocklotto.raffles.blocked=1 " + "WHERE blocklotto.raffles.id=?"); p.setInt(1, idRaffle); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
7.5.6.3 StartDrawJob
Tarea que inicia el procedimiento de sorteo y creación del sorteo del mismo tipo para la
siguiente semana.
Se realiza la comprobación de que exista un sorteo para ese día y esa hora.
package com.jorgefernandez.tft.blocklotto.resources.quartz; import com.jorgefernandez.tft.blocklotto.resources.raffles.ManageRaffles; import com.jorgefernandez.tft.blocklotto.resources.tools.Tools; import org.quartz.Job; import org.quartz.JobExecutionContext; import java.util.Calendar; import java.util.Date; public class StartDrawJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) { Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date()); int i = Tools.getRaffleByDayAndHour(calendar); if (i != 0) { new ManageRaffles().celebrateDrawAndCreateOtherOneSameType(i, calendar); } } }
public void celebrateDrawAndCreateOtherOneSameType(int idRaffle, Calendar calendar) { MysqlDB mysqlDB = new MysqlDB(); String[] winner = startDraw(idRaffle); if (!winner[1].isEmpty()) { payPrize(winner[1], idRaffle); mysqlDB.addNotification(winner[1], idRaffle); } else { mysqlDB.setRaffleCanceled(idRaffle); } createRaffle(mysqlDB.getTypeByRaffleID(idRaffle), getDateForFutureRaffle(calendar)); }
Comienza el procedimiento de realización del sorteo.
public String[] startDraw(int idRaffle) { Random generator = new Random(); MysqlDB mysqlDB = new MysqlDB(); HashMap<String, String> ticketsList = mysqlDB.getTicketsList(idRaffle); String[] winner = {"", ""}; if (ticketsList.size() > 0) { Object[] keys = ticketsList.keySet().toArray(); Object[] values = ticketsList.values().toArray(); int random = generator.nextInt(keys.length); winner[0] = keys[random].toString(); winner[1] = values[random].toString(); mysqlDB.addRaffleWinner(idRaffle, winner[0]); } return winner; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
68
Se obtienen los boletos participantes junto con sus dueños.
public HashMap<String, String> getTicketsList(int idRaffle) { HashMap<String, String> ticketsList = new HashMap<>(); try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.raffleNumbers.codWallet, blocklotto.raffleNumbers.number " + "FROM blocklotto.raffleNumbers " + "WHERE blocklotto.raffleNumbers.codRaffle =?"); p.setInt(1, idRaffle); p.executeQuery(); ResultSet rs = p.getResultSet(); while (rs.next()) { ticketsList.put(rs.getString("number"), rs.getString("codWallet")); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return ticketsList; }
Se añade el ganador del sorteo al sistema.
public void addRaffleWinner(int idRaffle, String prizeTicket) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.raffles " + "SET blocklotto.raffles.winningNumber =?, " + " blocklotto.raffles.state = 2 " + "WHERE " + " blocklotto.raffles.id =?"); p.setString(1, prizeTicket); p.setInt(2, idRaffle); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
Se paga el premio y acto seguido se cierra el canal para que el importe del premio aparezca en
el saldo de la cartera ganadora.
public void payPrize(String codWallet, int idRaffle) { if (!codWallet.isEmpty()) { MockLightningAccessor mockLightning = new MockLightningAccessor(); Invoice invoice = mockLightning.createInvoice(codWallet, new MysqlDB().getPrizeRaffle(idRaffle)); mockLightning.closeChannel(mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), masterWallet, "Pago de premio")); } }
Se añade la notificación de ganador del sorteo.
public void addNotification(String codWallet, int codRaffle) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO blocklotto.notifications(codWallet,codRaffle) " + "VALUES(?,?)"); p.setString(1, codWallet); p.setInt(2, codRaffle); p.execute(); } } catch (SQLException e) { e.printStackTrace(); } }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
69
En caso de no existir participantes, se cancela el sorteo.
public void setRaffleCanceled(int idRaffle) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("UPDATE blocklotto.raffles " + "SET blocklotto.raffles.state=3 " + "WHERE blocklotto.raffles.id=?"); p.setInt(1, idRaffle); p.execute(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }
public int getTypeByRaffleID(int idRaffle) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.raffles.type " + "FROM blocklotto.raffles " + "WHERE blocklotto.raffles.id=?"); p.setInt(1, idRaffle); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { return rs.getInt("type"); } rs.close(); p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return 0; }
Se calcula el día del siguiente sorteo.
public String getDateForFutureRaffle(Calendar calendar) { calendar.add(Calendar.DAY_OF_YEAR, 7); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:00"); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); return simpleDateFormat.format(calendar.getTime()); }
Una vez recabados los datos del tipo de sorteo a crear y el día de celebración, se crea el sorteo.
public static int createRaffle(int type, String date) { return new MysqlDB().addRaffle(type, date); }
public int addRaffle(int type, String date) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("INSERT INTO blocklotto.raffles(date,type,award) " + "VALUES(?,?,?)", Statement.RETURN_GENERATED_KEYS); p.setString(1, date); p.setInt(2, type); p.setFloat(3, getInitialPrizeByRaffleID(type)); p.executeUpdate(); ResultSet rs = p.getGeneratedKeys(); int key = 0; if (rs.next()) { key = rs.getInt(1); } p.close(); connection.close(); return key; } } catch (SQLException ignore) { } return 0; }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
70
A la hora de crear el sorteo se establece el premio de forma dinámica según el tipo de sorteo.
Concretamente el premio base son cinco veces el precio de un boleto. Dependiendo del tipo de
sorteo, el premio base cambiará ya que el precio de los boletos difiere de uno a otro.
public long getInitialPrizeByRaffleID(int type) { try { Connection connection = connection(); if (connection != null) { PreparedStatement p = connection.prepareStatement("SELECT blocklotto.raffleTypes.price " + "FROM blocklotto.raffleTypes " + "WHERE blocklotto.raffleTypes.id=?"); p.setInt(1, type); p.executeQuery(); ResultSet rs = p.getResultSet(); if (rs.next()) { return Math.round(5L * rs.getLong("price")); } p.close(); connection.close(); } } catch (SQLException e) { e.printStackTrace(); } return 0L; }
7.5.6.4 CloseMasterChannelJob
Tarea que cierra todos los canales abiertos de la cartera master para la recuperación de fondos.
Se puede observar el funcionamiento de la extracción de fondos en el punto Recuperar saldo de
los canales abiertos .
package com.jorgefernandez.tft.blocklotto.resources.quartz; import com.jorgefernandez.tft.blocklotto.resources.lightning.MockLightningAccessor; import com.jorgefernandez.tft.blocklotto.resources.tools.Configuration; import org.quartz.Job; import org.quartz.JobExecutionContext; public class CloseMasterChannelJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) { Configuration configuration = Configuration.getInstance(); MockLightningAccessor mockLightning = new MockLightningAccessor(); mockLightning.withdrawFunds(configuration.masterWallet); } }
7.5.7 TDD
Para el desarrollo de este proyecto me he ayudado del desarrollo guiado por pruebas. A
continuación, el código de todas las pruebas creadas.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
71
7.5.7.1 ManageRaffleTest package com.jorgefernandez.tft.blocklotto.resources.raffles; import com.jorgefernandez.tft.blocklotto.resources.bbdd.MysqlDB; import com.jorgefernandez.tft.blocklotto.resources.lightning.MockLightningAccessor; import com.jorgefernandez.tft.blocklotto.resources.lightning.objects.BuyRaffle; import com.jorgefernandez.tft.blocklotto.resources.lightning.objects.Wallet; import com.jorgefernandez.tft.blocklotto.resources.tools.Tools; import org.junit.Assert; import org.junit.Test; import java.util.Calendar; import java.util.GregorianCalendar; public class ManageRafflesTest { public int createRaffle() { return ManageRaffles.createRaffle(4, Tools.getCurrentTime()); } @Test public void createRaffle_Created_RightParameters() { int id = ManageRaffles.createRaffle(3, Tools.getCurrentTime()); Assert.assertNotEquals(0, id); new ManageRaffles().removeRaffle(id); } @Test public void createRaffle_NotCreated_WrongParameters() { int id = ManageRaffles.createRaffle(9, Tools.getCurrentTime()); Assert.assertEquals(0, id); } @Test public void removeRaffle_Removed_RaffleExists() { int id = createRaffle(); Assert.assertTrue(new ManageRaffles().removeRaffle(id)); } @Test public void removeRaffle_NotRemoved_RaffleDoesntExist() { int id = -1; Assert.assertFalse(new ManageRaffles().removeRaffle(id)); } @Test public void getAwardRaffle_Award_RaffleExists() { int id = createRaffle(); MysqlDB mysqlDB = new MysqlDB(); Assert.assertEquals(2500000, mysqlDB.getPrizeRaffle(id)); new ManageRaffles().removeRaffle(id); } @Test public void getAwardRaffle_0_RaffleDoesntExist() { Assert.assertEquals(0, new MysqlDB().getPrizeRaffle(-1)); } @Test public void getRemainingTickets_NumberTickets_RaffleExists() { int id = createRaffle(); Assert.assertEquals(250, new MysqlDB().getRemainingTickets(id)); new ManageRaffles().removeRaffle(id); } @Test public void getRemainingTickets_0_RaffleDoesntExists() { Assert.assertEquals(0, new MysqlDB().getRemainingTickets(-1)); } @Test public void buyTickets_245_WithStock() { checkBuyTickets(5, 245); } @Test public void buyTickets_250_WithOutStock() { checkBuyTickets(300, 250); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
72
private void checkBuyTickets(int i, int i2) { int id = createRaffle(); Wallet wallet = createWallet(); ManageRaffles manageRaffles = new ManageRaffles(); MysqlDB mysqlDB = new MysqlDB(); BuyRaffle buyRaffle = new BuyRaffle(wallet.getId(), id, i); manageRaffles.buyTickets(buyRaffle); Assert.assertEquals(i2, mysqlDB.getRemainingTickets(id)); mysqlDB.deleteTickets(id); manageRaffles.removeRaffle(id); new MockLightningAccessor().deleteWallet(wallet); } @Test public void startDraw_Raffled_TicketsBuyed() { int id = createRaffle(); Wallet wallet = createWallet(); ManageRaffles manageRaffles = new ManageRaffles(); BuyRaffle buyRaffle = new BuyRaffle(wallet.getId(), id, 5); manageRaffles.buyTickets(buyRaffle); Assert.assertEquals(5, manageRaffles.startDraw(id)[0].length()); manageRaffles.removeRaffle(id); new MockLightningAccessor().deleteWallet(wallet); } @Test public void startDraw_NotRaffled_TicketsDindtBuy() { int id = createRaffle(); ManageRaffles manageRaffles = new ManageRaffles(); Assert.assertEquals(0, manageRaffles.startDraw(id)[0].length()); manageRaffles.removeRaffle(id); } @Test public void payPrize_Paid_RaffleDrawn() { ManageRaffles manageRaffles = new ManageRaffles(); MysqlDB mysqlDB = new MysqlDB(); Wallet wallet = createWallet(); int id = createRaffle(); BuyRaffle buyRaffle = new BuyRaffle(wallet.getId(), id, 5); manageRaffles.buyTickets(buyRaffle); manageRaffles.payPrize(manageRaffles.startDraw(id)[1], id); Assert.assertEquals((99465950 + mysqlDB.getInitialPrizeByRaffleID(4)), Tools.convertBTCtoSatoshis(mysqlDB.getAmountByWalletID(wallet.getId()))); manageRaffles.removeRaffle(id); new MockLightningAccessor().deleteWallet(wallet); } private Wallet createWallet() { Wallet wallet = new Wallet(); wallet.setPass("kokokoko"); return new MockLightningAccessor().createWallet(wallet); } @Test public void getDateForFutureRaffle_17_calendar() { Calendar calendar = new GregorianCalendar(2018,0,1); int i = calendar.get(Calendar.DAY_OF_MONTH); new ManageRaffles().getDateForFutureRaffle(calendar); Assert.assertEquals(i + 7, calendar.get(Calendar.DAY_OF_MONTH)); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
73
7.5.7.2 MockLightningAccessorTest package com.jorgefernandez.tft.blocklotto.resources.lightning; import com.jorgefernandez.tft.blocklotto.resources.bbdd.MysqlDB; import com.jorgefernandez.tft.blocklotto.resources.lightning.objects.*; import com.jorgefernandez.tft.blocklotto.resources.tools.Tools; import org.junit.Assert; import org.junit.Test; import java.util.ArrayList; import java.util.List; import static com.jorgefernandez.tft.blocklotto.resources.tools.Tools.*; public class MockLightningAccessorTest { @Test public void generatedRandomWords() { Assert.assertEquals(24, new MockLightningAccessor().generateRandomWords().size()); } @Test public void createWallet_Created_WithPass() { Wallet wallet = createWallet("kokokoko"); Assert.assertEquals(66, wallet.getPub().length()); Assert.assertEquals(34, wallet.getAddress().length()); Assert.assertEquals(24, wallet.getSeedWords().size()); Assert.assertNotNull(wallet.getId()); new MockLightningAccessor().deleteWallet(wallet); } @Test public void createWallet_NotCreated_WithOutPass() { Wallet wallet = createWallet(""); Assert.assertNull(wallet.getId()); Assert.assertNull(wallet.getPub()); Assert.assertNull(wallet.getAddress()); Assert.assertNull(wallet.getSeedWords()); } private Wallet unlockWallet(String pass) { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = createWallet("kokokoko"); Wallet walletB = new Wallet(); walletB.setPass(pass); walletB.setId(wallet.getId()); wallet = mockLightning.unlockWallet(walletB); mockLightning.deleteWallet(wallet); return wallet; } @Test public void unlockWallet_Unlocked_RightPass() { Assert.assertTrue(unlockWallet("kokokoko").isUnlock()); } @Test public void unlockWallet_Locked_WrongPass() { Assert.assertFalse(unlockWallet("aaa").isUnlock()); } @Test public void restoreWallet_Restored_RightWords() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = new Wallet(); walletB.setSeedWords(walletA.getSeedWords()); walletB.setPass("kukukuku"); Assert.assertEquals(walletA.getId(), mockLightning.restoreWallet(walletB).getId()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void restoreWallet_Fail_WrongWords() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = new Wallet(); walletB.setSeedWords(stringToWords("car hello monitor telephone door airplane train boat clock headphone speaker")); walletB.setPass("kukukuku"); Assert.assertNull(mockLightning.restoreWallet(walletB).getId()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void deleteWallet_Deleted_WalletCreated() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = createWallet("kokokoko"); Assert.assertTrue(mockLightning.deleteWallet(wallet)); } @Test public void deleteWallet_NotDeleted_WalletCreated() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = createWallet(""); Assert.assertFalse(mockLightning.deleteWallet(wallet)); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
74
@Test public void deleteChannel_NotDeleted_ChannelCreated() { Assert.assertFalse(new MysqlDB().deleteChannel("TEST")); } @Test public void deleteChannel_Deleted_ChannelCreated() { MockLightningAccessor mockLightningAccessor = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); mockLightningAccessor.createPeer(walletA.getId(), walletB.getId()); Channel channel = mockLightningAccessor.openChannel(walletA.getId(), walletB.getId(), 20000, 0); Assert.assertTrue(new MysqlDB().deleteChannel(channel.getFunding_txid())); mockLightningAccessor.deleteWallet(walletA); mockLightningAccessor.deleteWallet(walletB); } @Test public void getWalletBalance_1Balance_RightWallet() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = createWallet("kokokoko"); WalletBalance walletBalance = mockLightning.getWalletBalance(wallet.getId()); Assert.assertEquals(1f, walletBalance.getTotal_balance(), 0); mockLightning.deleteWallet(wallet); } @Test public void getWalletBalance_0Balance_WrongWallet() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = createWallet(""); WalletBalance walletBalance = mockLightning.getWalletBalance(wallet.getId()); Assert.assertEquals(0f, walletBalance.getTotal_balance(), 0); mockLightning.deleteWallet(wallet); } @Test public void createPeer_Created_TwoDifferentWallets() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet nodeA = createWallet("kokokoko"); Wallet nodeB = createWallet("kokokoko"); mockLightning.createPeer(nodeA.getId(), nodeB.getId()); Assert.assertTrue(new MysqlDB().checkPeer(nodeA.getId(), nodeB.getId())); mockLightning.deleteWallet(nodeA); mockLightning.deleteWallet(nodeB); } @Test public void createPeer_NotCreated_TwoSameWallet() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet nodeA = createWallet("kokokoko"); mockLightning.createPeer(nodeA.getId(), nodeA.getId()); Assert.assertFalse(new MysqlDB().checkPeer(nodeA.getId(), nodeA.getId())); mockLightning.deleteWallet(nodeA); } @Test public void createPeer_NotCreated_PeerExist() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet nodeA = createWallet("kokokoko"); Wallet nodeB = createWallet("kokokoko"); mockLightning.createPeer(nodeA.getId(), nodeB.getId()); Assert.assertFalse(mockLightning.createPeer(nodeA.getId(), nodeB.getId())); mockLightning.deleteWallet(nodeA); mockLightning.deleteWallet(nodeB); } @Test public void deletePeer_Deleted_PeerExist() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet nodeA = createWallet("kokokoko"); Wallet nodeB = createWallet("kokokoko"); mockLightning.createPeer(nodeA.getId(), nodeB.getId()); Assert.assertTrue(mockLightning.deletePeer(nodeA.getId(), nodeB.getId())); mockLightning.deleteWallet(nodeA); mockLightning.deleteWallet(nodeB); } @Test public void deletePeer_NotDeleted_PeerNotExist() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet nodeA = createWallet("kokokoko"); Wallet nodeB = createWallet("kokokoko"); Assert.assertFalse(mockLightning.deletePeer(nodeA.getId(), nodeB.getId())); mockLightning.deleteWallet(nodeA); mockLightning.deleteWallet(nodeB); } private Wallet createWallet(String pass) { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = new Wallet(); wallet.setPass(pass); return mockLightning.createWallet(wallet); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
75
@Test public void convertBTCtoSatoshi_Converted_DiferentTests() { Assert.assertEquals(0L, convertBTCtoSatoshis(0d)); Assert.assertEquals(100000000L, convertBTCtoSatoshis(1d)); Assert.assertEquals(84000L, convertBTCtoSatoshis(0.00084000)); Assert.assertEquals(311234000L, convertBTCtoSatoshis(3.11234000)); } @Test public void convertSatoshitoBTC_Converted_DiferentTests() { Assert.assertEquals(0d, convertSatoshistoBTC(0), 0); Assert.assertEquals(1d, convertSatoshistoBTC(100000000), 0); Assert.assertEquals(0.00000840d, convertSatoshistoBTC(840), 0); Assert.assertEquals(2.08778004, convertSatoshistoBTC(208778004), 0); } @Test public void openChannel_DontOpen_WithOutPeerConnection() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletLocal = createWallet("kokokoko"); Wallet walletRemote = createWallet("kokokoko"); String s = mockLightning.openChannel(walletLocal.getId(), walletRemote.getId(), 50000, 0).getFunding_txid(); Assert.assertNull(s); mockLightning.deleteWallet(walletLocal); mockLightning.deleteWallet(walletRemote); } @Test public void openChannel_Openend_WithPeerConnection() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletLocal = createWallet("kokokoko"); Wallet walletRemote = createWallet("kokokoko"); mockLightning.createPeer(walletLocal.getId(), walletRemote.getId()); String s = mockLightning.openChannel(walletLocal.getId(), walletRemote.getId(), 50000, 0).getFunding_txid(); Assert.assertEquals(64, s.length()); mockLightning.deletePeer(walletLocal.getId(), walletRemote.getId()); new MysqlDB().deleteChannel(s); mockLightning.deleteWallet(walletLocal); mockLightning.deleteWallet(walletRemote); } @Test public void generateRandomStrings_Generated_DifferentTests() { Assert.assertEquals(50, generateRandomString(50).length()); Assert.assertEquals(80, generateRandomString(80).length()); Assert.assertEquals(189, generateRandomString(189).length()); Assert.assertEquals(1, generateRandomString(1).length()); Assert.assertEquals(0, generateRandomString(0).length()); } @Test public void createInvoice_Added_RightParameters() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = createWallet("kokokoko"); Invoice invoiceA = mockLightning.createInvoice(wallet.getId(), 1000); Invoice invoiceB = mockLightning.createInvoice(wallet.getId(), 1000); Assert.assertEquals(189, invoiceA.getPay_req().length()); Assert.assertEquals(2, invoiceB.getAdd_index()); mockLightning.deleteWallet(wallet); } @Test public void createInvoice_NotAdded_WrongParameters() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet wallet = createWallet("kokoko"); Invoice invoiceA = mockLightning.createInvoice(wallet.getId(), 1000); Invoice invoiceB = mockLightning.createInvoice(wallet.getId(), 1000); Assert.assertNull(invoiceA.getPay_req()); Assert.assertEquals(0, invoiceB.getAdd_index()); } @Test public void getValidPaymentRoute_Valid_RightParameters() { MockLightningAccessor mockLightning = new MockLightningAccessor(); MysqlDB mysqlDB = new MysqlDB(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); mockLightning.createPeer(walletA.getId(), walletB.getId()); mockLightning.openChannel(walletA.getId(), walletB.getId(), 32000, 0); Invoice invoice = mockLightning.createInvoice(walletB.getId(), 10000); Assert.assertEquals(64, mysqlDB.getValidPaymentRoute(invoice.getPay_req(), walletA.getId(), walletB.getId()).length()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
76
@Test public void getValidPaymentRoute_Invalid_WrongParameters() { MockLightningAccessor mockLightning = new MockLightningAccessor(); MysqlDB mysqlDB = new MysqlDB(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Wallet walletC = createWallet("kokokoko"); mockLightning.createPeer(walletA.getId(), walletB.getId()); mockLightning.openChannel(walletA.getId(), walletB.getId(), 32000, 0); Invoice invoice = mockLightning.createInvoice(walletB.getId(), 10000); Assert.assertEquals("", mysqlDB.getValidPaymentRoute(invoice.getPay_req(), walletC.getId(), walletB.getId())); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); mockLightning.deleteWallet(walletC); } @Test public void sendPaymentWithPayReq_Send_HaveFunds() { MockLightningAccessor mockLightning = new MockLightningAccessor(); MysqlDB mysqlDB = new MysqlDB(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); int amount = 1000000; Invoice invoice = mockLightning.createInvoice(walletB.getId(), amount); mockLightning.closeChannel(mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletA.getId(), "Testing SendPayments")); Assert.assertEquals(1.01D, mysqlDB.getAmountByWalletID(walletB.getId()), 0); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void sendPaymentWithPayReq_DontSend_HaventFunds() { MockLightningAccessor mockLightning = new MockLightningAccessor(); MysqlDB mysqlDB = new MysqlDB(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); int amount = 1000000000; Invoice invoice = mockLightning.createInvoice(walletB.getId(), amount); mockLightning.closeChannel(mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletA.getId(), "Testing SendPayments")); Assert.assertEquals(1f, mysqlDB.getAmountByWalletID(walletB.getId()), 0); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void sendPaymentWithPayReq_0Payments_OutOfBalance() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Invoice invoice = mockLightning.createInvoice(walletB.getId(), 500000000); mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletA.getId(), "Testing paymentMoves"); Assert.assertEquals(100000000L, convertBTCtoSatoshis(mockLightning.getWalletBalance(walletA.getId()).getTotal_balance())); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void sendPaymentWithPayReq_1Payment_HaveEnoughBalance() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Invoice invoice = mockLightning.createInvoice(walletB.getId(), 500000); mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletA.getId(), "Testing paymentMoves"); Assert.assertEquals(98000000L, convertBTCtoSatoshis(mockLightning.getWalletBalance(walletA.getId()).getTotal_balance())); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void wordsToString_valid_RightParameters() { List<String> list = new ArrayList<>(); list.add("dog"); list.add("cat"); list.add("lion"); Assert.assertEquals(getHashText("dog cat lion"), Tools.wordsToString(list)); } @Test public void stringToWords_valid_RightParameters() { List<String> list = new ArrayList<>(); list.add("dog"); list.add("cat"); list.add("lion"); Assert.assertEquals(list, Tools.stringToWords("dog cat lion")); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
77
@Test public void getTotalBalanceInChannels_TotalBalance_ExistChannelForWallet() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Invoice invoice = mockLightning.createInvoice(walletB.getId(), 50000); mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletA.getId(), "Testing TotalBalanceInChannels"); invoice = mockLightning.createInvoice(walletA.getId(), 1000000); mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletB.getId(), "Testing TotalBalanceInChannels"); Assert.assertEquals(1040950, mockLightning.getTotalBalanceInChannels(walletB).getBalance()); Assert.assertEquals(2940950, mockLightning.getTotalBalanceInChannels(walletA).getBalance()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void getTotalBalanceInChannels_0_DontExistChannelForWallet() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Assert.assertEquals(0, mockLightning.getTotalBalanceInChannels(walletB).getBalance()); Assert.assertEquals(0, mockLightning.getTotalBalanceInChannels(walletA).getBalance()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void getAllPaymentMovesByWallet_Payments_ExistMovements() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Invoice invoice = mockLightning.createInvoice(walletB.getId(), 50000); mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletA.getId(), "Testing paymentMoves"); invoice = mockLightning.createInvoice(walletA.getId(), 1000000); mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletB.getId(), "Testing paymentMoves"); invoice = mockLightning.createInvoice(walletB.getId(), 10000); mockLightning.sendPaymentWithPayReq(invoice.getPay_req(), walletA.getId(), "Testing paymentMoves"); List<PaymentMoves> paymentMoves = mockLightning.getAllPaymentMovesByWallet(walletA); Assert.assertEquals(100950000L, paymentMoves.get(1).getHistoricalAmount()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void getAllPaymentMovesByWallet_0_DontExistMovements() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); List<PaymentMoves> paymentMoves = mockLightning.getAllPaymentMovesByWallet(walletA); Assert.assertEquals(0, paymentMoves.size()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void moneyTransfer_Error_SameWallet() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Assert.assertEquals("Error", mockLightning.transferCoin(walletA.getId(), new MysqlDB().getWalletIDByPUB(walletA.getPub()), 800000).getMessage()); mockLightning.deleteWallet(walletA); } @Test public void transferCoin_TransferComplete_WalletAHasFundsAndTransferToWalletBInNewChannel() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Assert.assertEquals("Transfer complete", mockLightning.transferCoin(walletA.getId(), new MysqlDB().getWalletIDByPUB(walletB.getPub()), 800000).getMessage()); Assert.assertEquals(1, new MysqlDB().getNumberChannelsbyWallet(walletB.getId())); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void transferCoin_Error_WalletAHasNotFundsAndTransferToWalletBInNewChannel() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Assert.assertEquals("Error", mockLightning.transferCoin(walletA.getId(), new MysqlDB().getWalletIDByPUB(walletB.getPub()), 800000000).getMessage()); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); }
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
78
@Test public void transferCoin_TransferComplete_WalletAHasFundsAndTransferToWalletBInExistingChannel() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); mockLightning.createPeer(walletA.getId(), walletB.getId()); mockLightning.openChannel(walletA.getId(), walletB.getId(), 1000000, 0); Assert.assertEquals("Transfer complete", mockLightning.transferCoin(walletA.getId(), new MysqlDB().getWalletIDByPUB(walletB.getPub()), 800000).getMessage()); Assert.assertEquals(800000, mockLightning.getTotalBalanceInChannels(walletB).getBalance()); Assert.assertEquals(1, new MysqlDB().getNumberChannelsbyWallet(walletB.getId())); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); } @Test public void transferCoin_TransferComplete_WalletAHasFundsAndTransferToWalletBThroughWalletCInExistingChannel() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Wallet walletC = createWallet("kokokoko"); mockLightning.createPeer(walletA.getId(), walletC.getId()); mockLightning.openChannel(walletA.getId(), walletC.getId(), 1000000, 0); mockLightning.createPeer(walletC.getId(), walletB.getId()); mockLightning.openChannel(walletC.getId(), walletB.getId(), 1000000, 0); Assert.assertEquals("Transfer complete", mockLightning.transferCoin(walletA.getId(), new MysqlDB().getWalletIDByPUB(walletB.getPub()), 800000).getMessage()); Assert.assertEquals(800000, mockLightning.getTotalBalanceInChannels(walletB).getBalance()); Assert.assertEquals(1, new MysqlDB().getNumberChannelsbyWallet(walletB.getId())); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); mockLightning.deleteWallet(walletC); } @Test public void transferCoin_TransferComplete_WalletAHasFundsAndTransferToWalletBThroughWalletCInNewChannel() { MockLightningAccessor mockLightning = new MockLightningAccessor(); Wallet walletA = createWallet("kokokoko"); Wallet walletB = createWallet("kokokoko"); Wallet walletC = createWallet("kokokoko"); mockLightning.createPeer(walletA.getId(), walletC.getId()); mockLightning.openChannel(walletA.getId(), walletC.getId(), 1000000, 0); mockLightning.createPeer(walletC.getId(), walletB.getId()); mockLightning.openChannel(walletC.getId(), walletB.getId(), 100000, 0); Assert.assertEquals("Transfer complete", mockLightning.transferCoin(walletA.getId(), new MysqlDB().getWalletIDByPUB(walletB.getPub()), 800000).getMessage()); Assert.assertEquals(800000, mockLightning.getTotalBalanceInChannels(walletB).getBalance()); Assert.assertEquals(2, new MysqlDB().getNumberChannelsbyWallet(walletB.getId())); mockLightning.deleteWallet(walletA); mockLightning.deleteWallet(walletB); mockLightning.deleteWallet(walletC); } }
8. Conclusiones y trabajos futuros En este apartado se expondrá cual ha sido el resultado final, así como si se han cumplido o no
los objetivos. Por último, se expondrán las posibles mejoras que se le podría aplicar al proyecto
en un futuro.
8.1 Resultado Al terminar este proyecto se ha obtenido un servicio web el cual es capaz de suministrar
información a un cliente mediante diferentes peticiones de este. Este servicio web es capaz de
simular operaciones propias de Lightning Network, como son:
• Creación, restauración y desbloqueo de una cartera.
• Realización de pagos instantáneos a través de los canales.
• Consulta de saldo tanto de la cartera como de los canales abiertos en los que la cartera esté
incluida.
• Cierre de estos canales para recuperar los fondos que permanecen dentro de los canales
abiertos.
• Transferencias eligiendo las rutas válidas más cortas. Estas rutas contienen canales de los
cuales no formamos parte.
• Listar notificaciones que avisan al ganador de un sorteo.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
79
• Listar los sorteos, totales, pendientes, pendientes con participación y los celebrados.
Por otro lado, también se ha desarrollado el sistema automatizado de sorteos que celebra, paga
premios y crea nuevos sorteos.
8.2 Consecución de los objetivos Si bien todos los casos de uso han sido cumplidos satisfactoriamente, también es verdad que no
se ha hecho uso de la tecnología Lightning Network. Esto ha sido así por lo ya explicado en el
apartado 3.3 Problemas con Lightning Network. Personalmente me hubiera gustado que el
protocolo hubiera estado más avanzado y no diera tantos problemas a la hora de desarrollar
con él en Java. Como se ha comentado anteriormente, la búsqueda de soluciones para el
lenguaje Java de dicho protocolo son frustrantes. Por ejemplo, si nos fijamos en el número de
usos de librerías como LightningJ en Maven Repository, vemos que no lo está usando
prácticamente nadie. Muchos desarrolladores optan por realizar sus proyectos en Python o en
JavaScript ya que la creadora del proyecto, Lightning Network Daemon, da soporte a esos
lenguajes con tutoriales y diferentes guías. Así y todo, Lightning Network Daemon está en una
fase muy temprana.
Ilustración 36 - Maven Repository - LightningJ
8.3 Trabajos futuros A continuación, se expondrán cuáles podrían ser algunas futuras líneas de trabajo relacionadas
con este proyecto:
• Una vez se estabilice el desarrollo de Lightning Network, habría que rehacer el proyecto para
que fuera compatible con el protocolo.
• Añadir un sistema de control de saldo a la cartera maestra, para que, de esta manera, si el
saldo fuera inferior a una cierta cantidad, se cerraran los canales relacionados con la cartera
y de este modo se recuperara liquidez para poder hacer frente a los premios.
• Añadir una interfaz de administración del sistema. Esta interfaz podría dar información
como número de usuarios, cantidad recaudada en diferentes periodos de tiempo,
estadísticas sobre el juego, etc.
• Desarrollo de una interfaz web para poder hacer uso de la aplicación desde cualquier
dispositivo.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
80
9. Herramientas utilizadas A continuación, los recursos necesarios para la realización de este proyecto.
9.1 Hardware
9.1.1 Ordenador portátil personal
Para el desarrollo del proyecto he hecho uso de mi ordenador portátil cuyas especificaciones
son las siguientes:
• Intel® Core™ i7-4700MQ CPU @2.40GHz.
• 8 GB de RAM
• Tarjeta gráfica Nvidia GTX 770M 3GB DDR5
• Conexión a internet
9.1.2 Servidores
Los servidores para ofrecer el servicio están documentados en el apartado Diagrama de
despliegue.
9.2 Software
9.2.1 Windows 10 Home
Este es el sistema operativo sobre el que desarrollo en mi ordenador portátil personal. Con
licencia personal de estudiante.
9.2.2 IntelliJ IDEA Ultimate 2018.3
Para el desarrollo en Java se hace uso de este entorno de desarrollo. Licencia personal de
estudiante.
9.2.3 Trello
Este software de administración de tareas se ha usado para llevar el control de las tareas
vinculadas a este proyecto. Permite la creación de listas de tareas y el establecimiento de colores
para marcarlas como realizadas, pendientes o en proceso. Comparto Trello con mi compañera
de proyecto Cynthia J. Afonso García. De esta manera estamos siempre informados de en qué
se está trabajando en cada momento.
9.2.4 SoapUI by SmartBear
Herramienta que permite la realización de pruebas contra servidores web. Lo utilizo para probar
las peticiones enviadas al web service mandando los parámetros con Json.
9.2.5 Github
Permite el alojamiento de proyectos utilizando el sistema de control de versiones. Este proyecto
está alojado en esta dirección https://github.com/kovutech/BlockLotto.
9.2.6 MySQL Workbench
Herramienta de diseño de bases de datos Mysql.
9.2.7 Github Desktop
Herramienta que facilita la gestión del repositorio Github.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
81
10. Referencias [1] ¿Qué es la tecnología de contabilidad distribuida o blockchain? -
https://www.criptonoticias.com/informacion/que-es-tecnologia-contabilidad-distribuida-
blockchain/
[2] ¿Qué es la minería de bitcoins (criptomonedas)? -
https://www.criptonoticias.com/informacion/que-es-la-mineria-de-bitcoins-criptomonedas/
[3] ¿Qué es Bitcoin? - https://www.criptonoticias.com/informacion/que-es-bitcoin/
[4] Whitepaper de Bitcoin explicado en español - https://academy.bit2me.com/paper-original-
bitcoin-en-espanol/#Prueba-de-trabajo
[5] Bitcoin Problems & Issues in 2018 - https://www.abitgreedy.com/bitcoin-problems/
[6] The 5 Major Problems with Bitcoin and Blockchain Technology -
https://blockchainreview.io/blockchain-bitcoin-problems-limitations-issues-weaknesses/
[7] Lightning Labs - https://lightning.engineering/
[8] Lightning Network Scalable, Instant Bitcoin/Blockchain Transactions -
https://lightning.network/
[9] Lightning App Directory - https://dev.lightning.community/lapps/
[10] Tutorial Lightning Network LND - https://dev.lightning.community/tutorial/01-
lncli/index.html
[11] LND Overview and Developer Guide - https://dev.lightning.community/overview/
[12] LightningJ - https://www.lightningj.org/
[13] Slack Lightning group - https://lightningcommunity.slack.com/
[14] Quartz Tutorials - http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/
[15] Jersey and Guice: a perfect combination for writing RESTful APIs -
https://sites.google.com/a/athaydes.com/renato-athaydes/posts/jersey_guice_rest_api
[16] How to write a Java gRPC client for the Lightning Network Daemon -
https://github.com/lightningnetwork/lnd/blob/master/docs/grpc/java.md
[17] CronMaker - http://www.cronmaker.com/
[18] Blockchain: Qué es y cuales podrían ser sus usos y ventajas en la Industria 4.0
https://grupogaratu.com/que-es-blockchain-usos-y-ventajas/
[19] Lightning Desktop App por Lightning Labs - https://github.com/lightninglabs/lightning-
app
[20] Yall’s - https://yalls.org/
[21] lnd.fun - http://lnd.fun/
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
82
Anexo I: Manual de usuario del cliente Seguidamente se mostrarán cuáles son los diferentes métodos a los que tiene acceso el cliente
para operar con el servicio de loterías.
Cada apartado se compondrá de las siguientes partes:
1. Descripción
2. Imagen generada por SoapUI que muestra la dirección a realizar la petición, Json de la
petición y de la respuesta.
3. Posibles aclaraciones
1. Creación de cartera Creación de la cartera del usuario con la que podrá operar haciendo transferencias con el
protocolo Lightning Network.
Ilustración 37 - Manual - Creación de cartera
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
83
2. Restauración de cartera Restauración de la cartera. Es necesario conocer las 24 palabras generadas en la creación de la
cartera para poder restaurarla.
Ilustración 38 - Manual - Restauración de cartera
3. Desbloqueo de cartera Desbloqueo de cartera en situaciones en las que el nodo se haya caído y ya no estemos
autenticados.
Ilustración 39 - Manual - Desbloqueo de cartera
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
84
4. Consulta de saldo Consulta de saldo de la cartera.
Ilustración 40 - Manual - Consulta de saldo
5. Compra de boletos Procedimiento para la compra de boletos de un sorteo determinado
Ilustración 41 - Manual - Compra de boletos
6. Consulta de saldo en los canales Se consultan todos los fondos de todos los canales abiertos.
Ilustración 42 - Manual - Consulta de saldo en los canales
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
85
7. Extraer fondos de los canales Se extraen todos los fondos de todos los canales abiertos.
Ilustración 43 - Manual - Extraer fondos
8.Movimientos de pago Listado de movimientos de pago relacionados con la cartera
Ilustración 44 - Manual - Movimientos de pago
9. Transferencias a otra cuenta Transferencias efectuadas a otra cuenta. Puede o no existir una ruta válida. En caso de no existir
se creará una directa con el destinatario. El canal al finalizar la transferencia queda abierto, por
lo que el saldo no se verá reflejado en la cartera de destino hasta que el canal se cierre.
Ilustración 45 - Manual - Transferencias a otra cuenta
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
86
10. Ver notificaciones Ver las notificaciones de los premios conseguidos.
Ilustración 46 - Manual - Ver notificaciones
11. Marcar notificación como leída.
Ilustración 47 - Marcar notificación como leída
12. Listar todos los sorteos
Ilustración 48 - Manual - Listar todos los sorteos
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
87
Explicación de algunos campos de la respuesta:
• award: premio (price).
• price: precio de cada boleto.
• amountTicket: Número de boletos que he comprado para ese sorteo.
• priceTotal: Total de dinero gastado en comprar participaciones para este sorteo.
• totalParticipations: Número total de boletos a la venta.
• currentParticipations: Número total de boletos vendidos.
13. Listar los sorteos pendientes
Ilustración 49 - Manual - Listar los sorteos pendientes
14. Listar los sorteos pendientes en los que participo
Ilustración 50 - Manual - Listar los sorteos pendientes en los que participo
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
88
15. Listar todos los sorteos en los que he participado
Ilustración 51 - Manual - Listar todos los sorteos en los que he participado
Anexo II: Manual de usuario del administrador.
1. Configuración de los sorteos Para añadir un nuevo tipo de sorteo, se debe añadir un nuevo registro en la tabla raffleTypes y
crear el sorteo en la tabla raffles, poniendo en el campo tipo, el id del nuevo tipo creado.
A continuación, debemos de especificar una configuración de cron en la clase QuartzListener
para que programe la ejecución de este nuevo tipo de sorteo.
1.1 Base de datos
Ilustración 52 - Manual - Tabla raffleTypes
En la tabla raffleTypes, tenemos los 4 distintos sorteos que están configurados actualmente.
Explicación de la tabla:
• name: nombre del sorteo
• maxTickets: número máximo de boletos que se pueden comprar antes de que el sistema
bloquee el sorteo. Podría suceder que se compraran más boletos de los permitidos porque
dos o más usuarios están comprando a la vez los últimos boletos disponibles y no se están
realizando controles de bloqueo. Pero eso no tiene ninguna importancia ya que el cálculo
del premio se realiza de forma dinámica con respecto al número de boletos comprados. El
máximo se establece, para tener más o menos acotados el importe de los premios.
• price: Precio de un boleto.
• Info: Cadena de texto informativa.
• day: Día de la semana en el que se celebrará el sorteo. Ejemplo: domingo 1 y sábado 7.
Trabajo de Fin de Grado – Cursos 2018/2019 - ENERO
BLOCKLOTTO
89
• hour: Hora del día en el que se celebrará el sorteo. Hora GMT.
1.2 Quartz
En la clase QuartzListener se programa cuando queremos realizar las comprobaciones para
bloquear y celebrar un sorteo.
Ilustración 53 - Manual - Configuración cron
Existe una herramienta [17] que puede ayudar a la hora de crear nuevas string de cron
compatibles con Quartz.