Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TransactionReceipt.RawRepresentation method #63

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ryanschneider
Copy link
Contributor

@ryanschneider ryanschneider commented Apr 21, 2022

Ok, I think this is now ready for review. While we don't have a strict use case for this code at the moment, it does help verify things are correct. I dusted it off today since there were discussions on the Eth R&D Discord around a potential bug in how Infura was handling old tx receipts (see the mainnet test tx as an example) and was able to prove we are returning correct data by calculating the receiptRoot below.

  • I computed two separate receiptRoots for "single tx" blocks, a Sepolia block and a pre-Byzantium Mainnet block.
  • Speaking of pre-Byzantium, I updated to support that as well.
  • And rebased against latest and added BlobTx support.

Adding proper trie hashing support feels way out of scope for this project, so I'll continue to just verify the behavior "out of band" using ethereumJS since that's worked so far.

For posterity here's the scripts used to verify both the Sepolia and Mainent receipt roots (same code just different constants/comments):

Mainnet script
// Mainnet
import { BaseTrie as Trie } from 'merkle-patricia-tree'
import { toBuffer, bufferToHex, rlp } from 'ethereumjs-util'

// This is the binary representation of the pre-Byzantium receipt for tx hash
// 0xf94b37f170c234eacc203d683808bba6f671f12d8e87c71d246d4aee03deb579
// on Mainnet: https://etherscan.io/tx/0xf94b37f170c234eacc203d683808bba6f671f12d8e87c71d246d4aee03deb579
//
// As computed by this go-ethlibs PR: https://github.com/INFURA/go-ethlibs/pull/63
const encodedReceipt = '0xf90129a0c2b35ef55562a517f53c5a8eab65255b6c68950a7b980d2d0e970190c35282288347b760b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0'

async function computeReceiptRoot() {
  // We want to compute the receipt root of a block with only 1 tx in it
  // The block in question is 0x11d68b50f327f5ebac40b9487cacf4b6c6fb8ddabd852bbddac16dfc2d4ca6a7
  // on Mainnet: https://etherscan.io/block/2284956
  // and has a receipt root of 0x45be296e6c6b0215eeee992909e19e659c224452ae04cb00f7662e65fce81ce1
  const trie = new Trie()
  // for receipt tries, the key is the 0-based index of the tx in the block,
  // and the value is the binary representation of the receipt
  await trie.put(rlp.encode(0), toBuffer(encodedReceipt))
  const root = bufferToHex(trie.root)
  const expectedRoot = '0x45be296e6c6b0215eeee992909e19e659c224452ae04cb00f7662e65fce81ce1'
  if (root == expectedRoot) {
    console.log({
      matches: true,
      expected: expectedRoot,
      computed: root
    })
  } else {
    console.error({
      matches: false,
      expected: expectedRoot,
      computed: root
    })
  }
}

computeReceiptRoot()
Sepolia script
// Sepolia
import { BaseTrie as Trie } from 'merkle-patricia-tree'
import { toBuffer, bufferToHex, rlp } from 'ethereumjs-util'

// This is the binary representation of the receipt for tx hash
// 0x30296f5f32972c7c3b39963cfd91073000cb882c294adc2dcf0ac9ca34d67bd2
// on Sepoia: https://sepolia.otterscan.io/tx/0x30296f5f32972c7c3b39963cfd91073000cb882c294adc2dcf0ac9ca34d67bd2
//
// As computed by this go-ethlibs PR: https://github.com/INFURA/go-ethlibs/pull/63
const encodedReceipt = '0x02f901850182aebbb9010000800000000000000000000000000000000000000000100000020000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000400000000000000000f87cf87a94830bf80a3839b300291915e7c67b70d90823ffedf842a0e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109ca00000000000000000000000001b57edab586cbdabd4d914869ae8bb78dbc05571a00000000000000000000000000000000000000000000000000de0b6b3a7640000'

async function computeReceiptRoot() {
  // We want to compute the receipt root of a block with only 1 tx in it
  // The block in question is 0x59d58395078eb687813eeade09f4ccd3a40084e607c3b0e0b987794c12be48cc
  // on Sepolia: https://sepolia.otterscan.io/block/852597
  // and has a receipt root of 0x7800894d3a17b7f4ce8f17f96740e13696982605164eb4465bdd8a313d0953a5
  const trie = new Trie()
  // for receipt tries, the key is the 0-based index of the tx in the block,
  // and the value is the binary representation of the receipt
  await trie.put(rlp.encode(0), toBuffer(encodedReceipt))
  const root = bufferToHex(trie.root)
  const expectedRoot = '0x7800894d3a17b7f4ce8f17f96740e13696982605164eb4465bdd8a313d0953a5'
  
  if (root == expectedRoot) {
    console.log({
      matches: true,
      expected: expectedRoot,
      computed: root
    })
  } else {
    console.error({
      matches: false,
      expected: expectedRoot,
      computed: root
    })
  }
}

computeReceiptRoot()

@ryanschneider ryanschneider force-pushed the receipts-raw-represemntation branch from d0442fc to 28fdfb4 Compare February 10, 2023 00:55
@ryanschneider ryanschneider force-pushed the receipts-raw-represemntation branch from 28fdfb4 to e4c792f Compare January 25, 2024 19:01
@ryanschneider
Copy link
Contributor Author

Rebased and updated to support pre-EIP-658 tx receipts (which had .Root instead of .Status).

@ryanschneider ryanschneider marked this pull request as ready for review January 25, 2024 20:16
Copy link
Contributor

@antonydenyer antonydenyer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable.
Haven't fully verified - approving to unblock.

// The EIP-2718 ReceiptPayload for this transaction is rlp([status, cumulativeGasUsed, logsBloom, logs]).
// Same as TransactionTypeLegacy.
if t.Status == nil {
fields = append(fields, "status")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see fields is appended to here & also for TransactionTypeLegacy (line 54) but nil is returned unconditionally, is that intentional?

return rlp.Value{List: list}
}

switch t.TransactionType() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a case for TransactionTypeBlob here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants