Skip to content

Commit

Permalink
Merge branch 'main' into smallchange
Browse files Browse the repository at this point in the history
  • Loading branch information
ping-ke committed Oct 12, 2024
2 parents e809c34 + 648e477 commit b0e0ed9
Show file tree
Hide file tree
Showing 12 changed files with 188 additions and 45 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);
}
}
4 changes: 1 addition & 3 deletions contracts/test/EthStorageContractTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ pragma solidity ^0.8.19;

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

contract EthStorageContractTest is Test {
Expand Down Expand Up @@ -63,7 +61,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
4 changes: 1 addition & 3 deletions contracts/test/StorageContractTest.t.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./TestStorageContract.sol";
import "../StorageContract.sol";
import "forge-std/Test.sol";
import "forge-std/Vm.sol";

contract StorageContractTest is Test {
uint256 constant STORAGE_COST = 10000000;
Expand Down Expand Up @@ -172,7 +170,7 @@ contract Attacker is Test {
storageContract = _storageContract;
}

fallback() external payable {
receive() external payable {
uint256 _shardId = 0;
uint256 _nonce = 0;
bytes32[] memory _encodedSamples = new bytes32[](0);
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/TestDecentralizedKV.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ contract TestDecentralizedKV is DecentralizedKV {
dataMap[kvIndices[0]] = data;
}

function get(bytes32 key, DecodeType decodeType, uint256 off, uint256 len)
function get(bytes32 key, DecodeType, /* decodeType */ uint256 off, uint256 len)
public
view
override
Expand Down
4 changes: 2 additions & 2 deletions contracts/test/TestEthStorageContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract TestEthStorageContract is EthStorageContract {
_putBatchInternal(keys, dataHashes, lengths);
}

function putBlob(bytes32 _key, uint256 _blobIdx, uint256 _length) public payable override {
function putBlob(bytes32 _key, uint256, /* _blobIdx */ uint256 _length) public payable override {
bytes32 dataHash = bytes32(uint256(1 << 8 * 8));
require(dataHash != 0, "EthStorageContract: failed to get blob hash");

Expand Down Expand Up @@ -194,7 +194,7 @@ contract TestEthStorageContract is EthStorageContract {
require(nonce < nonceLimit, "nonce too big");

// Check if the data matches the hash in metadata and obtain the solution hash.
bytes32 hash0 = verifySamples(shardId, initHash0, miner, encodedSamples, masks, inclusiveProofs, decodeProof);
verifySamples(shardId, initHash0, miner, encodedSamples, masks, inclusiveProofs, decodeProof);

uint256 diff = _calculateDiffAndInitHashSingleShard(shardId, mineTs);

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;
}
}
26 changes: 12 additions & 14 deletions contracts/test/TestMerkleLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,27 @@ contract TestMerkleLib {
return MerkleLib.merkleRootWithMinTree(data, chunkSize);
}

function merkleRootNoView(bytes memory data, uint256 chunkSize, uint256 nChunkBits) public returns (bytes32) {
function merkleRootNoView(bytes memory data, uint256 chunkSize, uint256 nChunkBits) public pure returns (bytes32) {
return MerkleLib.merkleRoot(data, chunkSize, nChunkBits);
}

function keccak256NoView(bytes memory data) public returns (bytes32) {
function keccak256NoView(bytes memory data) public pure returns (bytes32) {
return keccak256(data);
}

function verify(
bytes memory chunkData,
uint256 chunkIdx,
bytes32 root,
bytes32[] memory proofs
) public pure returns (bool) {
function verify(bytes memory chunkData, uint256 chunkIdx, bytes32 root, bytes32[] memory proofs)
public
pure
returns (bool)
{
return MerkleLib.verify(keccak256(chunkData), chunkIdx, root, proofs);
}

function getProof(
bytes memory data,
uint256 chunkSize,
uint256 nChunkBits,
uint256 chunkIdx
) public pure returns (bytes32[] memory) {
function getProof(bytes memory data, uint256 chunkSize, uint256 nChunkBits, uint256 chunkIdx)
public
pure
returns (bytes32[] memory)
{
return MerkleLib.getProof(data, chunkSize, nChunkBits, chunkIdx);
}

Expand Down
28 changes: 14 additions & 14 deletions 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 All @@ -19,13 +19,13 @@ contract TestStorageContract is StorageContract {
}

function verifySamples(
uint256 _startShardId,
bytes32 _hash0,
address _miner,
bytes32[] memory _encodedSamples,
uint256[] memory _masks,
bytes[] calldata _inclusiveProofs,
bytes[] calldata _decodeProof
uint256, /* _startShardId */
bytes32, /* _hash0 */
address, /* _miner */
bytes32[] memory, /* _encodedSamples */
uint256[] memory, /* _masks */
bytes[] calldata, /* _inclusiveProofs */
bytes[] calldata /* _decodeProof */
) public pure override returns (bytes32) {
return bytes32(0);
}
Expand All @@ -50,12 +50,12 @@ contract TestStorageContract is StorageContract {
uint256 _blockNum,
uint256 _shardId,
address _miner,
uint256 _nonce,
bytes32[] memory _encodedSamples,
uint256[] memory _masks,
bytes calldata _randaoProof,
bytes[] calldata _inclusiveProofs,
bytes[] calldata _decodeProof
uint256, /* _nonce */
bytes32[] memory, /* _encodedSamples */
uint256[] memory, /* _masks */
bytes calldata, /* _randaoProof */
bytes[] calldata, /* _inclusiveProofs */
bytes[] calldata /* _decodeProof */
) internal override {
uint256 mineTs = _getMinedTs(_blockNum);
_rewardMiner(_shardId, _miner, mineTs, 1);
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
Loading

0 comments on commit b0e0ed9

Please sign in to comment.