¿Cómo escribir un contrato inteligente para WebAssembly en una red Ontology? Parte 2: C ++

imagen

En este artículo, veremos dos ejemplos de cómo escribir un contrato inteligente en C ++ usando WASM basado en la red blockchain de Ontology. Hoy, después de varios meses de operación estable en modo de prueba, Ontology lanzó WASM en la red principal, lo que permite transferir contratos dApp con lógica empresarial compleja a la cadena de bloques sin dolor y a un costo menor, lo que enriquece significativamente el ecosistema dApp.

Ontology Wasm también admite la creación de contratos inteligentes en el lenguaje Rust, puede leer sobre esto aquí .

A continuación hay dos ejemplos de un contrato inteligente: primero, escriba "¡Hola, mundo!" y luego cree un sobre de dinero virtual que se pueda enviar a un amigo como regalo.

Desarrollo de un contrato WASM usando C ++



Ejemplo 1. Hola mundo


Comencemos con Hello World:

#include<ontiolib/ontio.hpp> #include<stdio.h> using namespace ontio; class hello:public contract { public: using contract::contract: void sayHello(){ printf("hello world!"); } }; ONTIO_DISPATCH(hello, (sayHello)); 

Creación de contrato


El compilador CDT de Ontology Wasm contiene un punto de entrada y parámetros de análisis, por lo que los desarrolladores no necesitan redefinir los métodos de entrada. Además, para escribir la lógica del servicio, puede llamar a los métodos API del contrato inteligente.

 ONTIO_DISPATCH(hello, (sayHello)); 

En el ejemplo anterior, solo admitimos sayHello hasta ahora:

 printf("hello world!"); 

"¡Hola mundo!" se mostrará en el registro del nodo de depuración. Al escribir un contrato inteligente directamente, printf solo se puede usar para depurar, ya que el contrato inteligente en sí contiene más comandos funcionales.

API de contrato inteligente


Ontology Wasm proporciona las siguientes API para interactuar con la cadena de bloques del servidor:

imagen

Ejemplo 2: sobre de dinero


Ahora veamos un ejemplo más complejo utilizando la API de contrato inteligente de Wasm.

En este ejemplo, escribiremos un sobre de dinero virtual, un análogo del sobre rojo (hongbao) es una característica popular del mensajero chino Wechat, que le permite enviar dinero a amigos en un chat. El usuario recibe un mensaje en forma de sobre rojo, lo abre y el dinero se acredita automáticamente al saldo de la cuenta.

Como parte de un contrato inteligente, los usuarios pueden usar este contrato para enviar tokens ONT, ONG u OEP-4 usando sobres de dinero virtual a sus amigos, quienes luego pueden transferir tokens a sus billeteras blockchain.

Preparando para crear un contrato


Primero, cree el archivo del contrato fuente y asígnele el nombre redEnvelope.cpp. A continuación, necesitamos tres API para este contrato:

  • createRedEnvelope : crea un sobre de dinero
  • queryEnvelope : solicita información sobre
  • ClaimEnvelope : abra un sobre y reciba dinero

 #include<ontiolib/ontio.hpp> using namespace ontio; class redEnvlope: public contract{ } ONTIO_DISPATCH(redEnvlope, (createRedEnvlope)(queryEnvlope)(claimEnvelope)); 

Ahora necesitamos guardar la clave-valor. En un contrato inteligente, los datos se almacenan en el contexto del contrato como un valor clave, y necesitamos agregar un prefijo a los datos CLAVE para una solicitud posterior.

A continuación definimos tres prefijos que utilizaremos:

 std::string rePrefix = "RE_PREFIX_"; std::string sentPrefix = "SENT_COUNT_"; std::string claimPrefix = "CLAIM_PREFIX_"; 

Dado que el contrato es compatible con los tokens Ontology: ONT y ONG, podemos determinar la dirección de su contrato por adelantado. A diferencia de un contrato inteligente estándar, la dirección del contrato de Ontology es fija y no se deriva del hash del contrato.

 address ONTAddress = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; address ONGAddress = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2}; 

A continuación, debe guardar información sobre el token utilizado en el contrato: la dirección del token del contrato, la cantidad total del sobre y la cantidad de sobres.

 struct receiveRecord{ address account; //User address asset amount; //Received amount ONTLIB_SERIALIZE(receiveRecord,(account)(amount)) }; struct envlopeStruct{ address tokenAddress; //Token asset address asset totalAmount; //Total amount asset totalPackageCount; //Total number of red envelope asset remainAmount; //Remaining amount asset remainPackageCount; //Remaining number of red envelope std::vector<struct receiveRecord> records; //Received records ONTLIB_SERIALIZE( envlopeStruct, (tokenAddress)(totalAmount)(totalPackageCount)(remainAmount)(remainPackageCount)(records) ) }; 

La siguiente es la operación de macro definida por Ontology Wasm CDT, que se utiliza para la serialización antes de la estructuración de datos.

 ONTLIB_SERIALIZE(receiveRecord,(account)(amount)) 

Crear un sobre


Ahora que hemos completado los preparativos necesarios, comenzaremos el desarrollo de la lógica API.

1. Al crear un sobre de dinero, es necesario indicar la dirección del propietario, el número y la cantidad de sobres, así como la dirección del token:

 bool createRedEnvlope(address owner,asset packcount, asset amount,address tokenAddr ){ return true; } 

2. Verifique la firma del propietario; de lo contrario, revertiremos (revertiremos la transacción) y saldremos:

 ontio_assert(check_witness(owner),"checkwitness failed"); 

Nota : ontio_assert (expr, errormsg): false expr devuelve un error y sale del contrato.

3. Si se utiliza un token ONT en un sobre, es importante recordar que ONT no está fragmentado (al menos 1 ONT). Entonces, la cantidad total del sobre de dinero debe ser mayor o igual que la cantidad de tokens para garantizar que haya al menos 1 ONT en cada sobre:

 if (isONTToken(tokenAddr)){ ontio_assert(amount >= packcount,"ont amount should greater than packcount"); } 

4. A continuación, determinamos para el titular del sobre el número total de sobres de dinero que envía:

 key sentkey = make_key(sentPrefix,owner.tohexstring()); asset sentcount = 0; storage_get(sentkey,sentcount); sentcount += 1; storage_put(sentkey,sentcount); 

5. Genere el hash del sobre: ​​el identificador que marca este sobre:

 H256 hash ; hash256(make_key(owner,sentcount),hash) ; key rekey = make_key(rePrefix,hash256ToHexstring(hash)); 

6. Traduciremos los tokens al contrato. Descubrimos la dirección del contrato que se está ejecutando actualmente usando el comando self_address (), luego transferiremos la cantidad asignada de tokens al contrato en función del tipo de tokens:

 address selfaddr = self_address(); if (isONTToken(tokenAddr)){ bool result = ont::transfer(owner,selfaddr ,amount); ontio_assert(result,"transfer native token failed!"); }else if (isONGToken(tokenAddr)){ bool result = ong::transfer(owner,selfaddr ,amount); ontio_assert(result,"transfer native token failed!"); }else{ std::vector<char> params = pack(std::string("transfer"),owner,selfaddr,amount); bool res; call_contract(tokenAddr,params, res ); ontio_assert(res,"transfer oep4 token failed!"); } 

Nota 1: para ONT y ONG, Ontology Wasm CDT proporciona la API ont :: transfer para transferir tokens; Los tokens OEP-4 deben enviarse utilizando el método convencional de llamada de contrato cruzado.

Nota 2: como una dirección de billetera normal, la dirección del contrato puede aceptar cualquier tipo de token. Sin embargo, la dirección del contrato se genera mediante un hash binario compilado y, por lo tanto, no tiene una clave privada correspondiente y no puede usar tokens de contrato. Si no ha configurado una clave privada, no podrá administrar estos tokens.

7. Guarde la información sobre el contrato en el almacén de datos:

 struct envlopeStruct es ; es.tokenAddress = tokenAddr; es.totalAmount = amount; es.totalPackageCount = packcount; es.remainAmount = amount; es.remainPackageCount = packcount; es.records = {}; storage_put(rekey, es); 

8. Envíe una notificación sobre la creación del sobre. Este es un proceso asincrónico para invocar un contrato inteligente, el contrato también enviará una notificación del resultado de la ejecución. El autor del contrato puede determinar el formato de ejecución.

 char buffer [100]; sprintf(buffer, "{\"states\":[\"%s\", \"%s\", \"%s\"]}","createEnvlope",owner.tohexstring().c_str(),hash256ToHexstring(hash).c_str()); notify(buffer); return true; 

Hurra, el sobre del dinero está casi listo. Ahora veamos cómo solicitar información sobre.

Sobre de consulta (consulta


La lógica de la solicitud es bastante simple, solo necesita obtener la información y el formato del almacén de datos y luego generar:

 std::string queryEnvlope(std::string hash){ key rekey = make_key(rePrefix,hash); struct envlopeStruct es; storage_get(rekey,es); return formatEnvlope(es); } 

Nota: para operaciones de contrato inteligente de solo lectura (por ejemplo, consulta), puede verificar el resultado a través de pre-exec. A diferencia de una llamada de contrato regular, pre-exec no requiere una firma de billetera y, por lo tanto, no requiere una comisión en ONG. Una vez hecho esto, otros usuarios ahora pueden solicitar el sobre si tienen un hash de sobre (ID de sobre).

Recibiendo Sobre


En esta etapa, ya hemos transferido los tokens a un contrato inteligente, ahora, para que tus amigos puedan reclamar con éxito una parte del sobre, debes enviarles el identificador del sobre (hash).

1. Para recibir un sobre, debe ingresar la dirección de su cuenta y el hash del sobre:

 bool claimEnvlope(address account, std::string hash){ return true; } 

2. Luego, el contrato verificará la firma de su cuenta para asegurarse de que usted sea el propietario. Cada cuenta puede solicitar un sobre solo una vez:

 ontio_assert(check_witness(account),"checkwitness failed"); key claimkey = make_key(claimPrefix,hash,account); asset claimed = 0 ; storage_get(claimkey,claimed); ontio_assert(claimed == 0,"you have claimed this envlope!"); 

3. Compruebe si el sobre se recibe de acuerdo con la información hash recibida de la tienda:

 key rekey = make_key(rePrefix,hash); struct envlopeStruct es; storage_get(rekey,es); ontio_assert(es.remainAmount > 0, "the envlope has been claimed over!"); ontio_assert(es.remainPackageCount > 0, "the envlope has been claimed over!"); 

4. Crear un registro de reclamo:

 struct receiveRecord record ; record.account = account; asset claimAmount = 0; 

5. Cálculo del importe de cada sobre solicitante.
Para el último participante, se determina el monto del resto, para cualquier otro, el monto declarado se determina mediante un número aleatorio calculado por el hash del bloque actual y la información del sobre actual:

 if (es.remainPackageCount == 1){ claimAmount = es.remainAmount; record.amount = claimAmount; }else{ H256 random = current_blockhash() ; char part[8]; memcpy(part,&random,8); uint64_t random_num = *(uint64_t*)part; uint32_t percent = random_num % 100 + 1; claimAmount = es.remainAmount * percent / 100; //ont case if (claimAmount == 0){ claimAmount = 1; }else if(isONTToken(es.tokenAddress)){ if ( (es.remainAmount - claimAmount) < (es.remainPackageCount - 1)){ claimAmount = es.remainAmount - es.remainPackageCount + 1; } } record.amount = claimAmount; } es.remainAmount -= claimAmount; es.remainPackageCount -= 1; es.records.push_back(record); 

6. Fondos de crédito


La cantidad correspondiente de tokens se transfiere a la cuenta de los solicitantes de sobres de acuerdo con el resultado del cálculo:

 address selfaddr = self_address(); if (isONTToken(es.tokenAddress)){ bool result = ont::transfer(selfaddr,account ,claimAmount); ontio_assert(result,"transfer ont token failed!"); } else if (isONGToken(es.tokenAddress)){ bool result = ong::transfer(selfaddr,account ,claimAmount); ontio_assert(result,"transfer ong token failed!"); } else{ std::vector<char> params = pack(std::string("transfer"),selfaddr,account,claimAmount); bool res = false; call_contract(es.tokenAddress,params, res ); ontio_assert(res,"transfer oep4 token failed!"); } 

7. Anotaremos información sobre la recepción de fondos e información actualizada sobre el sobre en la bóveda y enviaremos una notificación sobre el cumplimiento del contrato:

 storage_put(claimkey,claimAmount); storage_put(rekey,es); char buffer [100]; std::sprintf(buffer, "{\"states\":[\"%s\",\"%s\",\"%s\",\"%lld\"]}","claimEnvlope",hash.c_str(),account.tohexstring().c_str(),claimAmount); notify(buffer); return true; 

Como se mencionó anteriormente, este contrato puede enviar tokens desde el contrato a través de la API ClaimEnvelope. Esto garantiza la seguridad de los tokens mientras están en el sobre, ya que nadie puede retirar activos sin cumplir los requisitos necesarios.

Hecho Escribiste tu primer contrato inteligente. El código completo del contrato se puede encontrar en GitHub aquí .

Prueba de contrato


Hay dos formas de verificar un contrato:

  1. Use CLI
  2. Use el SDK de Golang

Conclusión


En este artículo, hablamos sobre cómo escribir un contrato inteligente para Ontolgy Wasm usando la API blockchain. Queda por resolver el problema de privacidad para que el contrato inteligente se convierta en un producto completo. En esta etapa del código, cualquiera puede obtener un hash del sobre rojo al rastrear los registros del contrato, lo que significa que cualquiera puede reclamar una parte del sobre. Este problema se puede resolver simplemente: definimos una lista de cuentas que pueden solicitar un sobre cuando se crea. Si lo desea, esta función también se puede probar.



Obtenga una subvención de Ontología para el desarrollo de dApp de $ 20,000

Solicite el Programa de Talento Estudiantil de Ontología



¿Eres desarrollador? Únete a nuestra comunidad tecnológica en Discord . Además, eche un vistazo al Centro de desarrolladores en nuestro sitio web, donde puede encontrar herramientas para desarrolladores, documentación y mucho más.

Ontología


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


All Articles