Solidity Event Logs – Why Do Public and External Functions Emit Strings Differently in Event Logs?

eventssolidity

Producing the logs

When the same event is logged with the same string argument, the data in the transaction receipt is different, depending on whether the function was external or public.

pragma solidity 0.4.19;

contract EmitStringPublicAndExternal {
    event EmitString(string logme);

    function logPublic(string logme) public {
        EmitString(logme);
    }
    
    function logExternal(string logme) external {
        EmitString(logme);
    }
}

When I call both functions with "glitch" (in geth --dev), I get logs with two different data:

  • public: 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006676c697463680000000000000000000000000000000000000000000000000000
  • external: 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006676c69746368

Note: "glitch" encoded with UTF-8, and then hex-encoded, is: 0x676c69746368

Why is this happening?

Full public transaction & result

> web3.eth.getTransaction('0xfd726f1a6f94f4050aff1baee4aa50c56d127a633bfc01ebe3b023ea926aad0f')
{
  blockHash: "0xf0e7c13f811d06874a0191da3b135649cac4c9cd42a331c611734593d441a4f3",
  blockNumber: 21,
  from: "0x39708c52aea50be256bccd350d043ff36ffbb0e6",
  gas: 124415,
  gasPrice: 1,
  hash: "0xfd726f1a6f94f4050aff1baee4aa50c56d127a633bfc01ebe3b023ea926aad0f",
  input: "0x6a85450600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006676c697463680000000000000000000000000000000000000000000000000000",
  nonce: 20,
  r: "0xb13ed94a954ce5e9b0c7823f4da21561f1d2d1b1a04b44cfd1f8b65acee8e1b9",
  s: "0x1bb866cde428d538ba7920e58d64a6acb14d918a39dd8e7fb39b019dc9424261",
  to: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
  transactionIndex: 0,
  v: "0xa96",
  value: 0
}

> web3.eth.getTransactionReceipt('0xfd726f1a6f94f4050aff1baee4aa50c56d127a633bfc01ebe3b023ea926aad0f')
{
  blockHash: "0xf0e7c13f811d06874a0191da3b135649cac4c9cd42a331c611734593d441a4f3",
  blockNumber: 21,
  contractAddress: null,
  cumulativeGasUsed: 24415,
  from: "0x39708c52aea50be256bccd350d043ff36ffbb0e6",
  gasUsed: 24415,
  logs: [{
      address: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
      blockHash: "0xf0e7c13f811d06874a0191da3b135649cac4c9cd42a331c611734593d441a4f3",
      blockNumber: 21,
      data: "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006676c697463680000000000000000000000000000000000000000000000000000",
      logIndex: 0,
      removed: false,
      topics: ["0xd98695e9e68b6580b8d80f03676b8575f4cc4225687169877f72d5c2b39ff61c"],
      transactionHash: "0xfd726f1a6f94f4050aff1baee4aa50c56d127a633bfc01ebe3b023ea926aad0f",
      transactionIndex: 0
  }],
  logsBloom: "0x00000000000000000000000000000002000000000000000000000000400000000000000000000001000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000040000000",
  status: "0x1",
  to: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
  transactionHash: "0xfd726f1a6f94f4050aff1baee4aa50c56d127a633bfc01ebe3b023ea926aad0f",
  transactionIndex: 0
}

Full external transaction & result

> web3.eth.getTransaction('0x09677264874077078d4b271f85ec7a940cf69f9b751b9a94e1cbf0f40dff1e86')
{
  blockHash: "0xe2e6036b11aa7abe948c5008e72758186a83d679c7a3d03f55a9ad959ce12680",
  blockNumber: 22,
  from: "0x39708c52aea50be256bccd350d043ff36ffbb0e6",
  gas: 123833,
  gasPrice: 1,
  hash: "0x09677264874077078d4b271f85ec7a940cf69f9b751b9a94e1cbf0f40dff1e86",
  input: "0xd486916900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006676c697463680000000000000000000000000000000000000000000000000000",
  nonce: 21,
  r: "0xe06e321ae4e3031ba30a32585050d5ed10388bba591e33d5e3d17077e2dd7323",
  s: "0x7b2d1a18453ef5bd66d8065fbf1f159d029b3a64b52488382cfb8bafc6b00f09",
  to: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
  transactionIndex: 0,
  v: "0xa96",
  value: 0
}

> web3.eth.getTransactionReceipt('0x09677264874077078d4b271f85ec7a940cf69f9b751b9a94e1cbf0f40dff1e86')
{
  blockHash: "0xe2e6036b11aa7abe948c5008e72758186a83d679c7a3d03f55a9ad959ce12680",
  blockNumber: 22,
  contractAddress: null,
  cumulativeGasUsed: 23833,
  from: "0x39708c52aea50be256bccd350d043ff36ffbb0e6",
  gasUsed: 23833,
  logs: [{
      address: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
      blockHash: "0xe2e6036b11aa7abe948c5008e72758186a83d679c7a3d03f55a9ad959ce12680",
      blockNumber: 22,
      data: "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006676c69746368",
      logIndex: 0,
      removed: false,
      topics: ["0xd98695e9e68b6580b8d80f03676b8575f4cc4225687169877f72d5c2b39ff61c"],
      transactionHash: "0x09677264874077078d4b271f85ec7a940cf69f9b751b9a94e1cbf0f40dff1e86",
      transactionIndex: 0
  }],
  logsBloom: "0x00000000000000000000000000000002000000000000000000000000400000000000000000000001000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000040000000",
  status: "0x1",
  to: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
  transactionHash: "0x09677264874077078d4b271f85ec7a940cf69f9b751b9a94e1cbf0f40dff1e86",
  transactionIndex: 0
}

web3.js parses both formats

> espe.allEvents({'fromBlock': 0, toBlock: 'latest'}).get()
[{
    address: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
    args: {
      logme: "glitch"
    },
    blockHash: "0xf0e7c13f811d06874a0191da3b135649cac4c9cd42a331c611734593d441a4f3",
    blockNumber: 21,
    event: "EmitString",
    logIndex: 0,
    removed: false,
    transactionHash: "0xfd726f1a6f94f4050aff1baee4aa50c56d127a633bfc01ebe3b023ea926aad0f",
    transactionIndex: 0
}, {
    address: "0x2e64f811ab15803d34e2e6c503263f1393677aa9",
    args: {
      logme: "glitch"
    },
    blockHash: "0xe2e6036b11aa7abe948c5008e72758186a83d679c7a3d03f55a9ad959ce12680",
    blockNumber: 22,
    event: "EmitString",
    logIndex: 0,
    removed: false,
    transactionHash: "0x09677264874077078d4b271f85ec7a940cf69f9b751b9a94e1cbf0f40dff1e86",
    transactionIndex: 0
}]

The external version appears to produce an invalid ABI encoding

As I read this section of the ABI doc (emphasis mine), the version of the event without the trailing 0s seems broken:

bytes, of length k (which is assumed to be of type uint256):

enc(X) = enc(k) pad_right(X), i.e. the number of bytes is encoded as a uint256 followed by the actual value of X as a byte sequence, followed by the minimum number of zero-bytes such that len(enc(X)) is a multiple of 32.

Why am I asking?

I don't think this affects the question, but just in case…

The eth-abi python utility rejects the external version of the event log as invalid. I'm trying to make sure I understand if the ABI spec and eth-abi need to be updated, or … is this a Solidity bug?

Best Answer

The Solidity team confirmed that external function event logging has a bug: https://github.com/ethereum/solidity/issues/3493

public should be the correct one :)

The fix is scheduled to be released in v0.5.0

Related Topic