Use python y solidity para calcular el selector de función de contrato inteligente de Ethereum y el valor constante de la interfaz de soporte respectivamente

1. ¿Cuál es el valor constante del selector de funciones y la interfaz de soporte?

       Cuando examinamos el contrato de muestra (plantilla) ERC721 escrito por OpenZeppelin, veremos este fragmento de código:

/*
 *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
 *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
 *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
 *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
 *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
 *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
 *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
 *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
 *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
 *
 *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
 *        0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
 */
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;

constructor () public {
    
    
    // register the supported interfaces to conform to ERC721 via ERC165
    _registerInterface(_INTERFACE_ID_ERC721);
}

       ¿Hay lectores tan curiosos como yo acerca de lo que significa? Representa un modelo de contrato debe apoyar ERC721 interfaz inteligente (alcanzar), a saber: 'balanceOf(address)', 'ownerOf(uint256)')... 'safeTransferFrom(address,address,uint256,bytes)').

       A esto se le balanceOf(address)llama la firma de la función. Los lectores que han estudiado la sobrecarga de funciones saben que la sobrecarga de funciones se distingue por nombres de funciones y listas de parámetros, y no incluye parámetros de retorno. Por lo tanto, la firma aquí es solo una lista de nombres de funciones y tipos de parámetros.Los parámetros están separados por comas y no contienen espacios adicionales.

bytes4(keccak256('balanceOf(address)'))El valor calculado        con el método se llama selector de función, que son los primeros cuatro bytes de los datos de la llamada del contrato inteligente. El contrato inteligente utiliza este selector para determinar qué función se llama. El método de cálculo del selector se enumera en los comentarios.

       El contrato inteligente estándar ERC721 debe admitir las 9 interfaces anteriores, pero no se puede verificar una por una (demasiado ineficiente). Por lo tanto, los 9 selectores de funciones son XORed (tenga en cuenta que el orden de los XOR de la interfaz no afecta el resultado), y bytes4se obtiene un valor constante de un tipo para representar la serie de interfaces. Finalmente, registre este valor constante en el constructor y está OK.

Recordatorio cálido:
       En el comentario anterior, falta un número 5en0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde ==esta línea0xe985e9c, que debería pertenecer a un error tipográfico de OpenZeppelin. No se modifica aquí para mantenerlo como está. Espero que los lectores noten esto al verificar los comentarios, y no tengan un 5, por lo que no se obtendrá el resultado correcto.

2. ¿En qué circunstancias debemos calcular manualmente el selector de funciones y los valores constantes de la interfaz de soporte?

       En circunstancias normales, no necesitamos calcular selectores de funciones ni admitir valores de interfaz. Pero cuando desee agregar una interfaz (serie) y desee que el contrato indique que admite o no esta interfaz (serie), debe calcular manualmente los valores constantes del selector de funciones y la interfaz de soporte. Nota: Cuando solo hay una interfaz (función), el valor constante de la interfaz de soporte es el selector de función. Demos un ejemplo de aplicación práctica.

       Alpha Wallet ( https://alphawallet.com/ ) Al mostrar tokens ERC721, para obtener todos los ID de token de los usuarios a la vez (el ERC721 estándar no proporciona esta interfaz, consulte la nota en la sección 1), hemos agregado un getBalancesmétodo por nosotros mismos :

function getBalances(address owner) public view returns(uint256[] memory) {
    
    
    return balances[owner];
}

       Por lo tanto, calcula el selector de la función como el valor de la constante de soporte (solo se agrega una función, por lo que el valor de la constante de soporte es el selector de función y las funciones múltiples son el selector XOR).

/* bytes4(keccak256('getBalances(address)')) == 0xc84aae17 */
bytes4 private constant _INTERFACE_ID_HONOR_BALANCES = 0xc84aae17;

constructor (string memory name, string memory symbol) ERC721Metadata(name,symbol) public {
    
    
    _registerInterface(_INTERFACE_ID_HONOR_BALANCES);
}

       Donde _INTERFACE_ID_HONOR_BALANCESla constante es un nombre personalizado, pero el valor se basa en bytes4(keccak256('getBalances(address)'))calculado. A continuación usamos Solidity y Python para el cálculo y la implementación, el método es muy simple.

Tres, usa el cálculo de solidez

       Solidity es diferente de otros lenguajes de programación. No se ejecuta después de la interpretación ni se ejecuta después de la compilación. Solo se puede escribir como un contrato inteligente y se puede implementar en Ethereum para que todos puedan llamar. Por lo tanto, necesitamos escribir un contrato inteligente simple, el código es el siguiente:

pragma solidity ^ 0.5 .0;

contract CalSelector {
    
    
    /**
     * 给定一个函数signature,如 'getSvg(uint256)',计算出它的选择器,也就是调用数据最开始的4个字节
     * 该选择器同时也可用于标明合约支持的接口,如alpha钱包对ERC721标准增加的getBalances接口
     * bytes4(keccak256('getBalances(address)')) == 0xc84aae17
     */
    function getSelector(string memory signature) public pure returns(bytes4) {
    
    
        return bytes4(keccak256(bytes(signature)));
    }

    /**
     * 用来计算合约支持的一系列接口的常量值,计算方法是将所有支持接口的选择器相异或
     * 例如 ERC721元数据扩展接口
     * bytes4(keccak256('name()')) == 0x06fdde03
     * bytes4(keccak256('symbol()')) == 0x95d89b41
     * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
     *
     * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
     */
    function getSupportedInterface(bytes4[] memory selectors) public pure returns(bytes4) {
    
    
        bytes4 result = 0x00000000;
        for (uint i = 0; i < selectors.length; i++) {
    
    
            result = result ^ selectors[i];
        }
        return result;
    }
}

       El contrato es muy simple, solo hay dos funciones, la primera función usa el método en los comentarios para calcular el selector de funciones. Tenga en cuenta que, por conveniencia, el tipo de parámetro de entrada es una cadena, que debe convertirse a un bytes4tipo antes del cálculo . El segundo parámetro de entrada de función son todos los selectores de función. El bytes4 result = 0x00000000;código aquí es porque 0 XOR es el valor en sí mismo y 4 bytes son solo 8 dígitos hexadecimales. Tenga en cuenta que los valores devueltos por estas dos funciones son de todos los bytes4tipos.

       El proceso de implementación del contrato no se presentará en este artículo, omítalo primero, vamos a presentar brevemente la llamada del contrato.

Cuarto, use Python para conectar contratos inteligentes en Ethereum

       Para usar Python para conectarse a contratos inteligentes de Ethereum, debe usar web3.pyesta biblioteca. Instálelo primero:

$ pip install web3

       Nos infuraconectamos a Ethereum a través de nodos, por lo que necesita uno más INFURA_PROJECT_ID. Luego configúrelo en la variable de entorno:

$ export WEB3_INFURA_PROJECT_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

       Pero INFURA_PROJECT_IDno importa si no tiene esto . Después de la prueba, se descubre que puede conectarse al contrato inteligente sin configurar la variable de entorno temporalmente. Si está asegurado o descubre que el siguiente código no se está ejecutando correctamente, aún necesita ir al sitio web de infura para crear un nuevo proyecto y obtener su propio project_id.

       Primero usamos Python para construir un objeto para conectarnos al contrato inteligente de Ethereum y crear uno nuevo en el directorio de trabajo contract.py. El código es el siguiente:

# 连接一个Kovan测试网上的用来计算支持接口常量值的智能合约
from web3.auto.infura.kovan import w3


# 合约ABI。注意,这里所有的true和false要替换成python的True和False
contract_abi = [
  {
    
    
    "constant": True,
    "inputs": [
      {
    
    
        "internalType": "string",
        "name": "signature",
        "type": "string"
      }
    ],
    "name": "getSelector",
    "outputs": [
      {
    
    
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "payable": False,
    "stateMutability": "pure",
    "type": "function"
  },
  {
    
    
    "constant": True,
    "inputs": [
      {
    
    
        "internalType": "bytes4[]",
        "name": "selectors",
        "type": "bytes4[]"
      }
    ],
    "name": "getSupportedInterface",
    "outputs": [
      {
    
    
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "payable": False,
    "stateMutability": "pure",
    "type": "function"
  }
]
# Kovan测试网上合约地址
contract_address = '0x07d74Cf0Ce4A1b10Ece066725DB1731515d62b76'
# 构造合约对象
CalSelector = w3.eth.contract(address=contract_address,abi=contract_abi)

       La construcción de un objeto de contrato requiere la ABI y la dirección del contrato. Basado en el principio de gratis, CalSelectorimplementamos el contrato inteligente en la Sección 3 de este artículo en la red de prueba kovan.

       Cabe señalar que debido a que el contrato es simple y el ABI es pequeño, está escrito directamente en el código y, por lo general, el ABI del contrato se encuentra en un archivo separado. Dado que el ABI posterior al contrato compilado (compilado con trufa) en truey falsees la primera letra en minúscula, y python ha realizado una primera letra distintiva en mayúscula, es necesario reemplazar el manual Truey False. Si guarda el ABI del contrato en un archivo separado, después de leer el archivo, primero debe usar el método del jsonmódulo loadspara convertirlo en un diccionario y luego obtener los ['abi']atributos, no es necesario reemplazar manualmente la truesuma false.

Cinco, usa Python para calcular y comparar los dos

       Cree uno nuevo en el mismo directorio de trabajo test.py, el código es el siguiente:

from web3.auto.infura.kovan import w3
from contract import CalSelector


# bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
func = 'isApprovedForAll(address,address)'

# 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
#     0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
# 注意,接口出现的顺序并不影响计算结果,这个也是显然亦见的
selectors = [
    0x70a08231,
    0x6352211e,
    0x095ea7b3,
    0x081812fc,
    0xa22cb465,
    0xe985e9c5,
    0x23b872dd,
    0x42842e0e,
    0xb88d4fde
]


def calSelectorByPython(_func):
    result = w3.keccak(text=_func)
    selector = (w3.toHex(result))[:10]
    return selector


def calSelectorBySolidity(_func):
    selector = CalSelector.functions.getSelector(_func).call()
    return w3.toHex(selector)


def calSupportedInterfaceByPython(_selectors):
    result = int('0x00000000',16)
    for selector in _selectors:
        result = result ^ selector
    return w3.toHex(result)


def calSupportedInterfaceBySolidity(_selectors):
    _param = [ w3.toBytes(selector) for selector in _selectors]
    supported_interface = CalSelector.functions.getSupportedInterface(_param).call()
    return w3.toHex(supported_interface)


if __name__ == "__main__":
    print(calSelectorByPython(func))
    print(calSelectorBySolidity(func))
    print('-------------------------')
    print(calSupportedInterfaceByPython(selectors))
    print(calSupportedInterfaceBySolidity(selectors))

       El código también es muy simple, se definen cuatro funciones, a saber, usar python y usar el contrato para calcular el selector de funciones, usar python y usar el contrato para calcular el valor constante de la interfaz compatible.

       El código usa directamente los comentarios en la primera sección de este artículo para verificación, preste atención al error administrativo que mencioné. Ejecutar directamente test.py:

➜  python python3 test.py
0xe985e9c5
0xe985e9c5
-------------------------
0x80ac58cd
0x80ac58cd

       Se puede ver en la salida que el resultado calculado usando Python es el mismo que el resultado calculado usando el contrato, y es el mismo que el número en el comentario. Una cosa que debe explicarse es que los selectores de funciones y los valores constantes de la interfaz son todos bytes4tipos, que son tipos en Python bytes. Para facilitar la visualización, los convertimos en forma de cadena hexadecimal.

6. Calcular directamente en etherscan

       Como puede ver en el contenido anterior, conectarse al contrato inteligente de Ethereum requiere una serie de operaciones, que aún son un poco problemáticas. ¿Qué pasa si no queremos usar Python u otros lenguajes de programación (como JavaScript) para conectarnos a los contratos inteligentes de Ethereum para los cálculos? No se preocupe, también tenemos una forma de llamar al contrato directamente en la página web.

       Sabemos que si un contrato inteligente es de código abierto, entonces el método del contrato se puede llamar directamente en etherscan. Usando esto, podemos calcular directamente el selector de funciones y el valor constante de la interfaz de soporte en etherscan.

       Visite el siguiente sitio web, la última dirección en la URL es la dirección del contrato en la tercera sección de este artículo en la red de prueba de kovan:

https://kovan.etherscan.io/address/0x07d74cf0ce4a1b10ece066725db1731515d62b76#readContract

Recordatorio:
dado que no se puede acceder directamente a etherscan, debe navegar por Internet de manera científica.

       Después de abrir la página web, haga clic en Contrato a continuación (una marca verde junto a ella significa que ha sido de código abierto). Si hace clic en Código, se mostrarán el código fuente del contrato y ABI. Hacemos clic en Leer contrato, y las dos funciones definidas en este contrato aparecerán en la lista a continuación.

       Nota: Los contratos que no hayan pasado la certificación de código abierto no pueden llamar directamente a las funciones correspondientes.
Inserte la descripción de la imagen aquí
       Ingrese el parámetro selector de función de cálculo getBalances(address)(esta es la interfaz personalizada agregada por Alpha Wallet a ERC721), haga clic en Consulta y se obtendrá el resultado correspondiente una vez finalizada la consulta. De manera similar, ingresamos en el getSupportedInterfacemétodo [0x06fdde03,0x95d89b41,0xc87b56dd](estos son todos los selectores de funciones de la interfaz de extensión de metadatos ERC721), y se obtendrá el resultado correspondiente.

       También puede marcar este sitio web para acceder directamente a él para realizar cálculos relacionados cuando sea necesario.

       De acuerdo, lo anterior es todo el contenido de este artículo. Deje un mensaje y corrija las deficiencias o errores.

Supongo que te gusta

Origin blog.csdn.net/weixin_39430411/article/details/104566176
Recomendado
Clasificación