Qtum lanza QIP-6, reduciendo en gran medida los costos de desarrollo a través de contratos precompilados

 

antecedentes

 

La función ecrecover en Ethereum se puede utilizar para obtener la dirección que firmó un mensaje. Esto es útil para probar que un mensaje o un dato fue firmado por una cuenta específica (en lugar de manipulado). Pero Qtum no usa el modelo de cuenta de Ethereum, sino que usa el modelo UTXO de Bitcoin. El algoritmo de dirección también es diferente de Ethereum, por lo que esta función no se aplica a Qtum. En algunos casos en los que es necesario verificar la información de la fuente de la firma, los desarrolladores de Qtum no pueden completar fácilmente esta verificación en un contrato inteligente. En su lugar, deben implementarse completamente en el contrato o llamar a un contrato que obtenga la clave pública del firmante a partir de la firma y el mensaje. Esto provoca unos gastos generales muy elevados, lo que a su vez hace que el coste de la llamada del contrato correspondiente sea muy elevado.

 

 

Detalles del problema

 

ecrecover acepta el hash de un mensaje y la firma del mensaje, luego calcula la clave pública correspondiente a la clave privada firmada y convierte la clave pública al formato de dirección Ethereum. Sin embargo, el algoritmo de dirección de Ethereum es diferente al de Qtum, y lo que ecrecover devuelve es el resultado del hash de la clave pública. Este proceso es irreversible, por lo que esta función no se puede utilizar en Qtum.

En Ethereum, el método de cálculo de la dirección es el siguiente:

keccak256 (clave pública)

 

En Qtum, el método de cálculo de la dirección es el mismo que el de Bitcoin y se utiliza el siguiente método de cálculo:

ripemd160 (sha256 (clave de publicación))

 

En el contrato de Qtum, msg.sender es una dirección de Qtum. Dado que cada paso de conversión de una clave pública a una dirección es irreversible, la dirección de Ethereum devuelta por ecrecover no se puede comparar con la dirección de Qtum en msg.sender. Sin embargo, el contrato inteligente de Qtum existente no proporciona ninguna función para obtener la dirección de Qtum de la firma del mensaje, lo que hace que los desarrolladores de contratos inteligentes de Qtum desarrollen o usen bibliotecas relacionadas con Secp256k1 para calcular la clave pública de firma y la dirección, lo que resulta en una mayor Costos de cálculo y tarifas contractuales más altas.

 

Otro detalle que debe tenerse en cuenta es que existen algunas diferencias sutiles entre la implementación del algoritmo de firma de mensajes de Bitcoin utilizado por Qtum y el algoritmo de firma de mensajes de Ethereum:

La firma Ethereum se compone del siguiente formato:

[r] [s] [v]

 

La firma de Qtum es:

[v] [r] [s]

 

Donde v es el id de recuperación, r es la coordenada X de un punto R en la curva elíptica y s es la coordenada Y de este punto R. Las diferencias anteriores conducen a diferentes detalles de implementación del algoritmo de recuperación de Qtum y Ethereum.

 

 

Solución de QIP-6

 

Al agregar un contrato precompilado a la máquina virtual Qtum, proporciona una interfaz para llamar al código de recuperación en el código central de Qtum. Los desarrolladores de contratos inteligentes solo necesitan escribir una función simple o dos para obtener la dirección del firmante del mensaje firmado. La interfaz del contrato precompilado recién agregado es consistente con ecrecover.

 

¿Qué es un contrato precompilado?

 

Los contratos precompilados son una solución de compromiso adoptada por EVM con el fin de proporcionar algunas funciones de biblioteca más complejas que no son adecuadas para escribir código de operación (se utilizan principalmente para cálculos complejos como cifrado y hash). Debido a que se implementa con código de bajo nivel y tiene una velocidad de ejecución rápida, consume menos para los desarrolladores que el uso directo de funciones que se ejecutan en el EVM. Ethereum utiliza contratos precompilados para proporcionar algunas operaciones de uso común y más engorrosas, como sha256, ripemd160hash, etc.

 

Implementación de contratos precompilados

 

El código central del contrato precompilado es implementado por la capa inferior de la máquina virtual (C ++), que proporciona una interfaz para llamadas de contratos inteligentes al registrarse en una dirección fija designada manualmente durante la inicialización de la máquina virtual.

 

Uso de contratos precompilados

 

Un método de llamada típico:

 

 

 

ensamblado { 
if iszero (call (gasLimit, contractAddress, value, input, inputLength, output, outputLength)) {revert 
(0, 0) 
} 
}

 

En la nueva versión de la máquina virtual, staticcall también se puede utilizar:

 

 

 

ensamblaje {éxito: = staticcall (gasLimit, contractAddress, input, inputLength, output, outputLength)}

 

Entre ellos, contractAddress es la dirección del contrato precompilado que se llamará. La dirección de btc_ecrecover recién agregada por Qtum esta vez es 0x85. input es la lista de parámetros del contrato de llamada. El valor de retorno de esta llamada representa si la llamada fue exitosa, 1 significa éxito y 0 significa fracaso. Los datos devueltos se escribirán en la salida.

Veamos un ejemplo:

 

 

 

solidez del pragma ^ 0.5.0;

/ **
* @title Operaciones de firma de curva elíptica
* @dev Basado en

https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d
* TODO Elimine esta biblioteca una vez que la solidez admita pasar una firma a ecrecover.
* Ver https://github.com/ethereum/solidity/issues/864
* /

library ECDSA { / ** * @dev Recuperar la dirección del firmante de un mensaje usando su firma. * @param hash bytes32 mensaje, el hash es el mensaje firmado. Lo que se recupera es la dirección del firmante. * @param firma bytes firma, la firma se genera usando web3.eth.sign () * / función recuperar (bytes32 hash, bytes firma de memoria) vista interna devuelve (dirección) { // Verifique la longitud de la firma if (signature.length! = 65) { return (dirección (0));










}

// Divide la firma en variables
r, syv bytes32 r;
bytes32 s;
uint8 v;

// ecrecover toma los parámetros de la firma, y ​​la única forma de obtenerlos
// actualmente es usando ensamblador.
// solhint-disable-next-line no-inline-assembly
assembly { v: = byte (0, mload (add (signature, 0x20))) r: = mload (add (signature, 0x21)) s: = mload ( add (signature, 0x41)) } // EIP-2 aún permite la maleabilidad de la firma para ecrecover (). Elimine esta posibilidad y haga que la firma // sea única. Apéndice F en el documento Ethereum Yellow (https://ethereum.github.io/yellowpaper/paper.pdf), define







// el rango válido para s en (281): 0 <s <secp256k1n ÷ 2 + 1, y para v en (282): v ∈ {27, 28}. La mayoría de
// firmas de las bibliotecas actuales generan una firma única con un valor s en la mitad inferior del orden.
//
// Si su biblioteca genera firmas maleables, como valores s en el rango superior, calcule un nuevo valor s
// con 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 y voltee v de 27 a 28 o
// viceversa. Si su biblioteca también genera firmas con 0/1 para v en lugar de 27/28, agregue 27 av para aceptar
// estas firmas maleables también.
if (uint256 (s)> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { dirección de retorno (0); }



// Admite tanto comprimido como sin comprimir
if (v! = 27 && v! = 28 && v! = 31 && v! = 32) { return address (0); } // Si la firma es válida (y no maleable), devuelve la dirección del firmante return btc_ecrecover (hash, v, r, s); } función btc_ecrecover (bytes32 msgh, uint8 v, bytes32 r, bytes32 s) vista pública devuelve (dirección) { uint256 [4] entrada de memoria; entrada [0] = uint256 (msgh); entrada [1] = v; entrada [2] = uint256 (r); entrada [3] = uint256 (s); uint256 [1] recuperación de memoria; uint256 éxito; ensamblado { éxito: = llamada estática (no (0), 0x85, entrada, 0x80, retval, 32) } if (éxito! = 1) {





















dirección de retorno (0);
}

dirección de retorno (retval [0]);
}
}

 

 

 

En el ejemplo anterior, simplemente llame a la función btc_ecrecover para obtener la dirección del firmante del mensaje. Para simplificar la entrada, también se encapsula una función de recuperación en el ejemplo, para que el desarrollador pueda completar la llamada del contrato pasando la firma original.

 

A continuación, no usamos contratos precompilados, pero usamos completamente código de solidez para implementar la función btc_ecrecover. El código se implementa de la siguiente manera:

 

 

 

solidez del pragma ^ 0.4.26;
importar {ECCMath} de "github.com/androlo/standard-contracts/contracts/src/crypto/ECCMath.sol";
importar {Secp256k1} de "github.com/androlo/standard-contracts/contracts/src/crypto/Secp256k1.sol";

biblioteca ECDSA { uint256 constante p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f; uint256 constante n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141; uint256 constante gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798; uint256 constante gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8; función de recuperación (bytes32 hash, bytes de firma de memoria) devuelve la vista interna (dirección) { if (signature.length! = 65) {







return (dirección (0));
}

bytes32 r;
bytes32 s;
uint8 v;

ensamblado { v: = byte (0, mload (agregar (firma, 0x20))) r: = mload (agregar (firma, 0x21)) s: = mload (agregar (firma, 0x41)) } if (uint256 (s) > n / 2) { dirección de retorno (0); } if (v! = 27 && v! = 28 && v! = 31 && v! = 32) { dirección de retorno (0); } return btc_ecrecover (hash, v, r, s); } función btc_ecrecover (bytes32 msgh, uint8 v, bytes32 r, bytes32 s) vista pública devuelve (dirección) { uint i = 0; uint256 rr = uint256 (r); uint256 ss = uint256 (s); bool isYOdd = ((v - 27) & 1)! = 0;






















bool isSecondKey = ((v - 27) & 2)! = 0;
bool está comprimido = ((v - 27) & 4)! = 0;
if (rr> = p% n && isSecondKey) { dirección de retorno (0); } uint256 [3] memoria P = _getPoint (uint256 (msgh), rr, ss, isYOdd, isSecondKey); if (P [2] == 0) { dirección de retorno (0); } ECCMath.toZ1 (P, p); bytes de memoria publicKey; if (isCompressed) { publicKey = nuevos bytes (33); publicKey [0] = byte (P [1]% 2 == 0? 2: 3); para (i = 0; i <32; ++ i) { publicKey [32 - i] = byte ((P [0] >> (8 * i)) & 0xff); } } else { publicKey = nuevos bytes (65); publicKey [0] = 4; para (i = 0; i <32; ++ i) {




















publicKey [32 - i] = byte ((P [0] >> (8 * i)) & 0xff);
publicKey [64 - i] = byte ((P [1] >> (8 * i)) & 0xff);
}
}
dirección de retorno (ripemd160 (sha256 (publicKey)));
}

función _getPoint (uint256 msgh, uint256 r, uint256 s, bool isYOdd, bool isSecondKey) devuelve la vista interna (uint256 [3] memoria) { uint256 rx = isSecondKey? r + n: r; uint256 ry = ECCMath.expmod (ECCMath.expmod (rx, 3, p) + 7, p / 4 + 1, p); if (isYOdd! = (ry% 2 == 1)) { ry = p - ry; } uint256 invR = ECCMath.invmod (r, n); return Secp256k1._add ( Secp256k1._mul (n - mulmod (msgh, invR, n), [gx, gy]), Secp256k1._mul (mulmod (s, invR, n), [rx, ry]) ); }













}

 

 

 

Implementamos los dos contratos anteriores en la cadena de prueba, las direcciones son las siguientes:

  • Contrato precompilado: 21ea1d8376d1820d7091084a76f380143b59aaf8

  • Implementación de solidez: 4fdff1b4bde5edf13360ff0946518a01115ce818

 

Usar dirección

qQqip6i2e2buCZZNdqMw4VNpaYpnLm4JAx firma el mensaje btc_ecrecover test, obtenemos los parámetros de llamada de btc_ecrecover:

 

 

bytes32 msgh = 0xdfa80e3294fd8806ab908904403db376b3dd35c6356ab2d3b884db4f6ec5e93d 
uint8 v = 0x20 
bytes32 r = 0xca08c0813407de3a78053c976462eacbde3fd69843e21acf8dd636149bf4b753 
bytes32 s = 0x0731bce3ed9b489da0165af79759c1d586ef8fe53b3aab95fcab68d01ed6f156

 

 

Los resultados de la convocatoria de los dos contratos son los siguientes:

 

1. Contrato precompilado

 

 

 

callcontract 21ea1d8376d1820d7091084a76f380143b59aaf8 69bc0963dfa80e3294fd8806ab908904403db376b3dd35c6356ab2d3b884db4f6ec5e93d0000000000000000000000000000000000000000000000000000000000000020ca08c0813407de3a78053c976462eacbde3fd69843e21acf8dd636149bf4b7530731bce3ed9b489da0165af79759c1d586ef8fe53b3aab95fcab68d01ed6f156

{ "dirección": "21ea1d8376d1820d7091084a76f380143b59aaf8", "executionResult": { "gasUsed": 32688, "exceptuados": "Ninguno", "newAddress": "21ea1d8376d1820d7091084a76f380143b59aaf8", "salida": "0000000000000000000000004fdff1b4bde5edf13360ff0946518a01115ce818", "codeDeposit" : 0, "gasRefunded": 0, "depositSize ": 0, " gasForDeposit ": 0, " exceptedMessage ":" " },












"transactionReceipt": { "stateRoot": "5d9e1ad1b5d09e9e7c41d09078434488927366adf8ebf5a0049bb99610a817f1", "gasUsed": 32.688, "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "log": [] } }





 

 

 

2.implementación de solidez

 

 

 

d3764a0b7fbbe2e39ee4adc3908b5b5dbea22c14 callcontract 69bc0963dfa80e3294fd8806ab908904403db376b3dd35c6356ab2d3b884db4f6ec5e93d0000000000000000000000000000000000000000000000000000000000000020ca08c0813407de3a78053c976462eacbde3fd69843e21acf8dd636149bf4b7530731bce3ed9b489da0165af79759c1d586ef8fe53b3aab95fcab68d01ed6f156

{ "dirección": "d3764a0b7fbbe2e39ee4adc3908b5b5dbea22c14", "executionResult": { "gasUsed": 886077, "exceptuados": "Ninguno", "newAddress": "d3764a0b7fbbe2e39ee4adc3908b5b5dbea22c14", "salida": "0000000000000000000000004fdff1b4bde5edf13360ff0946518a01115ce818", "codeDeposit" : 0, "gasRefunded": 0, "depositSize ": 0, " gasForDeposit ": 0, " exceptedMessage ":" " },












"transactionReceipt": { "stateRoot": "5d9e1ad1b5d09e9e7c41d09078434488927366adf8ebf5a0049bb99610a817f1", "gasUsed": 886077, "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "log": [] } }





 

 

 

Se puede ver que llamar a btc_ecrecover en el contrato precompilado requiere gas 32688, mientras que la implementación completa de solidez requiere gas 886077. El costo del gas de la implementación del contrato precompilado es mucho menor que la implementación de la solidez.

 

 

Impacto de QIP-6

 

QIP-6 reduce en gran medida el costo de desarrollo de los desarrolladores de contratos inteligentes. Desde la perspectiva de llamar al contrato, si se utiliza la solidez para implementar la recuperación en el contrato, su uso de gas supera con creces la función btc_ecrecover. Por lo tanto, el costo de la llamada del contrato de usar btc_ecrecover para obtener la dirección de la firma del mensaje también se reduce considerablemente. Además, QIP-6 también hace que el sistema de contrato inteligente de Qtum sea más completo.

 

Por otro lado, QIP-6 no ha modificado el ecrecover original, manteniendo la compatibilidad de Qtum y Ethereum, y teóricamente no traerá ningún riesgo.

 

Supongo que te gusta

Origin blog.csdn.net/weixin_42667079/article/details/101267471
Recomendado
Clasificación