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

Update limit for L2 #112

Merged
merged 12 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contracts/DecentralizedKV.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ contract DecentralizedKV is OwnableUpgradeable {
require(msg.value >= upfrontPayment() * _batchSize, "DecentralizedKV: not enough batch payment");
}

/// @notice Check the update rate limit of blobs put (L2 only).
syntrust marked this conversation as resolved.
Show resolved Hide resolved
function _checkUpdateLimit(uint256 _blobs) internal virtual {}

/// @notice Called by public putBlob and putBlobs methods.
/// @param _keys Keys of the data.
/// @param _dataHashes Hashes of the data.
Expand Down Expand Up @@ -153,6 +156,7 @@ contract DecentralizedKV is OwnableUpgradeable {
}

_checkAppend(batchPaymentSize);
_checkUpdateLimit(_keys.length - batchPaymentSize);

return res;
}
Expand Down
34 changes: 29 additions & 5 deletions contracts/EthStorageContractL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,30 @@ interface IL1Block {
contract EthStorageContractL2 is EthStorageContract2 {
/// @notice The precompile contract address for L1Block.
IL1Block internal constant L1_BLOCK = IL1Block(0x4200000000000000000000000000000000000015);
/// @notice The rate limit to update blobs per block
uint256 internal immutable UPDATE_LIMIT;

/// @notice A slot to store both `blockLastUpdate` (left 224) and `blobsUpdated` (right 32)
uint256 internal updateState;

/// @notice Constructs the EthStorageContractL2 contract.
constructor(Config memory _config, uint256 _startTime, uint256 _storageCost, uint256 _dcfFactor)
EthStorageContract2(_config, _startTime, _storageCost, _dcfFactor)
{}
constructor(
Config memory _config,
uint256 _startTime,
uint256 _storageCost,
uint256 _dcfFactor,
uint256 _updateLimit
) EthStorageContract2(_config, _startTime, _storageCost, _dcfFactor) {
UPDATE_LIMIT = _updateLimit;
}

/// @notice Get the current block number
function _blockNumber() internal view override returns (uint256) {
function _blockNumber() internal view virtual override returns (uint256) {
return L1_BLOCK.number();
}

/// @notice Get the current block timestamp
function _blockTs() internal view override returns (uint256) {
function _blockTs() internal view virtual override returns (uint256) {
return L1_BLOCK.timestamp();
}

Expand All @@ -53,4 +64,17 @@ contract EthStorageContractL2 is EthStorageContract2 {
require(bh != bytes32(0), "EthStorageContractL2: failed to obtain blockhash");
return RandaoLib.verifyHeaderAndGetRandao(bh, _headerRlpBytes);
}

/// @notice Check the update rate limit of blobs put.
function _checkUpdateLimit(uint256 _blobs) internal override {
uint256 blockLastUpdate = updateState >> 32;
uint256 blobsUpdated = blockLastUpdate == block.number ? updateState & type(uint32).max : 0;
syntrust marked this conversation as resolved.
Show resolved Hide resolved
require(blobsUpdated + _blobs <= UPDATE_LIMIT, "EthStorageContractL2: exceeds update rate limit");
updateState = block.number << 32 | (blobsUpdated + _blobs);
}

/// @notice Getter for UPDATE_LIMIT
function getUpdateLimit() public view returns (uint256) {
syntrust marked this conversation as resolved.
Show resolved Hide resolved
return UPDATE_LIMIT;
}
}
79 changes: 79 additions & 0 deletions contracts/test/EthStorageContractL2Test.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "./TestEthStorageContractL2.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

contract EthStorageContractL2Test is Test {
uint256 constant STORAGE_COST = 0;
uint256 constant SHARD_SIZE_BITS = 19;
uint256 constant MAX_KV_SIZE = 17;
uint256 constant PREPAID_AMOUNT = 0;
uint256 constant UPDATE_LIMIT = 16;

TestEthStorageContractL2 storageContract;
address owner = address(0x1);

function setUp() public {
TestEthStorageContractL2 imp = new TestEthStorageContractL2(
StorageContract.Config(MAX_KV_SIZE, SHARD_SIZE_BITS, 2, 0, 0, 0), 0, STORAGE_COST, 0, UPDATE_LIMIT
);
bytes memory data = abi.encodeWithSelector(
storageContract.initialize.selector, 0, PREPAID_AMOUNT, 0, address(0x1), address(0x1)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(address(imp), owner, data);

storageContract = TestEthStorageContractL2(address(proxy));
}

function testUpdateLimit() public {
uint256 size = 6;
bytes32[] memory hashes = new bytes32[](size);
bytes32[] memory keys = new bytes32[](size);
uint256[] memory blobIdxs = new uint256[](size);
uint256[] memory lengths = new uint256[](size);
for (uint256 i = 0; i < size; i++) {
keys[i] = bytes32(uint256(i));
hashes[i] = bytes32(uint256((i + 1) << 64));
blobIdxs[i] = i;
lengths[i] = 10 + i * 10;
}
vm.blobhashes(hashes);
vm.roll(10000);

// No updates
storageContract.putBlobs(keys, blobIdxs, lengths);
assertEq(storageContract.getBlobsUpdated(), 0);
assertEq(storageContract.getBlockLastUpdate(), 10000);

// Append 1 new key-values, leaving 5 as updating
keys[0] = bytes32(uint256(10));
storageContract.putBlobs(keys, blobIdxs, lengths);
assertEq(storageContract.getBlobsUpdated(), 5);
assertEq(storageContract.getBlockLastUpdate(), 10000);

// Update all 6
storageContract.putBlobs(keys, blobIdxs, lengths);
assertEq(storageContract.getBlobsUpdated(), 11);

// Update all 6 again, exceeds UPDATE_LIMIT = 16
vm.expectRevert("EthStorageContractL2: exceeds update rate limit");
storageContract.putBlobs(keys, blobIdxs, lengths);
assertEq(storageContract.getBlockLastUpdate(), 10000);

vm.roll(block.number + 1);

// Update all 6
storageContract.putBlobs(keys, blobIdxs, lengths);
assertEq(storageContract.getBlobsUpdated(), 6);
assertEq(storageContract.getBlockLastUpdate(), 10001);

// Update till exceeds UPDATE_LIMIT = 16
storageContract.putBlobs(keys, blobIdxs, lengths);
assertEq(storageContract.getBlobsUpdated(), 12);
assertEq(storageContract.getBlockLastUpdate(), 10001);
vm.expectRevert("EthStorageContractL2: exceeds update rate limit");
storageContract.putBlobs(keys, blobIdxs, lengths);
}
}
2 changes: 1 addition & 1 deletion contracts/test/EthStorageContractTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ contract EthStorageContractTest is Test {
keys[1] = bytes32(uint256(1));
uint256[] memory blobIdxs = new uint256[](2);
blobIdxs[0] = 0;
blobIdxs[1] = 0;
blobIdxs[1] = 1;
uint256[] memory lengths = new uint256[](2);
lengths[0] = 10;
lengths[1] = 20;
Expand Down
33 changes: 33 additions & 0 deletions contracts/test/TestEthStorageContractL2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../EthStorageContractL2.sol";

contract TestEthStorageContractL2 is EthStorageContractL2 {
constructor(
Config memory _config,
uint256 _startTime,
uint256 _storageCost,
uint256 _dcfFactor,
uint256 _updateLimit
) EthStorageContractL2(_config, _startTime, _storageCost, _dcfFactor, _updateLimit) {}

/// @notice Get the number of blobs updated within the current block.
function getBlobsUpdated() public view returns (uint256) {
return updateState & type(uint32).max;
}

/// @notice Get the block number of the last update.
function getBlockLastUpdate() public view returns (uint256) {
return updateState >> 32;
}

function _blockNumber() internal view virtual override returns (uint256) {
return block.number;
}

/// @notice Get the current block timestamp
function _blockTs() internal view virtual override returns (uint256) {
return block.timestamp;
}
}
2 changes: 1 addition & 1 deletion contracts/test/TestStorageContract.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX License Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../StorageContract.sol";
Expand Down
8 changes: 5 additions & 3 deletions scripts/deployL2-it.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const config = [
];
const storageCost = 1500000000000000; // storageCost - 1,500,000Gwei forever per blob - https://ethresear.ch/t/ethstorage-scaling-ethereum-storage-via-l2-and-da/14223/6#incentivization-for-storing-m-physical-replicas-1
const dcfFactor = 340282366367469178095360967382638002176n; // dcfFactor, it mean 0.95 for yearly discount
const updateLimit = 90; // 45 blobs/s according to sync/encoding test, times block interval of L2
syntrust marked this conversation as resolved.
Show resolved Hide resolved

async function verifyContract(contract, args) {
// if (!process.env.ETHERSCAN_API_KEY) {
Expand All @@ -45,6 +46,7 @@ async function deployContract() {
startTime, // startTime
storageCost,
dcfFactor,
updateLimit,
{ gasPrice: gasPrice }
);
await implContract.deployed();
Expand Down Expand Up @@ -85,21 +87,21 @@ async function deployContract() {
await verifyContract(impl, [config, startTime, storageCost, dcfFactor]);

// wait for contract finalized
var intervalId = setInterval(async function (){
var intervalId = setInterval(async function () {
try {
const block = await hre.ethers.provider.getBlock("finalized");
console.log(
"finalized block number is",
block.number,
"at",
new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" })
);
);
if (receipt.blockNumber < block.number) {
fs.writeFileSync(".caddr", ethStorageProxy.address);
clearInterval(intervalId);
}
} catch (e) {
console.error(`EthStorage: get finalized block failed!`, e.message);g
console.error(`EthStorage: get finalized block failed!`, e.message);
}
}, 60000);
}
Expand Down
2 changes: 2 additions & 0 deletions scripts/deployL2.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const config = [
];
const storageCost = 1500000000000000; // storageCost - 1,500,000Gwei forever per blob - https://ethresear.ch/t/ethstorage-scaling-ethereum-storage-via-l2-and-da/14223/6#incentivization-for-storing-m-physical-replicas-1
const dcfFactor = 340282366367469178095360967382638002176n; // dcfFactor, it mean 0.95 for yearly discount
const updateLimit = 90; // 45 blobs/s according to sync/encoding test, times block interval of L2

async function verifyContract(contract, args) {
// if (!process.env.ETHERSCAN_API_KEY) {
Expand All @@ -44,6 +45,7 @@ async function deployContract() {
startTime, // startTime
storageCost,
dcfFactor,
updateLimit,
{ gasPrice: gasPrice }
);
await implContract.deployed();
Expand Down
Loading