Cómo implementar independientemente (Prueba de existencia) en 2 pasos

Hola a todos! Mi nombre es Ararat y trabajo para QuantNet, que realiza concursos de estrategias algorítmicas. Recientemente, surgió una tarea importante para mí: proporcionar garantías para la inviolabilidad de las fechas de los usuarios (esto es extremadamente importante, ya que para verificar correctamente la efectividad de las estrategias, es necesario utilizar datos de los mercados financieros mundiales en tiempo real).

Ahí es donde me encontré con el concepto de PoE (Prueba de existencia). Se ha escrito lo suficiente sobre esto en Internet, pero los detalles de la plataforma me hicieron romper un poco la mente. Por lo tanto, decidí escribir este artículo y compartir mi experiencia en la arquitectura e implementación de PoE. Creo que será especialmente relevante para los chicos de fintech.

Dividí mi artículo en 3 bloques principales:

  • ¿Qué es PoE y cuándo puede ser necesario?
  • Algoritmo de implementación
  • La solución a mi caso específico

Entonces, ¿qué es la prueba de existencia?


La Prueba de Existencia (literalmente, prueba de existencia) ayuda a demostrar que un documento, archivo o datos se crearon en una fecha y hora específicas. La mayor solicitud es el registro de patentes. Pero por lo que vi, la mayoría de las veces se aplica en áreas en la intersección de finanzas y TI.

Un ejemplo de mi área de negociación algorítmica: tiene un algoritmo que proporciona aproximadamente el 70% de los pronósticos correctos para las acciones para las próximas 2 semanas. Decide vender sus pronósticos a otros actores del mercado. El punto es pequeño: convencer al comprador de que sus pronósticos son correctos y se hicieron antes de los resultados de la licitación, es decir, garantizar su implementación en un momento específico.

¿Cómo se puede garantizar esto? Derecha, implementar PoE.

Algoritmo de implementación de PoE


Primero necesitas:

  1. Prepare el archivo que desea patentar. (Hice el PDF, pero puedes usar cualquier otro contenedor).
  2. Obtenga el hash del archivo dado (utilicé el formato sha256).

Por si acaso, un hash es una "huella digital" individual de un archivo, lo que garantiza la ausencia (casi completa) de coincidencia con el hash de otro archivo. Cuando reciba el hash del archivo, solo tendrá que generar una transacción en la red blockchain, que indica el hash del documento en el cuerpo de la transacción.

Eso es todo. Con el prólogo terminado. Ahora pasamos a lo más interesante.

(Para mayor claridad, creé un código especial (únicamente para demostración). Puede ver el ejemplo de demostración del servicio aquí ).

Echemos un vistazo más de cerca a cómo funciona la demostración.

Propongo dividir la implementación en 2 partes:

  1. Preparación de un contrato inteligente y una billetera ether.
  2. Generación de transacciones en código Node.js y clave privada.

Vamos en orden:

Parte 1: preparación de un contrato inteligente y billetera Ethereum

PoE se asoció por primera vez con la cadena de bloques de Bitcoin. Pero elegí el ether blockchain y los contratos inteligentes para la implementación. Los contratos inteligentes nos brindan flexibilidad, modularidad y escalabilidad en el futuro, actuando como un almacenamiento para hashes en la cadena de bloques.

Elegí un contrato inteligente simple que puede aceptar un hash con una fecha apropiada, el formato hash => date, también sería excelente tener un método que devuelva la fecha hash cuando se llama. Además, sería bueno tener cuidado de que solo el propietario del contrato pudiera agregar nuevos hashes.

Código de contrato inteligente:

``` pragma solidity 0.5.9; /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } /** * @title HashStore * @dev The contract store the hashes and returns date by hash * Only the owner can add new hashes */ contract HashStore is Ownable { mapping(bytes32 => uint256) private _hashes; event HashAdded(bytes32 hash); function addHash(bytes32 rootHash) external onlyOwner { require(_hashes[rootHash] == 0, "addHash: this hash was already deployed"); _hashes[rootHash] = block.timestamp; emit HashAdded(rootHash); } function getHashTimestamp(bytes32 rootHash) external view returns (uint256) { return _hashes[rootHash]; } } ``` 

Como ya notó, usamos 2 contratos separados: Ownable y HashStore.
El contrato de HashStore es responsable de almacenar los hash y emitir la fecha de hash a pedido. El contrato Ownable es responsable de verificar que el nuevo hash haya sido agregado exclusivamente por el propietario del contrato.

Para agregar un hash, debe llamar al método addHash, pasando el valor sha256 como argumento a nuestro archivo. Si los hashes dentro del contrato coinciden, la transacción será rechazada. Esto filtra la duplicación no deseada de valores con diferentes fechas. Está marcado aquí:

 require(_hashes[rootHash] == 0, "addHash: this hash was already deployed"); 

Podemos obtener la fecha de transacción hash usando el método getHashTimestamp, pasando el hash con la fecha de transacción deseada como argumento. El método getHashTimestamp devuelve la hora en formato UNIX. Puede traducir a cualquier formato más legible.

Entonces, descubrimos el contrato, ahora debe implementarlo en la red. Para hacer esto, necesitamos dos cosas:

  1. dirección ether para interactuar con la cadena de bloques
  2. algo de ether para llamar a métodos y desplegar contratos.

Para no desperdiciar el éter en las pruebas, podemos usar la red de prueba ropsten. Obtenga más información aquí. Esta dirección es el propietario del contrato, ya que fue el iniciador de la implementación y puede agregar nuevos hashes en el futuro. Después de eso, debe guardar esta dirección en un lugar seguro, así como la clave privada de su billetera y la dirección del contrato. Todos necesitaremos esto en el futuro.

Parte 2: generación de transacciones en Node.js y clave privada

Entonces, en este punto ya tenemos una billetera ether, su clave secreta, un contrato en la red de prueba y un hash de datos. Queda por configurar la interacción con la cadena de bloques.

Para hacer esto, usaremos la biblioteca web3.js y crearemos un token para nuestro nodo. Creé el mío usando el servicio infura.io y se parece a esto:

 ropsten.infura.io/v3/YOUR_TOKEN_HERE 

Para el hash, use el paquete sha256. El formato de datos puede ser cualquiera, pero en el ejemplo usamos los datos en JSON.

¿Cómo resolví mi caso usando PoE?


Además de la presencia de PoE, era importante para mí no sobrecargar a los usuarios con el blockchain y la tarifa de transacción, por ejemplo, llamar al método addHash (bytes32 rootHash) cuesta 0.2 finney (0.0002 ETH o $ 0.06 a la tasa de junio de 2019).



Alrededor de 30 conclusiones de la posición de estrategia salieron por día, es decir, nos cuesta $ 2.1. Si el número de usuarios aumenta 100 veces, y la tasa de Ether aumenta, el costo, por supuesto, aumentará.

Decidí mantener un hash en la cadena de bloques por un día. Este hash se generará a partir de los hash de los resultados diarios de la estrategia y sus identificadores internos.



Obtenemos una solución que no es inferior en funcionalidad, sin embargo, mucho más barata. No vemos problemas con la escalabilidad, sin embargo, siempre puede dividir el resultado de una posición de estrategia en varios bloques y guardar varios hashes en la red blockchain. Y un participante puede verificar fácilmente un hash separado utilizando otros hashes vecinos.

Al final


Para la verificación, creamos una plantilla especial, que está disponible como un cuaderno Jupyter. En cualquier momento, el usuario puede descargar la plantilla, regenerar todo y verificar que todas las posiciones sean realmente las que se crearon y guardaron en el pasado.

 ```   : 1.    API       1.     2.    API  ,         3.          ,              .          . ``` 

Source: https://habr.com/ru/post/459046/


All Articles