Skip to content

Conversation

@chrismaree
Copy link
Member

@chrismaree chrismaree commented Jan 31, 2022

No description provided.

chrismaree and others added 7 commits January 28, 2022 14:04
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Co-authored-by: Matt Rice <matthewcrice32@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
@chrismaree chrismaree marked this pull request as draft January 31, 2022 20:49
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
@@ -1,13 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
Copy link
Member Author

Choose a reason for hiding this comment

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

I removed this for now...we dont have a definition of what this requires just yet and I thought it cleaner to directly add the finder to the hub.

uint64 unclaimedPoolRebalanceLeafCount;
bytes32 poolRebalanceRoot;
bytes32 destinationDistributionRoot;
mapping(uint256 => uint256) claimedBitMap;
Copy link
Member Author

Choose a reason for hiding this comment

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

this is an important change: we use a 1D bitMap after this PR, thereby reducing storage requirements and enabling us to re-use state between each proposal. tradeoff is we can only have up to 256 different chains supported, but this is likely fine :)

Copy link
Member

Choose a reason for hiding this comment

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

yeah this seems reasonable until v3 :D

uint64 indexed relayerRefundId,
uint64 requestExpirationTimestamp,
uint64 poolRebalanceLeafCount,
uint64 unclaimedPoolRebalanceLeafCount,
Copy link
Member Author

Choose a reason for hiding this comment

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

variable name changed for @nicholaspai

Signed-off-by: chrismaree <christopher.maree@gmail.com>
require(getCurrentTime() >= refundRequest.requestExpirationTimestamp, "Not passed liveness");

// Verify the leafId in the poolRebalance has not yet been claimed.
require(!MerkleLib.isClaimed(relayerRefund.claimedBitMap, poolRebalance.leafId), "Already claimed");
Copy link
Member Author

Choose a reason for hiding this comment

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

we use a separate MerkleLib method that I added to deal with 1D bit maps.

Copy link
Member Author

Choose a reason for hiding this comment

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

I will add unit tests for this once @mrice32 's MerkleLib PR has been merged.

Signed-off-by: chrismaree <christopher.maree@gmail.com>
function getNumberOfRelayRefunds() public view returns (uint256) {
if (relayerRefundRequests.length == 0) return 0;
return relayerRefundRequests.length - 1;
function _getRefundProposalAncillaryData() public view returns (bytes memory ancillaryData) {
Copy link
Member Author

Choose a reason for hiding this comment

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

this was my initial pass at constructing the ancillary data. curious what you guys think & if we need more/less in this.

Copy link
Member

Choose a reason for hiding this comment

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

these seem reasonable. I'm thinking about ways we can reduce the amount of calls to appendKeyValue... because these functions are expensive I believe

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we call this in the happy path? Or only on disputes? If only on disputes, we might not need to worry about gas quite as much (although it's still worth thinking about!!).

Copy link
Member Author

Choose a reason for hiding this comment

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

it only happens in the dispute case so the gas does not matter too much I don't think.

* @param index the index to check in the bitmap.
\* @return bool indicating if the index within the claimedBitMap has been marked as claimed.
*/
function isClaimed1D(uint256 claimedBitMap, uint256 index) public pure returns (bool) {
Copy link
Member Author

Choose a reason for hiding this comment

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

these are the new merkleLib methods I added. v simple and adapted from the original ones to take in a uint256 claimedBitMap rather than mapping(uint256 => uint256) storage claimedBitMap

@@ -0,0 +1,33 @@
import { getContractFactory, utf8ToHex } from "./utils";
Copy link
Member Author

Choose a reason for hiding this comment

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

~30 lines to deploy the whole UMA ecosystem <3. I love clean repos.

@chrismaree chrismaree requested review from mrice32 and nicholaspai and removed request for nicholaspai February 1, 2022 22:07
@chrismaree chrismaree marked this pull request as ready for review February 1, 2022 22:07
Signed-off-by: chrismaree <christopher.maree@gmail.com>
refundRequest.unclaimedPoolRebalanceLeafCount = poolRebalanceLeafCount;
refundRequest.poolRebalanceRoot = poolRebalanceRoot;
refundRequest.destinationDistributionRoot = destinationDistributionRoot;
refundRequest.proposer = msg.sender;
Copy link
Member

Choose a reason for hiding this comment

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

All things being equal I prefer using key-value syntax to set refundRequest here because it means that the developer is less likely to forget to set a parameter. Are there gas savings to setting each param individually and leaving default values as-is for keys like proposerBondRepaid?

Copy link
Member Author

Choose a reason for hiding this comment

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

so that would replace the delete call plus this assigmeneet with this:

// Remove the existing information relating to the previous relayer refund request and set to the new info.
        refundRequest = RefundRequest({
            requestExpirationTimestamp: requestExpirationTimestamp,
            unclaimedPoolRebalanceLeafCount: poolRebalanceLeafCount,
            poolRebalanceRoot: poolRebalanceRoot,
            destinationDistributionRoot: destinationDistributionRoot,
            claimedBitMap: 0,
            proposer: msg.sender,
            proposerBondRepaid: false
        });

I tried out the gas costs:
using the old assignment + delete that I had cost: 155863
using the syntax above cost: 156041

I think the reason the original syntax is cheaper is you are deleting storage and then not re-asigining some things, such as the claimedBitMap and the proposerBondRepaid. for these variables the default (post delete values) are the correct in this context.

As a result, I'm going leave it with the original implementation, unless you have anything else I can try that is syntactically better but still gas efficient.

Copy link
Contributor

Choose a reason for hiding this comment

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

I also think there are some annoying solidity inefficiencies with key value syntax. I think they create the entire struct in mem and then assign it rather than just assigning each value directly to storage. This increased mem usage might be why the gas costs are higher.

Copy link
Member

Choose a reason for hiding this comment

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

sounds good to me

delete refundRequest; // Remove the existing information relating to the previous relayer refund request.

refundRequest.requestExpirationTimestamp = requestExpirationTimestamp;
refundRequest.unclaimedPoolRebalanceLeafCount = poolRebalanceLeafCount;
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to check that unclaimedPoolRebalanceLeafCount > 0? I only ask because the condition unclaimedPoolRebalanceLeafCount == 0 is an important one in this contract but I also don't think its exploitable to set it to 0.

Copy link
Member Author

Choose a reason for hiding this comment

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

ye, this is a good point and is actually something we should check. if the relayer provided a set of proofs with poolRebalanceLeafCount set to 0 then there is actually no L1->L2 actions that this bundle can do. they could be providing valid roots that do nothing. I agree that we should only process a bundle if it has at least one valid L2 action. I'll add this require.

Copy link
Member

@nicholaspai nicholaspai left a comment

Choose a reason for hiding this comment

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

I haven't checked all the tests out carefully yet but first pass the contract logic makes sense!

Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
@chrismaree chrismaree changed the title chrismaree/third pass replayment claim feat: hubpool third iteration. Add dispute logic and refine bitmap structure. Feb 1, 2022
Copy link
Contributor

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

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

This looks awesome!! Only minor comments and responses to threads.


event RelayerRefundDisputed(uint256 relayerRefundId, address disputer);
event RelayerRefundDisputed(
address disputer,
Copy link
Contributor

Choose a reason for hiding this comment

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

indexed?

Comment on lines +99 to +100
SkinnyOptimisticOracleInterface.Request ooPriceRequest,
RefundRequest refundRequest
Copy link
Contributor

Choose a reason for hiding this comment

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

One minor comment on these: we might want to select out parts of these structs we want to emit to 1) reduce the data and 2) make etherscan better able to encode/decode the events rather than just showing bytes.

Copy link
Member

Choose a reason for hiding this comment

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

this is a good point and i'll change the same for SpokePool

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm going to leave this for now and will try and see how much of a gas difference this makes.

Copy link
Member

Choose a reason for hiding this comment

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

i don't think it should change anything

}

function setBondTokenFinalFeeMultiplier(uint64 newBondAmount) public onlyOwner {
function setBond(IERC20 newBondToken, uint256 newBondAmount) public onlyOwner onlyIfNoActiveRequest {
Copy link
Contributor

Choose a reason for hiding this comment

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

+1 for this modifier

"Last bundle has unclaimed leafs"
);
) public onlyIfNoActiveRequest {
require(poolRebalanceLeafCount > 0, "Bundle must have at least 1 leaf");
Copy link
Contributor

Choose a reason for hiding this comment

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

+1 good check. This will make us more spam resistant.

refundRequest.unclaimedPoolRebalanceLeafCount = poolRebalanceLeafCount;
refundRequest.poolRebalanceRoot = poolRebalanceRoot;
refundRequest.destinationDistributionRoot = destinationDistributionRoot;
refundRequest.proposer = msg.sender;
Copy link
Contributor

Choose a reason for hiding this comment

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

I also think there are some annoying solidity inefficiencies with key value syntax. I think they create the entire struct in mem and then assign it rather than just assigning each value directly to storage. This increased mem usage might be why the gas costs are higher.

function getNumberOfRelayRefunds() public view returns (uint256) {
if (relayerRefundRequests.length == 0) return 0;
return relayerRefundRequests.length - 1;
function _getRefundProposalAncillaryData() public view returns (bytes memory ancillaryData) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we call this in the happy path? Or only on disputes? If only on disputes, we might not need to worry about gas quite as much (although it's still worth thinking about!!).

refundRequest.requestExpirationTimestamp
);

ancillaryData = AncillaryData.appendKeyValueUint(
Copy link
Contributor

Choose a reason for hiding this comment

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

If we do end up caring about hyper-optimizing gas usage here, we probably need to take a second look at the Uint method, since I don't think we optimized that last time around.

Copy link
Member Author

Choose a reason for hiding this comment

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

it only is called in the dispute case so I don't think it matters too much

Copy link
Member

@nicholaspai nicholaspai left a comment

Choose a reason for hiding this comment

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

Thanks for responding to my comments, lgtm

\* @return bool indicating if the index within the claimedBitMap has been marked as claimed.
*/
function isClaimed1D(uint256 claimedBitMap, uint256 index) public pure returns (bool) {
return claimedBitMap & (1 << index) > 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can you explain the differences in logic from the uniswap one?

I think their version of this code is:

        uint256 mask = (1 << index);
        return claimedBitMap & mask == mask;

Not trying to be nit-picky, this logic is just very dense, and I want to make sure we're super confident in the correctness.

Copy link
Member Author

Choose a reason for hiding this comment

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

going to add unit tests now on this and will verify the behaviour. can test out this other code example as well (note I created my implementation from modifying the uniswap code to be 1 dimensional :) )

Copy link
Member Author

Choose a reason for hiding this comment

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

both work equivalently. I reverted to your recommendation as it's easier to read.

bytes32 poolRebalanceRoot;
bytes32 destinationDistributionRoot;
mapping(uint256 => uint256) claimedBitMap;
uint256 claimedBitMap;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a good change, but we've started making some assumptions like "there will never be more than X tokens or Y chains". I want to write these down somewhere. Should we go back to the interface doc and write these down? Or start a known issues and constraints doc to outline this sort of stuff? Thoughts?

I will go ahead and start such a doc, but let me know if you disagree.

Copy link
Member Author

@chrismaree chrismaree Feb 2, 2022

Choose a reason for hiding this comment

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

ok great. ye, you right, we need to write this down! the interface doc is now somewhat out of date though in some regards. our options are to update the interface doc or start a new one.

in either case, I'm going to add some more comments on this in the code.

event BondAmountSet(uint64 newBondMultiplier);

event BondTokenSet(address newBondMultiplier);
event BondSet(address newBondToken, uint256 newBondAmount);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we index newBondToken?

Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Signed-off-by: chrismaree <christopher.maree@gmail.com>
Copy link
Contributor

@mrice32 mrice32 left a comment

Choose a reason for hiding this comment

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

LGTM! Minor comments on events!

bytes32 destinationDistributionRoot,
address indexed proposer
);
event RelayerRefundExecuted(uint256 relayerRefundId, MerkleLib.PoolRebalance poolRebalance, address caller);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we enumerate the fields in poolRebalance? Also, do you think there are fields we can remove? Do we think these emissions are important for bot verification or anything?

);

relayerRefundRequests.pop();
emit RelayerRefundDisputed(msg.sender, ooPriceRequest, refundRequest);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we enumerate these structs for better parseability? Also might be nice to do so so we can drop fields that are unnecessary.

Copy link
Member Author

Choose a reason for hiding this comment

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

will do this on a follow up.

Signed-off-by: chrismaree <christopher.maree@gmail.com>
@chrismaree chrismaree merged commit e46a6cc into master Feb 3, 2022
@chrismaree chrismaree deleted the chrismaree/third-pass-replayment-claim branch February 3, 2022 00:32
pxrl pushed a commit that referenced this pull request Feb 20, 2024
Co-authored-by: Matt Rice <matthewcrice32@gmail.com>
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.

4 participants