Skip to content

Commit

Permalink
[FUN-877] persist data fetched from allowlist contract (#11648)
Browse files Browse the repository at this point in the history
* feat: add allowlist cache layer migration

* feat: add getAllowedSendersInRange and getAllowedSendersCount methods for Tos

* feat: iterate over allowlist contract response, load and update allowlist

* feat: reduce gas by removing mapping and using EnumerableSet .length() and .at() methods

* feat: implement orm level for allowlist

* fix: fix iteration loop and add tests

* fix: correct var naming, zero indexing array, doc

* chore: rename cache to stored, split batchSize onchain vs stored

* fix: remove id from allowlist primary key, use it only for ordering

* feat: store allowlist in batches

* chore: correct CreateAllowedSenders naming

* chore: CreateAllowedSenders in main routine

* fix: add a trottle to prevent RPC rate limiting

* chore: split orm in subscriptions and allowlist pkgs

* feat: put the new functionality under a feature flag

* fix: fix subscriptions consumers address

* fix: store only on feature enable to prevent blocked addresses not being sync

* chore: rename CachedSubscriptions to StoredSubscriptions

* feat: implement GetBlockedSendersInRange and GetBlockedSendersCount in tos contract

* feat: implement sync of blocked senders with allowed

* chore: add checks and table constraints

* chore: methods renaming

* chore: correct log and var name
  • Loading branch information
agparadiso authored Jan 24, 2024
1 parent 59d1c99 commit 1022aa0
Show file tree
Hide file tree
Showing 29 changed files with 1,392 additions and 355 deletions.
150 changes: 80 additions & 70 deletions contracts/gas-snapshots/functions.gas-snapshot

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController,
string public constant override typeAndVersion = "Functions Terms of Service Allow List v1.1.0";

EnumerableSet.AddressSet private s_allowedSenders;
mapping(address => bool) private s_blockedSenders;
EnumerableSet.AddressSet private s_blockedSenders;

event AddedAccess(address user);
event BlockedAccess(address user);
Expand All @@ -29,6 +29,7 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController,
error InvalidSignature();
error InvalidUsage();
error RecipientIsBlocked();
error InvalidCalldata();

TermsOfServiceAllowListConfig private s_config;

Expand Down Expand Up @@ -70,7 +71,7 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController,

/// @inheritdoc ITermsOfServiceAllowList
function acceptTermsOfService(address acceptor, address recipient, bytes32 r, bytes32 s, uint8 v) external override {
if (s_blockedSenders[recipient]) {
if (s_blockedSenders.contains(recipient)) {
revert RecipientIsBlocked();
}

Expand Down Expand Up @@ -101,6 +102,32 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController,
return s_allowedSenders.values();
}

/// @inheritdoc ITermsOfServiceAllowList
function getAllowedSendersCount() external view override returns (uint64) {
return uint64(s_allowedSenders.length());
}

/// @inheritdoc ITermsOfServiceAllowList
function getAllowedSendersInRange(
uint64 allowedSenderIdxStart,
uint64 allowedSenderIdxEnd
) external view override returns (address[] memory allowedSenders) {
if (
allowedSenderIdxStart > allowedSenderIdxEnd ||
allowedSenderIdxEnd >= s_allowedSenders.length() ||
s_allowedSenders.length() == 0
) {
revert InvalidCalldata();
}

allowedSenders = new address[]((allowedSenderIdxEnd - allowedSenderIdxStart) + 1);
for (uint256 i = 0; i <= allowedSenderIdxEnd - allowedSenderIdxStart; ++i) {
allowedSenders[i] = s_allowedSenders.at(uint256(allowedSenderIdxStart + i));
}

return allowedSenders;
}

/// @inheritdoc IAccessController
function hasAccess(address user, bytes calldata /* data */) external view override returns (bool) {
if (!s_config.enabled) {
Expand All @@ -118,19 +145,45 @@ contract TermsOfServiceAllowList is ITermsOfServiceAllowList, IAccessController,
if (!s_config.enabled) {
return false;
}
return s_blockedSenders[sender];
return s_blockedSenders.contains(sender);
}

/// @inheritdoc ITermsOfServiceAllowList
function blockSender(address sender) external override onlyOwner {
s_allowedSenders.remove(sender);
s_blockedSenders[sender] = true;
s_blockedSenders.add(sender);
emit BlockedAccess(sender);
}

/// @inheritdoc ITermsOfServiceAllowList
function unblockSender(address sender) external override onlyOwner {
s_blockedSenders[sender] = false;
s_blockedSenders.remove(sender);
emit UnblockedAccess(sender);
}

/// @inheritdoc ITermsOfServiceAllowList
function getBlockedSendersCount() external view override returns (uint64) {
return uint64(s_blockedSenders.length());
}

/// @inheritdoc ITermsOfServiceAllowList
function getBlockedSendersInRange(
uint64 blockedSenderIdxStart,
uint64 blockedSenderIdxEnd
) external view override returns (address[] memory blockedSenders) {
if (
blockedSenderIdxStart > blockedSenderIdxEnd ||
blockedSenderIdxEnd >= s_blockedSenders.length() ||
s_blockedSenders.length() == 0
) {
revert InvalidCalldata();
}

blockedSenders = new address[]((blockedSenderIdxEnd - blockedSenderIdxStart) + 1);
for (uint256 i = 0; i <= blockedSenderIdxEnd - blockedSenderIdxStart; ++i) {
blockedSenders[i] = s_blockedSenders.at(uint256(blockedSenderIdxStart + i));
}

return blockedSenders;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ interface ITermsOfServiceAllowList {
/// @return addresses - all allowed addresses
function getAllAllowedSenders() external view returns (address[] memory);

/// @notice Get details about the total number of allowed senders
/// @return count - total number of allowed senders in the system
function getAllowedSendersCount() external view returns (uint64);

/// @notice Retrieve a list of allowed senders using an inclusive range
/// @dev WARNING: getAllowedSendersInRange uses EnumerableSet .length() and .at() methods to iterate over the list
/// without the need for an extra mapping. These method can not guarantee the ordering when new elements are added.
/// Evaluate if eventual consistency will satisfy your usecase before using it.
/// @param allowedSenderIdxStart - index of the allowed sender to start the range at
/// @param allowedSenderIdxEnd - index of the allowed sender to end the range at
/// @return allowedSenders - allowed addresses in the range provided
function getAllowedSendersInRange(
uint64 allowedSenderIdxStart,
uint64 allowedSenderIdxEnd
) external view returns (address[] memory allowedSenders);

/// @notice Allows access to the sender based on acceptance of the Terms of Service
/// @param acceptor - The wallet address that has accepted the Terms of Service on the UI
/// @param recipient - The recipient address that the acceptor is taking responsibility for
Expand All @@ -37,6 +53,22 @@ interface ITermsOfServiceAllowList {
/// @notice Re-allows a previously blocked sender to accept the Terms of Service
/// @param sender - Address of the sender to unblock
function unblockSender(address sender) external;

/// @notice Get details about the total number of blocked senders
/// @return count - total number of blocked senders in the system
function getBlockedSendersCount() external view returns (uint64);

/// @notice Retrieve a list of blocked senders using an inclusive range
/// @dev WARNING: getBlockedSendersInRange uses EnumerableSet .length() and .at() methods to iterate over the list
/// without the need for an extra mapping. These method can not guarantee the ordering when new elements are added.
/// Evaluate if eventual consistency will satisfy your usecase before using it.
/// @param blockedSenderIdxStart - index of the blocked sender to start the range at
/// @param blockedSenderIdxEnd - index of the blocked sender to end the range at
/// @return blockedSenders - blocked addresses in the range provided
function getBlockedSendersInRange(
uint64 blockedSenderIdxStart,
uint64 blockedSenderIdxEnd
) external view returns (address[] memory blockedSenders);
}

// ================================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,69 @@ contract FunctionsTermsOfServiceAllowList_GetAllAllowedSenders is FunctionsOwner
}
}

/// @notice #getAllowedSendersCount
contract FunctionsTermsOfServiceAllowList_GetAllowedSendersCount is FunctionsOwnerAcceptTermsOfServiceSetup {
function test_GetAllowedSendersCount_Success() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

uint96 allowedSendersCount = s_termsOfServiceAllowList.getAllowedSendersCount();
// One allowed sender was made during setup
assertEq(allowedSendersCount, 1);
}
}

/// @notice #getAllowedSendersInRange
contract FunctionsTermsOfServiceAllowList_GetAllowedSendersInRange is FunctionsOwnerAcceptTermsOfServiceSetup {
function test_GetAllowedSendersInRange_Success() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

address[] memory expectedSenders = new address[](1);
expectedSenders[0] = OWNER_ADDRESS;

assertEq(s_termsOfServiceAllowList.getAllowedSendersInRange(0, 0), expectedSenders);
}

function test_GetAllowedSendersInRange_RevertIfAllowedSendersIsEmpty() public {
// setup a new empty s_termsOfServiceAllowList
FunctionsRoutesSetup.setUp();

// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

uint64 AllowedSendersCount = s_termsOfServiceAllowList.getAllowedSendersCount();
uint64 expected = 0;
assertEq(AllowedSendersCount, expected);

vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector);
s_termsOfServiceAllowList.getAllowedSendersInRange(0, 0);
}

function test_GetAllowedSendersInRange_RevertIfStartIsAfterEnd() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector);

s_termsOfServiceAllowList.getAllowedSendersInRange(1, 0);
}

function test_GetAllowedSendersInRange_RevertIfEndIsAfterLastAllowedSender() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

uint64 AllowedSendersCount = s_termsOfServiceAllowList.getAllowedSendersCount();
vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector);
s_termsOfServiceAllowList.getAllowedSendersInRange(1, AllowedSendersCount + 1);
}
}

/// @notice #hasAccess
contract FunctionsTermsOfServiceAllowList_HasAccess is FunctionsRoutesSetup {
function test_HasAccess_FalseWhenEnabled() public {
Expand Down Expand Up @@ -373,3 +436,78 @@ contract FunctionsTermsOfServiceAllowList_UnblockSender is FunctionsRoutesSetup
s_termsOfServiceAllowList.acceptTermsOfService(STRANGER_ADDRESS, STRANGER_ADDRESS, r, s, v);
}
}

/// @notice #getBlockedSendersCount
contract FunctionsTermsOfServiceAllowList_GetBlockedSendersCount is FunctionsRoutesSetup {
function setUp() public virtual override {
FunctionsRoutesSetup.setUp();

s_termsOfServiceAllowList.blockSender(STRANGER_ADDRESS);
}

function test_GetBlockedSendersCount_Success() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

uint96 blockedSendersCount = s_termsOfServiceAllowList.getBlockedSendersCount();
// One blocked sender was made during setup
assertEq(blockedSendersCount, 1);
}
}

/// @notice #getBlockedSendersInRange
contract FunctionsTermsOfServiceAllowList_GetBlockedSendersInRange is FunctionsRoutesSetup {
function setUp() public virtual override {
FunctionsRoutesSetup.setUp();

s_termsOfServiceAllowList.blockSender(STRANGER_ADDRESS);
}

function test_GetBlockedSendersInRange_Success() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

address[] memory expectedBlockedSenders = new address[](1);
expectedBlockedSenders[0] = STRANGER_ADDRESS;

assertEq(s_termsOfServiceAllowList.getBlockedSendersInRange(0, 0), expectedBlockedSenders);
}

function test_GetBlockedSendersInRange_RevertIfAllowedSendersIsEmpty() public {
// setup a new empty s_termsOfServiceBlockList
FunctionsRoutesSetup.setUp();

// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

uint64 BlockedSendersCount = s_termsOfServiceAllowList.getBlockedSendersCount();
uint64 expected = 0;
assertEq(BlockedSendersCount, expected);

vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector);
s_termsOfServiceAllowList.getBlockedSendersInRange(0, 0);
}

function test_GetBlockedSendersInRange_RevertIfStartIsAfterEnd() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector);

s_termsOfServiceAllowList.getBlockedSendersInRange(1, 0);
}

function test_GetBlockedSendersInRange_RevertIfEndIsAfterLastAllowedSender() public {
// Send as stranger
vm.stopPrank();
vm.startPrank(STRANGER_ADDRESS);

uint64 BlockedSendersCount = s_termsOfServiceAllowList.getBlockedSendersCount();
vm.expectRevert(TermsOfServiceAllowList.InvalidCalldata.selector);
s_termsOfServiceAllowList.getBlockedSendersInRange(1, BlockedSendersCount + 1);
}
}
Loading

0 comments on commit 1022aa0

Please sign in to comment.