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

feat: generate inversions of abi.encodePacked() for StakingMessages.unpack*() #482

Open
wants to merge 30 commits into
base: main
Choose a base branch
from

Conversation

ARR4N
Copy link

@ARR4N ARR4N commented Aug 6, 2024

Why this should be merged

Save significant gas + improve readability so it's easier to find bugs in reviews.

Gas before

Ran 5 tests for contracts/staking/tests/ValidatorMessagesTests.t.sol:ValidatorMessagesTest
[PASS] testRegisterSubnetValidatorMessage() (gas: 127215)
[PASS] testSetSubnetValidatorWeightMessage() (gas: 44491)
[PASS] testSubnetValidatorRegistrationMessage() (gas: 31525)
[PASS] testSubnetValidatorWeightUpdateMessag() (gas: 44578)
[PASS] testValidationUptimeMessage() (gas: 37860)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 563.33µs (757.46µs CPU time)

Gas after

Ran 5 tests for contracts/staking/tests/ValidatorMessagesTests.t.sol:ValidatorMessagesTest
[PASS] testRegisterSubnetValidatorMessage() (gas: 10151)
[PASS] testSetSubnetValidatorWeightMessage() (gas: 4728)
[PASS] testSubnetValidatorRegistrationMessage() (gas: 3782)
[PASS] testSubnetValidatorWeightUpdateMessag() (gas: 4815)
[PASS] testValidationUptimeMessage() (gas: 4133)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 372.38µs (320.00µs CPU time)

How this works

Packing was identical to abi.encodePacked() so it's replaced as such. There is no abi.decodePacked() so there's a code generator to load words directly from memory. Solidity automatically performs the necessary masking for bytes<N> types so only the mload() needs to be explicit.

Warning

There's an option to use mcopy() but we don't have the opcode yet so the generated code defaults to using the original memory space when returning dynamically sized arrays. This is a destructive operation that corrupts the input. If this isn't acceptable I can write a manual mcopy() instead.

How this was tested

The code generator also generates round-trip fuzz tests. To demonstrate equivalence with the previous approach, I originally only changed the pack* methods to allow the existing tests to show that original unpacking worked as expected; only then did I change the unpack* methods to use the generated code.

How is this documented

Comments in line with implementation, if and where required.

Note

Please excuse the force push and chaotic commit history. I honestly have no idea what went wrong when rebasing (then merging) onto the changed upstream branch. This SHOULD be squash merged!

@ARR4N ARR4N requested a review from geoff-vball August 6, 2024 19:33
@ARR4N ARR4N mentioned this pull request Aug 6, 2024
@cam-schultz
Copy link
Contributor

Thanks for putting this together! We'll definitely want to integrate these optimizations once the Warp message definitions in question are stable.

@iansuvak
Copy link
Contributor

iansuvak commented Aug 7, 2024

Yeah big fan of this method! Thanks!

@ARR4N ARR4N changed the base branch from staking-contract to staking-contracts August 9, 2024 13:34
github-advanced-security[bot]

This comment was marked as resolved.

@ARR4N ARR4N changed the base branch from staking-contracts to staking-contract August 9, 2024 13:35
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
contracts/staking/Unpack.sol Fixed Show fixed Hide fixed
@ARR4N ARR4N changed the title perf: SetSubnetValidatorWeightMessage (un)packing gas reductions feat: generate inversions of abi.encodePacked() for StakingMessages.unpack*() Aug 9, 2024
@ARR4N
Copy link
Author

ARR4N commented Aug 9, 2024

Thanks for putting this together! We'll definitely want to integrate these optimizations once the Warp message definitions in question are stable.

I've updated the PR to auto-generate the unpackers. Combined with using abi.encodePacked() this makes them easier to maintain if the Warp messages are changing.

@ARR4N ARR4N marked this pull request as ready for review August 9, 2024 14:24
@ARR4N ARR4N requested a review from a team as a code owner August 9, 2024 14:24

library Unpack {
/// @dev Thrown if the input buffer to an unpack function is of the wrong length.
error IncorrectInputLength(uint256 expected, uint256 actual);

Choose a reason for hiding this comment

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

how strongly do you feel about custom errors? We previously reverted from custom errors back to require statements, because we felt some tooling weren't updated to custom errors and lacked in debugging

Copy link
Author

Choose a reason for hiding this comment

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

Which tooling lacks support?

I think they're superior to string errors:

  1. Don't need to store the entire string in the contract code
  2. Can be parameterised to aid debugging and allow precise path detection in tests
  3. Avoid change-detector tests

Choose a reason for hiding this comment

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

we reverted to require statements over a year ago, so probably need to revisit this. I think back then some combination of forge errors, block explorers, and e2e testing just showed the hex error signature. This still seems like the case for our e2e test code, but there's likely a workaround we could do.

I am also leaning towards custom errors now, but think we should tackle this in a separate issue, and still conform to require to match the rest of the repo.

Copy link
Author

Choose a reason for hiding this comment

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

It's not a hill I'll die on, but something I feel I should push back on and ask "are you sure?" before I implement a toggle between the two.

Independent benchmarking of both deployment and usage gas.

Where is the problematic e2e test code?

Copy link
Author

Choose a reason for hiding this comment

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

As it currently stands, these reverts can never occur because I left the original length checks in place for the time being.

Choose a reason for hiding this comment

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

This folder holds the e2e tests I'm referring to https://github.com/ava-labs/teleporter/tree/main/tests/flows. This is also not a hill I'll die on, since I'm also leaning towards custom errors at this point, but wasn't sure to defer to a separate issue and changing all the require statements in the repo together so we can keep them consistent for now.

Copy link

@minghinmatthewlam minghinmatthewlam left a comment

Choose a reason for hiding this comment

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

LGTM once licensing is cleared up

@geoff-vball
Copy link
Contributor

Overall this looks so very useful! I think the current generated code was manually run on the current contracts? We should set up some sort of shell script (or add to the existing abi_bindings.sh) to make sure these are regenerated when files are updated.

@cam-schultz cam-schultz reopened this Sep 24, 2024
@cam-schultz cam-schultz changed the base branch from staking-contract to validator-manager September 24, 2024 14:33
@iansuvak iansuvak mentioned this pull request Oct 3, 2024
Base automatically changed from validator-manager to main October 29, 2024 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Progress 🏗
Development

Successfully merging this pull request may close these issues.

6 participants