project screenshot 1
project screenshot 2
project screenshot 3
project screenshot 4

IntegrityProof

Proof of Bundle Integrity allows bundle senders to express exact slot he/she wants his/her bundle to be placed on a block and trustlessly prove whether a block builder has tempered with the bundle leveraging zero knowledge proof

IntegrityProof

Created At

ETHGlobal New York

Winner of

trophy

🚀 Flashbots — Best Innovation

trophy

💡 Axiom — Most Creative

Project Description

Goals

  • prove that bundle has not been tempered with
  • enable trustless slot selection

Requirements

  • A challenger should be able to prove the block builder has tempered with the bundle
  • Bundle sender should not be able to game the system
  • Block builder should not be able to game the system
  • A bundle sender should be able to express slot preference to a block builder

Description

  • There will be only one contract, call it proofOfIntegrity.sol

  • When new bundle is submitted, a sender should include a tx calling the stamp() function of the proofOfIntegrity.sol contract

    • stamp(Txs tx, uint blockNumber, uint bundleIndex)
      • take in all of the transactions in a bundle. Txs is an array that takes in a contract defined Transaction type i.e. struct Transaction
      • the function calculate the hash using keccak256 , count the tx number in the bundle, and use blockNumber and msg.sender to update to mappings
        • mapping(address => (mapping(uint = > uint))) public bundleSizeMap
          • this is a mapping senderAddress ⇒ blockNumber ⇒ bundleSize
        • mapping(address => (mapping(uint => bytes))) public bundleHash
          • this is a mapping senderAddress ⇒ blockNumber ⇒ bundleHash
      • Additionally, function will update another mapping
        • mapping(address => (mapping uint => uint)) bundleIndexMap
          • this is a mapping senderAddress ⇒ blockNumber ⇒ bundleIndex
          • Note: bundleIndex is to express the slot number a bundle wants to be placed in the block. The top slot a bundle can express is 2, since the slot 1 will always be taken by the verifyBundle() function invoked by the block builder(more on this later)
      • Then, the function will check the value in the mapping(address => (mapping uint => bool)) bundleVerificationMap . If the ending bool is the default value 0, then the function reverts with log bundleVerificationMap not updated
        • Note: the reason why it needs to revert is because it forces the block builder to insert a verifyBundle() transaction call on the top of the bundle. This will also let the block builder know that the sender has requested that he/she wants the bundle to be later subject to proof-of-integrity
        • this is a mapping senderAddress ⇒ blockNumber ⇒ verified
        • this mapping can only be updated by the verifyBundle() function(more on this in the following section)
  • when the bundle is submitted, the block builder needs to call the verifyBundle() function, and this function can only be called by a whitelisted block builders in a whitelist mapping

    • verifyBundle(Txs txs, address sender, uint blockNumber) onlyBlockBuilder returns(bundleIndex)
      • Note:onlyBlockBuilder is a modifier checking whether the caller is in the whitelist
      • the purpose of this function is to make sure that the value in the bundleSize , bundleHash, inputted by the bundle sender is correct because bundle sender could game the system by randomly putting the arbitrary bundleSize and bundleHash
      • The block builder will need to supply the three arguments txs, sender, blockNumber. All three of these values can be found the in the bundle submitted by the bundle sender
      • in the function, it’ll do the following:
        • get the length of txs, and use sender and blockNumber to query the bundleSizeMap . If match, continue; otherwise revert
        • calculate the bundleHash from the txs, and use sender and blockNumber to query the bundleHashMap. if match, continue; otherwise revert
        • Update the bundleVerificationMap to true by providing the sender and blockNumber
        • returns the bundleIndexMap to get the index a bundle sender wants
    • If the bundle index is accepted by the block builder, the block builder will add the bundle, with the tx calling verifyBundle() on the top of the bundle, to the block. Otherwise, the block builder discard the bundle.
      • Note: verifyBundle() can be called in a simulation to get the bundleIndexMap

    💡 Technically, a block builder can still temper the bundle after this step. A malicious block builder could call the verifyBundle() function first, then to change the ordering, or insert its own transaction in the middle of the bundle, and include the tempered bundle into the block. All of the previous steps are for a block builder to verify the arguments supplied by the bundle sender in stamp()function is correct.

    This is why we need the following steps

  • After the block has been built and mined on-chain. A third party could challenge by submitting a proof of integrity using Axiom V2

  • In a challenge, a user could use a front-end application to query two values leveraging Axiom V2

    • bundle hash:
      • The front end app would require a user to provide two values: senderAddress and blockNumber
      • the address is the bundle sender’s address, and blockNumber is the block when bundle was submitted
      • the front-end app will call the Axiom V2 API to query the block info. In the payload, the front-end app would find the bundle submitted by the sender by indexing the senderAddress.
      • Then, the front-end app would query the bundleSizeMap and bundleHashMap to get the bundleSize and bundleHash, given the senderAddress and blockNumber
      • By using the index number on a block, plus the bundle size, the front-end app could generate a bundleHash value in the browser. If it matches the bundleHash result from the Axiom V2 API, then it mean bundle was not tempered with; otherwise, it means the bundle has been tempered
      • submit the proof to a staking contract where block builder’s stake will be slashed
    • Bundle index:
      • using similar method, the front-end app could get the bundleIndex to see if the block builder actually put the bundle at the slot number a bundle sender requested in the stamp() function

How it's Made

It's made using Axiom's V2 to trustlessly prove the behavior of a block builder. Front-end is generated using Axiom V2's repl export feature. Additionally, a mock block builder to test the mechanism has also been provided.

The mechanism works mostly by allowing sender and blockbuilder respectively insert transactions at the bottom and top of a bundle

background image mobile

Join the mailing list

Get the latest news and updates