Skip to content

Commit

Permalink
Merge pull request #112 from ethstorage/upload_limit
Browse files Browse the repository at this point in the history
Update limit for L2
  • Loading branch information
syntrust authored Oct 10, 2024
2 parents f009018 + 740ff61 commit 0d52b2c
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 10 deletions.
6 changes: 6 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 if the key-values being updated exceed the limit per block (L2 only).
function _checkUpdateLimit(uint256 _updateSize) 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,9 @@ contract DecentralizedKV is OwnableUpgradeable {
}

_checkAppend(batchPaymentSize);
if (_keys.length > batchPaymentSize) {
_checkUpdateLimit(_keys.length - batchPaymentSize);
}

return res;
}
Expand Down
37 changes: 32 additions & 5 deletions contracts/EthStorageContractL2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,33 @@ contract EthStorageContractL2 is EthStorageContract2 {
/// @notice The precompile contract address for L1Block.
IL1Block internal constant L1_BLOCK = IL1Block(0x4200000000000000000000000000000000000015);

/// @notice The mask to extract `blockLastUpdate`
uint256 internal constant MASK = ~uint256(0) ^ type(uint32).max;

/// @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 +68,16 @@ contract EthStorageContractL2 is EthStorageContract2 {
require(bh != bytes32(0), "EthStorageContractL2: failed to obtain blockhash");
return RandaoLib.verifyHeaderAndGetRandao(bh, _headerRlpBytes);
}

/// @notice Check if the key-values being updated exceed the limit per block.
function _checkUpdateLimit(uint256 _updateSize) internal override {
uint256 blobsUpdated = updateState & MASK == block.number << 32 ? updateState & type(uint32).max : 0;
require(blobsUpdated + _updateSize <= UPDATE_LIMIT, "EthStorageContractL2: exceeds update rate limit");
updateState = block.number << 32 | (blobsUpdated + _updateSize);
}

/// @notice Getter for UPDATE_LIMIT
function getUpdateLimit() public view returns (uint256) {
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(), 0);

// 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

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

0 comments on commit 0d52b2c

Please sign in to comment.