-
Notifications
You must be signed in to change notification settings - Fork 6
Add DEFLATE Inflator #8
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||
| pragma solidity >=0.8; | ||
|
|
||
| import "./IInflator.sol"; | ||
| import "inflate-sol/InflateLib.sol"; | ||
| import "account-abstraction/interfaces/IEntryPoint.sol"; | ||
| import {Test, console2} from "forge-std/Test.sol"; | ||
|
|
||
| /// Inflates a generic bundle compressed with DEFLATE | ||
| /// This reduces calldata size by ~72% and calldata cost by ~37% | ||
| /// (due to calldata 0-bytes being cheaper than non-0-bytes) | ||
| contract DeflateInflator is IInflator { | ||
| error InflateLibError(InflateLib.ErrorCode errorCode); | ||
|
|
||
| function inflate( | ||
| bytes calldata compressed | ||
| ) external view override returns (UserOperation[] memory, address payable) { | ||
| (InflateLib.ErrorCode errorCode, bytes memory decompressed) = InflateLib | ||
| .puff(compressed[3:], uint24(bytes3(compressed[0:3]))); | ||
|
|
||
| if (errorCode != InflateLib.ErrorCode.ERR_NONE) { | ||
| revert InflateLibError(errorCode); | ||
| } | ||
|
|
||
| UserOperation[] memory ops = abi.decode( | ||
| abi.encodePacked(uint256(0x20), decompressed), | ||
| (UserOperation[]) | ||
| ); | ||
|
|
||
| return (ops, payable(tx.origin)); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,12 @@ | |
| pragma solidity ^0.8.13; | ||
|
|
||
| import {Test, console2} from "forge-std/Test.sol"; | ||
| import {UserOperation} from "account-abstraction/interfaces/IEntryPoint.sol"; | ||
| import {UserOperation,IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol"; | ||
|
|
||
| import {BundleBulker} from "../src/BundleBulker.sol"; | ||
| import {IInflator} from "../src/IInflator.sol"; | ||
| import {DaimoTransferInflator} from "../src/DaimoTransferInflator.sol"; | ||
| import {DeflateInflator} from "../src/DeflateInflator.sol"; | ||
|
|
||
| contract BundleBulkerTest is Test { | ||
| BundleBulker public b; | ||
|
|
@@ -152,6 +153,61 @@ contract BundleBulkerTest is Test { | |
| ) | ||
| ); | ||
| } | ||
|
|
||
| function test_DeflateInflator() public { | ||
| DeflateInflator d = new DeflateInflator(); | ||
| b.registerInflator(0x42, d); | ||
|
|
||
| // Taken from a real bundle that was submitted to the network: | ||
| // https://basescan.org/tx/0xacb32cca8ddefcf73cb16fda17ff34cf15382e4e0cfb7a96e8149011a5fe3d29 | ||
| bytes memory raw = hex'000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000008bffa71a959af0b15c6eaa10d244d80bf23cb6a20000000000000000501c58693b65f1374631a2fca7bb7dc600000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000aae6000000000000000000000000000000000000000000000000000000000007b44a300000000000000000000000000000000000000000000000000000000000f427200000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014434fcd5be000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000a1b349c566c44769888948adc061abcdb54497f700000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001499d720cd5a04c16dc5377638e3f6d609c895714f00000000000000000000000000000000000000000000000000000000000000000000000000000000000001e80100006553c75f00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001ce1a2a89ec9d3cecd1e9fd65808d85702d7f8681d42ce8f0982363a362b87bd5498c72f497f9d27ae895c6d2c10a73e85b73d258371d2322c80ca5bfad242f5f000000000000000000000000000000000000000000000000000000000000002500000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22415141415a5650485830567a705463726d35665a6846505f566369545433584d57484832624e7a6a6435346531774e354d32696f222c226f726967696e223a226461696d6f2e636f6d227d0000000000000000000000000000000000000000000000000000000000000000000000000000000000'; | ||
| UserOperation[] memory originalOps = abi.decode(abi.encodePacked(uint256(0x20), raw), (UserOperation[])); | ||
|
|
||
| string[] memory inputs = new string[](3); | ||
| inputs[0] = "node"; | ||
| inputs[1] = "test/deflate.js"; | ||
| inputs[2] = '000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000008bffa71a959af0b15c6eaa10d244d80bf23cb6a20000000000000000501c58693b65f1374631a2fca7bb7dc600000000000000000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000493e000000000000000000000000000000000000000000000000000000000000aae6000000000000000000000000000000000000000000000000000000000007b44a300000000000000000000000000000000000000000000000000000000000f427200000000000000000000000000000000000000000000000000000000000f4240000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014434fcd5be000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000a1b349c566c44769888948adc061abcdb54497f700000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001499d720cd5a04c16dc5377638e3f6d609c895714f00000000000000000000000000000000000000000000000000000000000000000000000000000000000001e80100006553c75f00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001ce1a2a89ec9d3cecd1e9fd65808d85702d7f8681d42ce8f0982363a362b87bd5498c72f497f9d27ae895c6d2c10a73e85b73d258371d2322c80ca5bfad242f5f000000000000000000000000000000000000000000000000000000000000002500000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22415141415a5650485830567a705463726d35665a6846505f566369545433584d57484832624e7a6a6435346531774e354d32696f222c226f726967696e223a226461696d6f2e636f6d227d0000000000000000000000000000000000000000000000000000000000000000000000000000000000'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not just overall, i don't know about adding FFI i'd prefer to just say bytes memory compressed = '<constant hex>';...computed by calling test/deflate separately |
||
| bytes memory compressed = vm.ffi(inputs); | ||
|
|
||
| (UserOperation[] memory inflatedOps, address payable beneficiary) = b.inflate( | ||
| abi.encodePacked(uint32(0x42), uint24(raw.length), compressed) | ||
| ); | ||
|
|
||
| assertEq(beneficiary, tx.origin); | ||
| assertEq(abi.encode(inflatedOps), abi.encode(originalOps)); | ||
|
|
||
| // Calculate and print compression ratios | ||
|
|
||
| bytes memory uncompressedCallData = abi.encodeWithSelector(IEntryPoint.handleOps.selector, originalOps, tx.origin); | ||
| uint256 uncompressedCallDataLength = uncompressedCallData.length; | ||
| uint256 uncompressedCallDataCost = calculateCalldataCost(uncompressedCallData); | ||
|
|
||
| bytes memory compressedCallData = abi.encodePacked(uint32(0x42), uint24(raw.length), compressed); | ||
| uint256 compressedCallDataLength = compressedCallData.length; | ||
| uint256 compressedCallDataCost = calculateCalldataCost(compressedCallData); | ||
|
|
||
| console2.log("Uncompressed calldata length: ", uncompressedCallDataLength); | ||
| console2.log("Deflate compressed calldata length: ", compressedCallDataLength); | ||
| console2.log("Calldata length compression ratio: ", 100 - (compressedCallDataLength * 1e2) / uncompressedCallDataLength, "%"); | ||
| console2.log(""); | ||
| console2.log("Uncompressed calldata cost: ", uncompressedCallDataCost); | ||
| console2.log("Deflate compressed calldata cost: ", compressedCallDataCost); | ||
| console2.log("Calldata cost compression ratio: ", 100 - (compressedCallDataCost * 1e2) / uncompressedCallDataCost, "%"); | ||
| } | ||
|
|
||
| function calculateCalldataCost(bytes memory callData) public pure returns (uint256 cost) { | ||
| // 0 bytes cost 4 gas, non-0 bytes cost 16 gas | ||
| for (uint256 i = 0; i < callData.length; i++) { | ||
| if (callData[i] == 0) { | ||
| cost += 4; | ||
| } else { | ||
| cost += 16; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| } | ||
|
|
||
| contract DummyInflator is IInflator { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| const pako = require("pako"); | ||
|
|
||
| const compressed = pako.deflateRaw(new Uint8Array(Buffer.from(process.argv[2], 'hex')), { level: 9 }); | ||
|
|
||
| console.log(Buffer.from(compressed).toString('hex')); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 cc @nalinbhardwaj this is a good example of why we might not want to require in this case, there's an existing library that decompresses a |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
recommend pulling this test out into
DeflateInflator.t.sol(i'll delete
DaimoTransferInflatorin a future PR, already superseded byDaimoOpInflator)