Decodificar datos de entrada en la transacción

Tengo un ABI y el inputvalor de la transacción y quiero saber qué función se ha llamado en un contrato y con qué argumentos (uno o varios de diferentes tipos), preferiblemente usando geth, JSON-RPCy algún pseudocódigo que luego pueda traducir a Ruby.

Hay un par de preguntas similares sobre SO, pero ninguna de ellas realmente respondió la pregunta de una manera que sea comprensible para alguien nuevo en Ethereum.

Como ejemplo, uso el MetaCoin contrato generado por Truffle . Llamo al método getBalance('0xfd46f749f9d916122fe958d7f8d5ad033b187472')y obtengo la entrada 0xf8b2cb4f000000000000000000000000fd46f749f9d916122fe958d7f8d5ad033b187472.

trufa(desarrollo)> web3.eth.getTransaction('0x5d7a91c9f068d723ac52480c0ef61b9bf3f7b52dc726046e0b47f1beaa2f44ff')
{ hash: '0x5d7a91c9f068d723ac52480c0ef61b9bf3f7b52dc726046e0b47f1beaa2f44ff',
  una vez: 5,
  bloqueHash: '0xd150b520eb69a621a7218722c0de5b270c9abc499a90a81ca96340e274599c28',
  número de bloque: 6,
  índice de transacción: 0,
  de: '0xfd46f749f9d916122fe958d7f8d5ad033b187472',
  a: '0x8dec2f9cccbe718c7c51e9154f223eb226bb7942',
  valor: { [Cadena: '0'] s: 1, e: 0, c: [ 0 ] },
  gasolina: 4712388,
  preciogas: { [String: '100000000000'] s: 1, e: 11, c: [ 100000000000 ] },
  entrada: '0xf8b2cb4f0000000000000000000000000fd46f749f9d916122fe958d7f8d5ad033b187472' }

El ABI es el siguiente:

[
    {
      "constante": falso,
      "entradas": [
        {
          "nombre": "dirección",
          "tipo": "dirección"
        }
      ],
      "nombre": "obtenerBalanceInEth",
      "salidas": [
        {
          "nombre": "",
          "tipo": "uint256"
        }
      ],
      "a pagar": falso,
      "tipo": "función"
    },
    {
      "constante": falso,
      "entradas": [
        {
          "nombre": "receptor",
          "tipo": "dirección"
        },
        {
          "nombre": "cantidad",
          "tipo": "uint256"
        }
      ],
      "nombre": "enviarCoin",
      "salidas": [
        {
          "nombre": "suficiente",
          "tipo": "bool"
        }
      ],
      "a pagar": falso,
      "tipo": "función"
    },
    {
      "constante": falso,
      "entradas": [
        {
          "nombre": "dirección",
          "tipo": "dirección"
        }
      ],
      "nombre": "obtener Saldo",
      "salidas": [
        {
          "nombre": "",
          "tipo": "uint256"
        }
      ],
      "a pagar": falso,
      "tipo": "función"
    },
    {
      "entradas": [],
      "a pagar": falso,
      "tipo": "constructor"
    },
    {
      "anónimo": falso,
      "entradas": [
        {
          "indexado": cierto,
          "nombre": "_de",
          "tipo": "dirección"
        },
        {
          "indexado": cierto,
          "nombre": "_a",
          "tipo": "dirección"
        },
        {
          "indexado": falso,
          "nombre": "_valor",
          "tipo": "uint256"
        }
      ],
      "nombre": "Transferir",
      "tipo": "evento"
    }
  ]

¿Cómo puedo decodificar cualquier entrada para saber qué método se llamó y qué argumentos se pasaron?

Actualizar

He agregado todos los datos del bloque con el campo de entrada en la parte inferior de los registros.

{"number"=>"0x09",
 "hash"=>"0xf543eba0c80d49beea1f934c0f0ad9f0babe272a8318312545d43e6b612e4006",
 "parentHash"=>
  "0xde7a61e8dd84b5e080da8e16430951f7f138347c77ed263d0093e367c9d30775",
 "nonce"=>"0x0",
 "sha3Uncles"=>
  "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
 "logsBloom"=>
  "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
 "transactionsRoot"=>
  "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
 "stateRoot"=>
  "0x269cfa04ca1fe73202f788397ece62c520c64d5da7225ba2af2d673a2e9892f2",
 "receiptRoot"=>
  "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
 "miner"=>"0x0000000000000000000000000000000000000000",
 "difficulty"=>"0x0",
 "totalDifficulty"=>"0x0",
 "extraData"=>"0x0",
 "size"=>"0x03e8",
 "gasLimit"=>"0x47e7c4",
 "gasUsed"=>"0x5a3e",
 "timestamp"=>"0x58c83fda",
 "transactions"=>
  [{"hash"=>
     "0xce1eaf03dd1dfa5898243711ebcc1dc5c6357701f5a6c19f0b1f84130cb651fa",
    "nonce"=>"0x08",
    "blockHash"=>
     "0xf543eba0c80d49beea1f934c0f0ad9f0babe272a8318312545d43e6b612e4006",
    "blockNumber"=>"0x09",
    "transactionIndex"=>"0x0",
    "from"=>"0xfd46f749f9d916122fe958d7f8d5ad033b187472",
    "to"=>"0x8dec2f9cccbe718c7c51e9154f223eb226bb7942",
    "value"=>"0x0",
    "gas"=>"0x47e7c4",
    "gasPrice"=>"0x174876e800",
    "input"=>
     "0xf8b2cb4f000000000000000000000000fd46f749f9d916122fe958d7f8d5ad033b187472"}],
 "uncles"=>[]}
Cuando dices "entrada" te refieres a salida? Porque su ejemplo parece indicar que desea analizar la salida.
@SamuelHawksby-Robinson He agregado más detalles al final de la pregunta para aclarar.

Respuestas (5)

Puede consultar un paquete desarrollado por Consensys https://github.com/ConsenSys/abi-decoder

Impresionante. Esto funciona muy bien y devuelve el nombre del método ( getBalance), el nombre de cada argumento pasado en ( receiver) y el valor pasado en ( 0xfd46f749f9d916122fe958d7f8d5ad033b187472) para cada argumento. Sin embargo, ¿no devuelve el tipo de argumento?
Sí. No devuelve el tipo de argumentos.
Abrí un problema, un mantenedor amigo del repositorio ya lo agregó
Enfriar. trabajo rapido

Ah ok, entiendo lo que preguntas. Puede hacer esto web3.toAscii(transactionID.input)para obtener un formato legible por humanos.

Consulte web3.toAscii https://github.com/ethereum/wiki/wiki/JavaScript-API#web3toascii

Así que con tu código sería:

transaction = web3.eth.getTransaction('0x5d7a91c9f068d723ac52480c0ef61b9bf3f7b52dc726046e0b47f1beaa2f44ff')
input = web3.toAscii(transaction.input)
console.log(input)
esto me 'ø²ËO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000ýF÷IùÙ\u0016\u0012/éX×øÕ­\u0003;\u0018tr'da No estoy familiarizado con Ascii y no sé cómo podría deducir de esta cadena que getBalance(address)se llamó al método con el argumento 0xfd46f749f9d916122fe958d7f8d5ad033b187472.
@Samuel Hawksby-Robinson Utilicé su comando para mi hash Tx (0x37361fd8eb5f78c7558ccfaa338c1935ecbc3d486f7a63c5b3e1b245779bc90e) Sin embargo, recibo estos errores: "Error de tipo: web3.toAscii no es una función" y también "Error de referencia: la entrada no está definida" Gracias
@Questioner usa web3.hexToAscii para una versión posterior de web3js
@migu porque esta respuesta es incorrecta, no puede decodificar datos tx sin conocer abi, ya que los primeros 4 bytes de datos son selectores de funciones y el resto se decodifica de acuerdo con los tipos y tamaños de abi arg.

Ahora puede utilizar la web3.eth.abi.decodeLogfunción (web3 1.0).

Ejemplo de la documentación:

web3.eth.abi.decodeLog([{
    type: 'string',
    name: 'myString'
},{
    type: 'uint256',
    name: 'myNumber',
    indexed: true
},{
    type: 'uint8',
    name: 'mySmallNumber',
    indexed: true
}],
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000748656c6c6f252100000000000000000000000000000000000000000000000000',
['0x000000000000000000000000000000000000000000000000000000000000f310', '0x0000000000000000000000000000000000000000000000000000000000000010']);
> Result {
    '0': 'Hello%!',
    '1': '62224',
    '2': '16',
    myString: 'Hello%!',
    myNumber: '62224',
    mySmallNumber: '16'
}

Puede usar la herramienta ethereum-input-data-decoder para decodificar datos de entrada de transacciones dada la ABI.

Ejemplo:

const InputDataDecoder = require('ethereum-input-data-decoder');
const decoder = new InputDataDecoder(`${__dirname}/abi.json`);

const data = `0x67043cae0000000000000000000000005a9dac9315fdd1c3d13ef8af7fdfeb522db08f020000000000000000000000000000000000000000000000000000000058a20230000000000000000000000000000000000000000000000000000000000040293400000000000000000000000000000000000000000000000000000000000000a0f3df64775a2dfb6bc9e09dced96d0816ff5055bf95da13ce5b6c3f53b97071c800000000000000000000000000000000000000000000000000000000000000034254430000000000000000000000000000000000000000000000000000000000`;

const result = decoder.decodeData(data);

console.log(result);

{
  "name": "registerOffChainDonation",
  "types": [
    "address",
    "uint256",
    "uint256",
    "string",
    "bytes32"
    ],
    "inputs": [
      <BN: 5a9dac9315fdd1c3d13ef8af7fdfeb522db08f02>,
      <BN: 58a20230>,
      <BN: 402934>,
      "BTC",
      <Buffer f3 df ... 71 c8>
    ]
}

En el nuevo web3.js 1.0 debe usar: web3.utils.toAscii(transaction.input).

Algunas veces debe hacer una let input = transaction.input.replace(/^0x/, ``);para eliminar estos caracteres inútiles desde el inicio de la cadena.

Ver documentos .