9

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?

4

2 Answers 2

4

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

3

Nice catch. Its not bug, your getting different results because of public and external functions will handle memory differently.

In solidity public function immediately copies array arguments to memory. While copying to memory EVM will do 32 bytes padding.

EmitString(logme)

0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006676c69746368

As per EVM documentation EmitString(logme) logme is string type saving or representation would be 1st 32 bytes(32*2=64 chars) will represents location of string

0x0000000000000000000000000000000000000000000000000000000000000020 = 32

Now pointer will move to 33bytes to next 32 bytes is length so length is

0000000000000000000000000000000000000000000000000000000000000006 = 6, i.e next 6 bytes are data.

676c69746368 hex representation of string.

Memory representation of any bytes is padded with multiples of 32 bytes. Thats a reason you are getting 676c697463680000000000000000000000000000000000000000000000000000

Where is case of external functions can read directly from calldata. What is values your passing it will send same thing to the network. So thats reason you able to see only 676c69746368

Memory allocation is expensive, whereas reading from calldata is cheap.

That means its not a bug.

`external` vs `public` best practices

1
  • @carver hope you understand concept, please mark as accepted. It will help community to find ans easily. Commented Feb 15, 2018 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.