From 30eb29ef5faabef3d5a7835db8bca12425109c67 Mon Sep 17 00:00:00 2001 From: syntrust Date: Fri, 16 Aug 2024 10:51:23 +0800 Subject: [PATCH 01/16] fix xshard --- contracts/StorageContract.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index ea9aea4..ef28888 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -147,9 +147,9 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Checks the payment using the last mine time. function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { - uint256 totalEntries = kvEntryCount + 1; // include the one to be put - uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard id of the new KV - if ((totalEntries % (1 << SHARD_ENTRY_BITS)) == 1) { + uint256 totalEntries = kvEntryCount + _batchSize; // include the one to be put + uint256 shardId = totalEntries >> SHARD_ENTRY_BITS; // shard id of the new KV + if (shardId - (kvEntryCount >> SHARD_ENTRY_BITS) == 1) { // Open a new shard if the KV is the first one of the shard // and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? @@ -160,7 +160,7 @@ abstract contract StorageContract is DecentralizedKV { } require( - msg.value >= _upfrontPayment(infos[shardId].lastMineTime) * _batchSize, + msg.value >= upfrontPayment() * _batchSize, "StorageContract: not enough batch payment" ); } @@ -168,7 +168,7 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Upfront payment for the next insertion function upfrontPayment() public view virtual override returns (uint256) { uint256 totalEntries = kvEntryCount + 1; // include the one to be put - uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard id of the new KV + uint256 shardId = totalEntries >> SHARD_ENTRY_BITS; // shard id of the new KV // shard0 is already opened in constructor if ((totalEntries % (1 << SHARD_ENTRY_BITS)) == 1 && shardId != 0) { // Open a new shard if the KV is the first one of the shard From 68953c7bb44f4daabb4fd66e02df3eee31ca826c Mon Sep 17 00:00:00 2001 From: syntrust Date: Fri, 16 Aug 2024 11:46:46 +0800 Subject: [PATCH 02/16] comments --- contracts/StorageContract.sol | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index ef28888..4a95bc0 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -147,11 +147,10 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Checks the payment using the last mine time. function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { - uint256 totalEntries = kvEntryCount + _batchSize; // include the one to be put - uint256 shardId = totalEntries >> SHARD_ENTRY_BITS; // shard id of the new KV - if (shardId - (kvEntryCount >> SHARD_ENTRY_BITS) == 1) { - // Open a new shard if the KV is the first one of the shard - // and mark the shard is ready to mine. + uint256 totalEntries = kvEntryCount + _batchSize; // include the batch to be put + uint256 shardId = totalEntries >> SHARD_ENTRY_BITS; // shard id after the batch + if (shardId > (kvEntryCount >> SHARD_ENTRY_BITS)) { + // Open a new shard and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? if (shardId != 0) { // shard0 is already opened in constructor From ffbcb0472fd4aa1c3bb98b21bdc383a372275866 Mon Sep 17 00:00:00 2001 From: syntrust Date: Fri, 16 Aug 2024 15:10:37 +0800 Subject: [PATCH 03/16] fix totalPayment --- contracts/StorageContract.sol | 48 ++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 4a95bc0..2372289 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -100,9 +100,12 @@ abstract contract StorageContract is DecentralizedKV { ); /// @notice Constructs the StorageContract contract. Initializes the storage config. - constructor(Config memory _config, uint256 _startTime, uint256 _storageCost, uint256 _dcfFactor) - DecentralizedKV(1 << _config.maxKvSizeBits, _startTime, _storageCost, _dcfFactor) - { + constructor( + Config memory _config, + uint256 _startTime, + uint256 _storageCost, + uint256 _dcfFactor + ) DecentralizedKV(1 << _config.maxKvSizeBits, _startTime, _storageCost, _dcfFactor) { /* Assumptions */ require(_config.shardSizeBits >= _config.maxKvSizeBits, "StorageContract: shardSize too small"); require(_config.maxKvSizeBits >= SAMPLE_SIZE_BITS, "StorageContract: maxKvSize too small"); @@ -147,31 +150,35 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Checks the payment using the last mine time. function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { + uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; uint256 totalEntries = kvEntryCount + _batchSize; // include the batch to be put - uint256 shardId = totalEntries >> SHARD_ENTRY_BITS; // shard id after the batch - if (shardId > (kvEntryCount >> SHARD_ENTRY_BITS)) { + uint256 shardIdNew = totalEntries >> SHARD_ENTRY_BITS; // shard id after the batch + uint256 totalPayment = 0; + + if (shardIdNew > shardId) { + uint256 kvCountNew = totalEntries % (1 << SHARD_ENTRY_BITS); + totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; + totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); // Open a new shard and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? - if (shardId != 0) { + if (shardIdNew != 0) { // shard0 is already opened in constructor - infos[shardId].lastMineTime = _timestamp; + infos[shardIdNew].lastMineTime = _timestamp; } + } else { + totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; } - require( - msg.value >= upfrontPayment() * _batchSize, - "StorageContract: not enough batch payment" - ); + require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); } /// @notice Upfront payment for the next insertion function upfrontPayment() public view virtual override returns (uint256) { uint256 totalEntries = kvEntryCount + 1; // include the one to be put - uint256 shardId = totalEntries >> SHARD_ENTRY_BITS; // shard id of the new KV + uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard0 is already opened in constructor if ((totalEntries % (1 << SHARD_ENTRY_BITS)) == 1 && shardId != 0) { // Open a new shard if the KV is the first one of the shard - // and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? return _upfrontPayment(block.timestamp); } else { @@ -210,11 +217,10 @@ abstract contract StorageContract is DecentralizedKV { /// @param _shardId The shard id. /// @param _minedTs The mined timestamp. /// @return diff_ The difficulty of the shard. - function _calculateDiffAndInitHashSingleShard(uint256 _shardId, uint256 _minedTs) - internal - view - returns (uint256 diff_) - { + function _calculateDiffAndInitHashSingleShard( + uint256 _shardId, + uint256 _minedTs + ) internal view returns (uint256 diff_) { MiningLib.MiningInfo storage info = infos[_shardId]; require(_minedTs >= info.lastMineTime, "StorageContract: minedTs too small"); diff_ = MiningLib.expectedDiff(info, _minedTs, CUTOFF, DIFF_ADJ_DIVISOR, minimumDiff); @@ -260,8 +266,8 @@ abstract contract StorageContract is DecentralizedKV { reward = _paymentIn(STORAGE_COST * (kvEntryCount % (1 << SHARD_ENTRY_BITS)), info.lastMineTime, _minedTs); // Additional prepaid for the last shard if (prepaidLastMineTime < _minedTs) { - uint256 prepaidAmountCap = - STORAGE_COST * ((1 << SHARD_ENTRY_BITS) - kvEntryCount % (1 << SHARD_ENTRY_BITS)); + uint256 prepaidAmountCap = STORAGE_COST * + ((1 << SHARD_ENTRY_BITS) - (kvEntryCount % (1 << SHARD_ENTRY_BITS))); if (prepaidAmountCap > prepaidAmount) { prepaidAmountCap = prepaidAmount; } @@ -281,7 +287,7 @@ abstract contract StorageContract is DecentralizedKV { /// @return The mining reward. function miningReward(uint256 _shardId, uint256 _blockNumber) public view returns (uint256) { uint256 minedTs = block.timestamp - (block.number - _blockNumber) * 12; - (,, uint256 minerReward) = _miningReward(_shardId, minedTs); + (, , uint256 minerReward) = _miningReward(_shardId, minedTs); return minerReward; } From 57854d808436288252b28beb178385246de900e0 Mon Sep 17 00:00:00 2001 From: syntrust Date: Fri, 16 Aug 2024 19:34:30 +0800 Subject: [PATCH 04/16] batch payment --- contracts/DecentralizedKV.sol | 8 ++++- contracts/StorageContract.sol | 36 ++++++++++++--------- contracts/test/EthStorageContractTest.t.sol | 26 ++++++++++++++- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/contracts/DecentralizedKV.sol b/contracts/DecentralizedKV.sol index caaa383..a387552 100644 --- a/contracts/DecentralizedKV.sol +++ b/contracts/DecentralizedKV.sol @@ -103,9 +103,10 @@ contract DecentralizedKV is OwnableUpgradeable { function _paymentIn(uint256 _x, uint256 _fromTs, uint256 _toTs) internal view returns (uint256) { return _paymentInInterval(_x, _fromTs - START_TIME, _toTs - START_TIME); } - + /// @notice Evaluate payment given the timestamp. function _upfrontPayment(uint256 _ts) internal view returns (uint256) { + require(_ts >= START_TIME, "DecentralizedKV: invalid timestamp to evaluate payment"); return _paymentInf(STORAGE_COST, _ts - START_TIME); } @@ -114,6 +115,11 @@ contract DecentralizedKV is OwnableUpgradeable { return _upfrontPayment(block.timestamp); } + /// @notice Upfront payment for the next batch insertion + function upfrontPaymentInBatch(uint256 _batchSize) public view virtual returns (uint256) { + return upfrontPayment() * _batchSize; + } + /// @notice Checks before appending the key-value. function _prepareAppend(uint256 _batchSize) internal virtual { require(msg.value >= upfrontPayment() * _batchSize, "DecentralizedKV: not enough batch payment"); diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 2372289..2acdf54 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -150,26 +150,15 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Checks the payment using the last mine time. function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { - uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; - uint256 totalEntries = kvEntryCount + _batchSize; // include the batch to be put - uint256 shardIdNew = totalEntries >> SHARD_ENTRY_BITS; // shard id after the batch - uint256 totalPayment = 0; + uint256 totalPayment = upfrontPaymentInBatch(_batchSize); + require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); - if (shardIdNew > shardId) { - uint256 kvCountNew = totalEntries % (1 << SHARD_ENTRY_BITS); - totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; - totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); + uint256 shardIdNew = (kvEntryCount + _batchSize) >> SHARD_ENTRY_BITS; // shard id after the batch + if (shardIdNew > (kvEntryCount >> SHARD_ENTRY_BITS)) { // Open a new shard and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? - if (shardIdNew != 0) { - // shard0 is already opened in constructor - infos[shardIdNew].lastMineTime = _timestamp; - } - } else { - totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; + infos[shardIdNew].lastMineTime = _timestamp; } - - require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); } /// @notice Upfront payment for the next insertion @@ -186,6 +175,21 @@ abstract contract StorageContract is DecentralizedKV { } } + /// @notice Upfront payment for a batch insertion + function upfrontPaymentInBatch(uint256 _batchSize) public view virtual override returns (uint256) { + uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; + uint256 totalEntries = kvEntryCount + _batchSize; // include the batch to be put + uint256 shardIdNew = totalEntries >> SHARD_ENTRY_BITS; // shard id after the batch + uint256 totalPayment = 0; + if (shardIdNew > shardId) { + uint256 kvCountNew = totalEntries % (1 << SHARD_ENTRY_BITS); + totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); + } else { + totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; + } + return totalPayment; + } + /// @inheritdoc DecentralizedKV function _prepareAppend(uint256 _batchSize) internal virtual override { return _prepareAppendWithTimestamp(block.timestamp, _batchSize); diff --git a/contracts/test/EthStorageContractTest.t.sol b/contracts/test/EthStorageContractTest.t.sol index a9f0575..cef6015 100644 --- a/contracts/test/EthStorageContractTest.t.sol +++ b/contracts/test/EthStorageContractTest.t.sol @@ -75,7 +75,7 @@ contract EthStorageContractTest is Test { storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); // Enough storage cost - uint256 sufficientCost = 2 * storageContract.upfrontPayment(); + uint256 sufficientCost = storageContract.upfrontPaymentInBatch(2); storageContract.putBlobs{value: sufficientCost}(keys, blobIdxs, lengths); assertEq(storageContract.kvEntryCount(), 2); @@ -99,4 +99,28 @@ contract EthStorageContractTest is Test { assertEq(storageContract.size(bytes32(uint256(1))), 20); assertEq(storageContract.size(bytes32(uint256(2))), 30); } + + function testPutBlobsXshard() public { + uint256 size = 5; + 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)); + blobIdxs[i] = i; + lengths[i] = 10 + i*10; + } + vm.warp(vm.unixTime()); + + // uint256 insufficientCost = storageContract.upfrontPaymentInBatch(size - 1); + // // Expect the specific revert reason from _prepareBatchAppend due to insufficient msg.value + // vm.expectRevert("StorageContract: not enough batch payment"); + // storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); + + // Enough storage cost + uint256 sufficientCost = storageContract.upfrontPaymentInBatch(size); + storageContract.putBlobs{value: sufficientCost}(keys, blobIdxs, lengths); + + assertEq(storageContract.kvEntryCount(), size); + } } From 643796325ea38d529ab115ddd46860d7a1af3e49 Mon Sep 17 00:00:00 2001 From: syntrust Date: Mon, 19 Aug 2024 17:53:09 +0800 Subject: [PATCH 05/16] fix compute payment on putBlobs --- contracts/StorageContract.sol | 19 +++++++++++++------ contracts/test/EthStorageContractTest.t.sol | 12 ++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 2acdf54..e845722 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -150,15 +150,21 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Checks the payment using the last mine time. function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { - uint256 totalPayment = upfrontPaymentInBatch(_batchSize); - require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); - - uint256 shardIdNew = (kvEntryCount + _batchSize) >> SHARD_ENTRY_BITS; // shard id after the batch - if (shardIdNew > (kvEntryCount >> SHARD_ENTRY_BITS)) { + uint256 shardId = (kvEntryCount - _batchSize) >> SHARD_ENTRY_BITS; + uint256 shardIdNew = kvEntryCount >> SHARD_ENTRY_BITS; // shard id after the batch + uint256 totalPayment = 0; + if (shardIdNew > shardId) { + uint256 kvCountNew = kvEntryCount % (1 << SHARD_ENTRY_BITS); + totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; + totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); // Open a new shard and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? infos[shardIdNew].lastMineTime = _timestamp; + } else { + totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; } + + require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); } /// @notice Upfront payment for the next insertion @@ -183,7 +189,8 @@ abstract contract StorageContract is DecentralizedKV { uint256 totalPayment = 0; if (shardIdNew > shardId) { uint256 kvCountNew = totalEntries % (1 << SHARD_ENTRY_BITS); - totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); + totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; + totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); } else { totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; } diff --git a/contracts/test/EthStorageContractTest.t.sol b/contracts/test/EthStorageContractTest.t.sol index cef6015..059d5bd 100644 --- a/contracts/test/EthStorageContractTest.t.sol +++ b/contracts/test/EthStorageContractTest.t.sol @@ -110,13 +110,13 @@ contract EthStorageContractTest is Test { blobIdxs[i] = i; lengths[i] = 10 + i*10; } - vm.warp(vm.unixTime()); - - // uint256 insufficientCost = storageContract.upfrontPaymentInBatch(size - 1); - // // Expect the specific revert reason from _prepareBatchAppend due to insufficient msg.value - // vm.expectRevert("StorageContract: not enough batch payment"); - // storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); + vm.warp(0); + uint256 insufficientCost = storageContract.upfrontPaymentInBatch(size) - 1; + // Expect the specific revert reason from _prepareBatchAppend due to insufficient msg.value + vm.expectRevert("StorageContract: not enough batch payment"); + storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); + // Enough storage cost uint256 sufficientCost = storageContract.upfrontPaymentInBatch(size); storageContract.putBlobs{value: sufficientCost}(keys, blobIdxs, lengths); From f4846624605cdbe0fa699973bc594112db4c1ec2 Mon Sep 17 00:00:00 2001 From: syntrust Date: Mon, 19 Aug 2024 18:35:58 +0800 Subject: [PATCH 06/16] refactor --- contracts/StorageContract.sol | 29 ++++++++++----------- contracts/test/EthStorageContractTest.t.sol | 7 +++-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index e845722..e80dfea 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -150,21 +150,16 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Checks the payment using the last mine time. function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { - uint256 shardId = (kvEntryCount - _batchSize) >> SHARD_ENTRY_BITS; - uint256 shardIdNew = kvEntryCount >> SHARD_ENTRY_BITS; // shard id after the batch - uint256 totalPayment = 0; - if (shardIdNew > shardId) { - uint256 kvCountNew = kvEntryCount % (1 << SHARD_ENTRY_BITS); - totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; - totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); + uint256 kvEntryCountOld = kvEntryCount - _batchSize; + uint256 totalPayment = _upfrontPaymentInBatch(kvEntryCountOld, _batchSize); + require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); + uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard id after the batch + if (shardId > (kvEntryCountOld >> SHARD_ENTRY_BITS)) { // Open a new shard and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? - infos[shardIdNew].lastMineTime = _timestamp; - } else { - totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; + infos[shardId].lastMineTime = _timestamp; } - require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); } /// @notice Upfront payment for the next insertion @@ -183,11 +178,15 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Upfront payment for a batch insertion function upfrontPaymentInBatch(uint256 _batchSize) public view virtual override returns (uint256) { - uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; - uint256 totalEntries = kvEntryCount + _batchSize; // include the batch to be put - uint256 shardIdNew = totalEntries >> SHARD_ENTRY_BITS; // shard id after the batch + return _upfrontPaymentInBatch(kvEntryCount, _batchSize); + } + + /// @notice Upfront payment for a batch insertion + function _upfrontPaymentInBatch(uint256 _kvEntryCount, uint256 _batchSize) private view returns (uint256) { + uint256 shardId = _kvEntryCount >> SHARD_ENTRY_BITS; + uint256 totalEntries = _kvEntryCount + _batchSize; // include the batch to be put uint256 totalPayment = 0; - if (shardIdNew > shardId) { + if ((totalEntries >> SHARD_ENTRY_BITS) > shardId) { uint256 kvCountNew = totalEntries % (1 << SHARD_ENTRY_BITS); totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); diff --git a/contracts/test/EthStorageContractTest.t.sol b/contracts/test/EthStorageContractTest.t.sol index 059d5bd..0f9466f 100644 --- a/contracts/test/EthStorageContractTest.t.sol +++ b/contracts/test/EthStorageContractTest.t.sol @@ -101,7 +101,7 @@ contract EthStorageContractTest is Test { } function testPutBlobsXshard() public { - uint256 size = 5; + uint256 size = 6; bytes32[] memory keys = new bytes32[](size); uint256[] memory blobIdxs = new uint256[](size); uint256[] memory lengths = new uint256[](size); @@ -112,15 +112,14 @@ contract EthStorageContractTest is Test { } vm.warp(0); - uint256 insufficientCost = storageContract.upfrontPaymentInBatch(size) - 1; + uint256 sufficientCost = storageContract.upfrontPaymentInBatch(size); + uint256 insufficientCost = sufficientCost - 1; // Expect the specific revert reason from _prepareBatchAppend due to insufficient msg.value vm.expectRevert("StorageContract: not enough batch payment"); storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); // Enough storage cost - uint256 sufficientCost = storageContract.upfrontPaymentInBatch(size); storageContract.putBlobs{value: sufficientCost}(keys, blobIdxs, lengths); - assertEq(storageContract.kvEntryCount(), size); } } From 6905633e2b8b3db3b71c9c973cce626a71a423f1 Mon Sep 17 00:00:00 2001 From: syntrust Date: Mon, 19 Aug 2024 18:50:23 +0800 Subject: [PATCH 07/16] comments --- contracts/StorageContract.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index e80dfea..1683de5 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -150,7 +150,7 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Checks the payment using the last mine time. function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { - uint256 kvEntryCountOld = kvEntryCount - _batchSize; + uint256 kvEntryCountOld = kvEntryCount - _batchSize; // kvEntryCount already increased uint256 totalPayment = _upfrontPaymentInBatch(kvEntryCountOld, _batchSize); require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard id after the batch @@ -159,7 +159,6 @@ abstract contract StorageContract is DecentralizedKV { // (TODO): Setup shard difficulty as current difficulty / factor? infos[shardId].lastMineTime = _timestamp; } - } /// @notice Upfront payment for the next insertion @@ -177,6 +176,8 @@ abstract contract StorageContract is DecentralizedKV { } /// @notice Upfront payment for a batch insertion + /// @param _batchSize The blob count for a batch insertion. + /// @return The total payment for a batch insertion. function upfrontPaymentInBatch(uint256 _batchSize) public view virtual override returns (uint256) { return _upfrontPaymentInBatch(kvEntryCount, _batchSize); } From 7ce5478ede08a1b295d17eaf2e95e8370a73c0b7 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 20 Aug 2024 10:41:04 +0800 Subject: [PATCH 08/16] abstract block info --- contracts/EthStorageContractL2.sol | 60 ++++++----------------- contracts/StorageContract.sol | 37 +++++++++++--- contracts/libraries/RandaoLib.sol | 13 ----- contracts/test/TestEthStorageContract.sol | 9 ++-- contracts/test/TestRandao.sol | 4 -- test/randao-test.js | 4 +- 6 files changed, 48 insertions(+), 79 deletions(-) diff --git a/contracts/EthStorageContractL2.sol b/contracts/EthStorageContractL2.sol index a3017b8..5bb8efd 100644 --- a/contracts/EthStorageContractL2.sol +++ b/contracts/EthStorageContractL2.sol @@ -35,55 +35,23 @@ contract EthStorageContractL2 is EthStorageContract2 { uint256 _dcfFactor ) EthStorageContract2(_config, _startTime, _storageCost, _dcfFactor) {} + /// @notice Get the current block number + function blockNumber() internal view override returns (uint256) { + return L1_BLOCK.number(); + } + + /// @notice Get the current block timestamp + function blockTs() internal view override returns (uint256) { + return L1_BLOCK.timestamp(); + } + /// @notice Get the randao value from the L1 blockhash. - function _getRandao(uint256 _l1BlockNumber, bytes calldata _headerRlpBytes) internal view returns (bytes32) { + function getRandao( + uint256 _l1BlockNumber, + bytes calldata _headerRlpBytes + ) internal view override returns (bytes32) { bytes32 bh = L1_BLOCK.blockHash(_l1BlockNumber); require(bh != bytes32(0), "EthStorageContractL2: failed to obtain blockhash"); - return RandaoLib.verifyHeaderAndGetRandao(bh, _headerRlpBytes); } - - /// @notice We are still using L1 block number, timestamp, and blockhash to mine eventhough we are on L2. - /// @param _blockNumber L1 blocknumber. - /// @param _shardId Shard ID. - /// @param _miner Miner address. - /// @param _nonce Nonce. - /// @param _encodedSamples Encoded samples. - /// @param _masks Sample masks. - /// @param _randaoProof L1 block header RLP bytes. - /// @param _inclusiveProofs Sample inclusive proofs. - /// @param _decodeProof Mask decode proof. - function _mine( - uint256 _blockNumber, - uint256 _shardId, - address _miner, - uint256 _nonce, - bytes32[] memory _encodedSamples, - uint256[] memory _masks, - bytes calldata _randaoProof, - bytes[] calldata _inclusiveProofs, - bytes[] calldata _decodeProof - ) internal override { - // Obtain the blockhash of the block number of recent blocks - require(L1_BLOCK.number() - _blockNumber <= MAX_L1_MINING_DRIFT, "EthStorageContractL2: block number too old"); - // To avoid stack too deep, we resue the hash0 instead of using randao - - bytes32 hash0 = _getRandao(_blockNumber, _randaoProof); - // Estimate block timestamp - uint256 mineTs = L1_BLOCK.timestamp() - (L1_BLOCK.number() - _blockNumber) * 12; - - // Given a blockhash and a miner, we only allow sampling up to nonce limit times. - require(_nonce < nonceLimit, "EthStorageContractL2: nonce too big"); - - // Check if the data matches the hash in metadata and obtain the solution hash. - hash0 = keccak256(abi.encode(_miner, hash0, _nonce)); - hash0 = verifySamples(_shardId, hash0, _miner, _encodedSamples, _masks, _inclusiveProofs, _decodeProof); - - // Check difficulty - uint256 diff = _calculateDiffAndInitHashSingleShard(_shardId, mineTs); - uint256 required = uint256(2 ** 256 - 1) / diff; - require(uint256(hash0) <= required, "EthStorageContractL2: diff not match"); - - _rewardMiner(_shardId, _miner, mineTs, diff); - } } diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index ea9aea4..c85a2f6 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -174,7 +174,7 @@ abstract contract StorageContract is DecentralizedKV { // Open a new shard if the KV is the first one of the shard // and mark the shard is ready to mine. // (TODO): Setup shard difficulty as current difficulty / factor? - return _upfrontPayment(block.timestamp); + return _upfrontPayment(blockTs()); } else { return _upfrontPayment(infos[shardId].lastMineTime); } @@ -182,7 +182,7 @@ abstract contract StorageContract is DecentralizedKV { /// @inheritdoc DecentralizedKV function _prepareAppend(uint256 _batchSize) internal virtual override { - return _prepareAppendWithTimestamp(block.timestamp, _batchSize); + return _prepareAppendWithTimestamp(blockTs(), _batchSize); } /// @notice Verify the samples of the BLOBs by the miner (storage provider) including @@ -281,7 +281,7 @@ abstract contract StorageContract is DecentralizedKV { /// @param _blockNumber The block number. /// @return The mining reward. function miningReward(uint256 _shardId, uint256 _blockNumber) public view returns (uint256) { - uint256 minedTs = block.timestamp - (block.number - _blockNumber) * 12; + uint256 minedTs = getMinedTs(_blockNumber); (,, uint256 minerReward) = _miningReward(_shardId, minedTs); return minerReward; } @@ -359,13 +359,12 @@ abstract contract StorageContract is DecentralizedKV { bytes calldata _randaoProof, bytes[] calldata _inclusiveProofs, bytes[] calldata _decodeProof - ) internal virtual { - // Obtain the blockhash of the block number of recent blocks - require(block.number - _blockNumber <= MAX_L1_MINING_DRIFT, "StorageContract: block number too old"); + ) internal virtual { + require(blockNumber() - _blockNumber <= MAX_L1_MINING_DRIFT, "StorageContract: block number too old"); // To avoid stack too deep, we resue the hash0 instead of using randao - bytes32 hash0 = RandaoLib.verifyHistoricalRandao(_blockNumber, _randaoProof); + bytes32 hash0 = getRandao(_blockNumber, _randaoProof); // Estimate block timestamp - uint256 mineTs = block.timestamp - (block.number - _blockNumber) * 12; + uint256 mineTs = getMinedTs(_blockNumber); // Given a blockhash and a miner, we only allow sampling up to nonce limit times. require(_nonce < nonceLimit, "StorageContract: nonce too big"); @@ -382,6 +381,28 @@ abstract contract StorageContract is DecentralizedKV { _rewardMiner(_shardId, _miner, mineTs, diff); } + /// @notice Get the current block number + function blockNumber() internal view virtual returns (uint256) { + return block.number; + } + + /// @notice Get the current block timestamp + function blockTs() internal view virtual returns (uint256) { + return block.timestamp; + } + + /// @notice Get the randao value by block number. + function getRandao(uint256 _blockNumber, bytes calldata _headerRlpBytes) internal view virtual returns (bytes32) { + bytes32 bh = blockhash(_blockNumber); + require(bh != bytes32(0), "StorageContract: failed to obtain blockhash"); + return RandaoLib.verifyHeaderAndGetRandao(bh, _headerRlpBytes); + } + + /// @notice Get the mined timestamp + function getMinedTs(uint256 _blockNumber) internal view returns (uint256) { + return blockTs() - (blockNumber() - _blockNumber) * 12; + } + /// @notice Return the sample size bits. function sampleSizeBits() public pure returns (uint256) { return SAMPLE_SIZE_BITS; diff --git a/contracts/libraries/RandaoLib.sol b/contracts/libraries/RandaoLib.sol index fd5438e..3a0af58 100644 --- a/contracts/libraries/RandaoLib.sol +++ b/contracts/libraries/RandaoLib.sol @@ -35,17 +35,4 @@ library RandaoLib { require(_headerHash == item.rlpBytesKeccak256(), "RandaoLib: header hash mismatch"); return getRandaoFromHeader(item); } - - /// @notice Get the historical Randao mixDigest by block number - /// @param _blockNumber The block number - /// @param _headerRlpBytes The RLP data of the header - /// @return The Randao mixDigest - function verifyHistoricalRandao( - uint256 _blockNumber, - bytes memory _headerRlpBytes - ) internal view returns (bytes32) { - bytes32 bh = blockhash(_blockNumber); - require(bh != bytes32(0), "RandaoLib: failed to obtain blockhash"); - return verifyHeaderAndGetRandao(bh, _headerRlpBytes); - } } diff --git a/contracts/test/TestEthStorageContract.sol b/contracts/test/TestEthStorageContract.sol index d89b1c9..83ef852 100644 --- a/contracts/test/TestEthStorageContract.sol +++ b/contracts/test/TestEthStorageContract.sol @@ -145,12 +145,9 @@ contract TestEthStorageContract is EthStorageContract { bytes[] calldata inclusiveProofs, bytes[] calldata decodeProof ) internal { - // Obtain the blockhash of the block number of recent blocks - require(block.number - blockNumber <= 64, "block number too old"); - // To avoid stack too deep, we resue the hash0 instead of using randao - bytes32 hash0 = RandaoLib.verifyHistoricalRandao(blockNumber, randaoProof); - // Estimate block timestamp - uint256 mineTs = block.timestamp - (block.number - blockNumber) * 12; + require(super.blockNumber() - blockNumber <= MAX_L1_MINING_DRIFT, "block number too old"); + bytes32 hash0 = getRandao(blockNumber, randaoProof); + uint256 mineTs = getMinedTs(blockNumber); // Given a blockhash and a miner, we only allow sampling up to nonce limit times. require(nonce < nonceLimit, "nonce too big"); diff --git a/contracts/test/TestRandao.sol b/contracts/test/TestRandao.sol index 8e4aeef..5b95f5d 100644 --- a/contracts/test/TestRandao.sol +++ b/contracts/test/TestRandao.sol @@ -7,8 +7,4 @@ contract TestRandao { function verifyHeaderAndGetRandao(bytes32 headerHash, bytes memory headerRlpBytes) public pure returns (bytes32) { return RandaoLib.verifyHeaderAndGetRandao(headerHash, headerRlpBytes); } - - function verifyHistoricalRandao(uint256 blockNumber, bytes memory proof) public view returns (bytes32) { - return RandaoLib.verifyHistoricalRandao(blockNumber, proof); - } } diff --git a/test/randao-test.js b/test/randao-test.js index 3f1becb..5bcf43a 100644 --- a/test/randao-test.js +++ b/test/randao-test.js @@ -58,8 +58,8 @@ describe("Randao Test", function () { const Randao = await ethers.getContractFactory("TestRandao"); const rd = await Randao.deploy(); await rd.deployed(); - - let randao = await rd.verifyHistoricalRandao(bn, encodedHeader); + + let randao = await rd.verifyHeaderAndGetRandao(hash, encodedHeader); expect(randao).to.equal(block.mixHash); }); }); From 8a6e2ad1a1cb6d041fb9b30ed788f50de35232cc Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 20 Aug 2024 12:12:12 +0800 Subject: [PATCH 09/16] minor --- contracts/test/EthStorageContractTest.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/test/EthStorageContractTest.t.sol b/contracts/test/EthStorageContractTest.t.sol index 0f9466f..900c92d 100644 --- a/contracts/test/EthStorageContractTest.t.sol +++ b/contracts/test/EthStorageContractTest.t.sol @@ -110,10 +110,10 @@ contract EthStorageContractTest is Test { blobIdxs[i] = i; lengths[i] = 10 + i*10; } - vm.warp(0); uint256 sufficientCost = storageContract.upfrontPaymentInBatch(size); uint256 insufficientCost = sufficientCost - 1; + // Expect the specific revert reason from _prepareBatchAppend due to insufficient msg.value vm.expectRevert("StorageContract: not enough batch payment"); storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); From 51a0efe0721cab9e97bdf7c89aa96c67323c1260 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 20 Aug 2024 17:38:26 +0800 Subject: [PATCH 10/16] format --- contracts/DecentralizedKV.sol | 2 +- contracts/StorageContract.sol | 24 ++++++++++----------- contracts/test/EthStorageContractTest.t.sol | 6 +++--- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/contracts/DecentralizedKV.sol b/contracts/DecentralizedKV.sol index a387552..54bd651 100644 --- a/contracts/DecentralizedKV.sol +++ b/contracts/DecentralizedKV.sol @@ -103,7 +103,7 @@ contract DecentralizedKV is OwnableUpgradeable { function _paymentIn(uint256 _x, uint256 _fromTs, uint256 _toTs) internal view returns (uint256) { return _paymentInInterval(_x, _fromTs - START_TIME, _toTs - START_TIME); } - + /// @notice Evaluate payment given the timestamp. function _upfrontPayment(uint256 _ts) internal view returns (uint256) { require(_ts >= START_TIME, "DecentralizedKV: invalid timestamp to evaluate payment"); diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 1683de5..66d4a4a 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -100,12 +100,9 @@ abstract contract StorageContract is DecentralizedKV { ); /// @notice Constructs the StorageContract contract. Initializes the storage config. - constructor( - Config memory _config, - uint256 _startTime, - uint256 _storageCost, - uint256 _dcfFactor - ) DecentralizedKV(1 << _config.maxKvSizeBits, _startTime, _storageCost, _dcfFactor) { + constructor(Config memory _config, uint256 _startTime, uint256 _storageCost, uint256 _dcfFactor) + DecentralizedKV(1 << _config.maxKvSizeBits, _startTime, _storageCost, _dcfFactor) + { /* Assumptions */ require(_config.shardSizeBits >= _config.maxKvSizeBits, "StorageContract: shardSize too small"); require(_config.maxKvSizeBits >= SAMPLE_SIZE_BITS, "StorageContract: maxKvSize too small"); @@ -228,10 +225,11 @@ abstract contract StorageContract is DecentralizedKV { /// @param _shardId The shard id. /// @param _minedTs The mined timestamp. /// @return diff_ The difficulty of the shard. - function _calculateDiffAndInitHashSingleShard( - uint256 _shardId, - uint256 _minedTs - ) internal view returns (uint256 diff_) { + function _calculateDiffAndInitHashSingleShard(uint256 _shardId, uint256 _minedTs) + internal + view + returns (uint256 diff_) + { MiningLib.MiningInfo storage info = infos[_shardId]; require(_minedTs >= info.lastMineTime, "StorageContract: minedTs too small"); diff_ = MiningLib.expectedDiff(info, _minedTs, CUTOFF, DIFF_ADJ_DIVISOR, minimumDiff); @@ -277,8 +275,8 @@ abstract contract StorageContract is DecentralizedKV { reward = _paymentIn(STORAGE_COST * (kvEntryCount % (1 << SHARD_ENTRY_BITS)), info.lastMineTime, _minedTs); // Additional prepaid for the last shard if (prepaidLastMineTime < _minedTs) { - uint256 prepaidAmountCap = STORAGE_COST * - ((1 << SHARD_ENTRY_BITS) - (kvEntryCount % (1 << SHARD_ENTRY_BITS))); + uint256 prepaidAmountCap = + STORAGE_COST * ((1 << SHARD_ENTRY_BITS) - (kvEntryCount % (1 << SHARD_ENTRY_BITS))); if (prepaidAmountCap > prepaidAmount) { prepaidAmountCap = prepaidAmount; } @@ -298,7 +296,7 @@ abstract contract StorageContract is DecentralizedKV { /// @return The mining reward. function miningReward(uint256 _shardId, uint256 _blockNumber) public view returns (uint256) { uint256 minedTs = block.timestamp - (block.number - _blockNumber) * 12; - (, , uint256 minerReward) = _miningReward(_shardId, minedTs); + (,, uint256 minerReward) = _miningReward(_shardId, minedTs); return minerReward; } diff --git a/contracts/test/EthStorageContractTest.t.sol b/contracts/test/EthStorageContractTest.t.sol index 900c92d..3189338 100644 --- a/contracts/test/EthStorageContractTest.t.sol +++ b/contracts/test/EthStorageContractTest.t.sol @@ -108,12 +108,12 @@ contract EthStorageContractTest is Test { for (uint256 i = 0; i < size; i++) { keys[i] = bytes32(uint256(i)); blobIdxs[i] = i; - lengths[i] = 10 + i*10; + lengths[i] = 10 + i * 10; } - + uint256 sufficientCost = storageContract.upfrontPaymentInBatch(size); uint256 insufficientCost = sufficientCost - 1; - + // Expect the specific revert reason from _prepareBatchAppend due to insufficient msg.value vm.expectRevert("StorageContract: not enough batch payment"); storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); From 20fb33d6ae39f9438b40c8294c2e760e0b1aba13 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 20 Aug 2024 17:46:12 +0800 Subject: [PATCH 11/16] format --- contracts/EthStorageContractL2.sol | 19 +++++++++---------- contracts/StorageContract.sol | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/contracts/EthStorageContractL2.sol b/contracts/EthStorageContractL2.sol index 5bb8efd..8825dc5 100644 --- a/contracts/EthStorageContractL2.sol +++ b/contracts/EthStorageContractL2.sol @@ -28,12 +28,9 @@ contract EthStorageContractL2 is EthStorageContract2 { IL1Block internal constant L1_BLOCK = IL1Block(0x4200000000000000000000000000000000000015); /// @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) + EthStorageContract2(_config, _startTime, _storageCost, _dcfFactor) + {} /// @notice Get the current block number function blockNumber() internal view override returns (uint256) { @@ -46,10 +43,12 @@ contract EthStorageContractL2 is EthStorageContract2 { } /// @notice Get the randao value from the L1 blockhash. - function getRandao( - uint256 _l1BlockNumber, - bytes calldata _headerRlpBytes - ) internal view override returns (bytes32) { + function getRandao(uint256 _l1BlockNumber, bytes calldata _headerRlpBytes) + internal + view + override + returns (bytes32) + { bytes32 bh = L1_BLOCK.blockHash(_l1BlockNumber); require(bh != bytes32(0), "EthStorageContractL2: failed to obtain blockhash"); return RandaoLib.verifyHeaderAndGetRandao(bh, _headerRlpBytes); diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index c85a2f6..2ec3a8e 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -359,7 +359,7 @@ abstract contract StorageContract is DecentralizedKV { bytes calldata _randaoProof, bytes[] calldata _inclusiveProofs, bytes[] calldata _decodeProof - ) internal virtual { + ) internal virtual { require(blockNumber() - _blockNumber <= MAX_L1_MINING_DRIFT, "StorageContract: block number too old"); // To avoid stack too deep, we resue the hash0 instead of using randao bytes32 hash0 = getRandao(_blockNumber, _randaoProof); From e3e143f33c561d331741820a102bdfb8a7f21f08 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 20 Aug 2024 17:58:32 +0800 Subject: [PATCH 12/16] format --- contracts/DecentralizedKV.sol | 6 ------ contracts/EthStorageContractL2.sol | 9 +++------ contracts/StorageContract.sol | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/contracts/DecentralizedKV.sol b/contracts/DecentralizedKV.sol index 54bd651..caaa383 100644 --- a/contracts/DecentralizedKV.sol +++ b/contracts/DecentralizedKV.sol @@ -106,7 +106,6 @@ contract DecentralizedKV is OwnableUpgradeable { /// @notice Evaluate payment given the timestamp. function _upfrontPayment(uint256 _ts) internal view returns (uint256) { - require(_ts >= START_TIME, "DecentralizedKV: invalid timestamp to evaluate payment"); return _paymentInf(STORAGE_COST, _ts - START_TIME); } @@ -115,11 +114,6 @@ contract DecentralizedKV is OwnableUpgradeable { return _upfrontPayment(block.timestamp); } - /// @notice Upfront payment for the next batch insertion - function upfrontPaymentInBatch(uint256 _batchSize) public view virtual returns (uint256) { - return upfrontPayment() * _batchSize; - } - /// @notice Checks before appending the key-value. function _prepareAppend(uint256 _batchSize) internal virtual { require(msg.value >= upfrontPayment() * _batchSize, "DecentralizedKV: not enough batch payment"); diff --git a/contracts/EthStorageContractL2.sol b/contracts/EthStorageContractL2.sol index a3017b8..4f9439a 100644 --- a/contracts/EthStorageContractL2.sol +++ b/contracts/EthStorageContractL2.sol @@ -28,12 +28,9 @@ contract EthStorageContractL2 is EthStorageContract2 { IL1Block internal constant L1_BLOCK = IL1Block(0x4200000000000000000000000000000000000015); /// @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) + EthStorageContract2(_config, _startTime, _storageCost, _dcfFactor) + {} /// @notice Get the randao value from the L1 blockhash. function _getRandao(uint256 _l1BlockNumber, bytes calldata _headerRlpBytes) internal view returns (bytes32) { diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 66d4a4a..96af797 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -175,7 +175,7 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Upfront payment for a batch insertion /// @param _batchSize The blob count for a batch insertion. /// @return The total payment for a batch insertion. - function upfrontPaymentInBatch(uint256 _batchSize) public view virtual override returns (uint256) { + function upfrontPaymentInBatch(uint256 _batchSize) public view returns (uint256) { return _upfrontPaymentInBatch(kvEntryCount, _batchSize); } From 42ac1cb6832a63e4421cb58ef86ad8f4dd6c95ec Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 20 Aug 2024 18:02:00 +0800 Subject: [PATCH 13/16] minor --- contracts/EthStorageContractL2.sol | 9 ++++++--- contracts/StorageContract.sol | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/contracts/EthStorageContractL2.sol b/contracts/EthStorageContractL2.sol index 4f9439a..a3017b8 100644 --- a/contracts/EthStorageContractL2.sol +++ b/contracts/EthStorageContractL2.sol @@ -28,9 +28,12 @@ contract EthStorageContractL2 is EthStorageContract2 { IL1Block internal constant L1_BLOCK = IL1Block(0x4200000000000000000000000000000000000015); /// @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 + ) EthStorageContract2(_config, _startTime, _storageCost, _dcfFactor) {} /// @notice Get the randao value from the L1 blockhash. function _getRandao(uint256 _l1BlockNumber, bytes calldata _headerRlpBytes) internal view returns (bytes32) { diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 96af797..8ab0eef 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -276,7 +276,7 @@ abstract contract StorageContract is DecentralizedKV { // Additional prepaid for the last shard if (prepaidLastMineTime < _minedTs) { uint256 prepaidAmountCap = - STORAGE_COST * ((1 << SHARD_ENTRY_BITS) - (kvEntryCount % (1 << SHARD_ENTRY_BITS))); + STORAGE_COST * ((1 << SHARD_ENTRY_BITS) - kvEntryCount % (1 << SHARD_ENTRY_BITS)); if (prepaidAmountCap > prepaidAmount) { prepaidAmountCap = prepaidAmount; } From 8e612d4d3aa937ca3e7f301e8663dd76ef32d1d3 Mon Sep 17 00:00:00 2001 From: syntrust Date: Wed, 21 Aug 2024 10:10:37 +0800 Subject: [PATCH 14/16] fix comments --- contracts/EthStorageContractL2.sol | 6 +-- contracts/StorageContract.sol | 48 ++++++++++------------- contracts/test/TestEthStorageContract.sol | 4 +- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/contracts/EthStorageContractL2.sol b/contracts/EthStorageContractL2.sol index 8825dc5..9ec0c44 100644 --- a/contracts/EthStorageContractL2.sol +++ b/contracts/EthStorageContractL2.sol @@ -33,17 +33,17 @@ contract EthStorageContractL2 is EthStorageContract2 { {} /// @notice Get the current block number - function blockNumber() internal view override returns (uint256) { + function _blockNumber() internal view override returns (uint256) { return L1_BLOCK.number(); } /// @notice Get the current block timestamp - function blockTs() internal view override returns (uint256) { + function _blockTs() internal view override returns (uint256) { return L1_BLOCK.timestamp(); } /// @notice Get the randao value from the L1 blockhash. - function getRandao(uint256 _l1BlockNumber, bytes calldata _headerRlpBytes) + function _getRandao(uint256 _l1BlockNumber, bytes calldata _headerRlpBytes) internal view override diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index f8b53fa..123b49c 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -166,7 +166,7 @@ abstract contract StorageContract is DecentralizedKV { if ((totalEntries % (1 << SHARD_ENTRY_BITS)) == 1 && shardId != 0) { // Open a new shard if the KV is the first one of the shard // (TODO): Setup shard difficulty as current difficulty / factor? - return _upfrontPayment(blockTs()); + return _upfrontPayment(_blockTs()); } else { return _upfrontPayment(infos[shardId].lastMineTime); } @@ -186,7 +186,7 @@ abstract contract StorageContract is DecentralizedKV { uint256 totalPayment = 0; if ((totalEntries >> SHARD_ENTRY_BITS) > shardId) { uint256 kvCountNew = totalEntries % (1 << SHARD_ENTRY_BITS); - totalPayment += _upfrontPayment(blockTs()) * kvCountNew; + totalPayment += _upfrontPayment(_blockTs()) * kvCountNew; totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); } else { totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; @@ -196,7 +196,7 @@ abstract contract StorageContract is DecentralizedKV { /// @inheritdoc DecentralizedKV function _prepareAppend(uint256 _batchSize) internal virtual override { - return _prepareAppendWithTimestamp(blockTs(), _batchSize); + return _prepareAppendWithTimestamp(_blockTs(), _batchSize); } /// @notice Verify the samples of the BLOBs by the miner (storage provider) including @@ -292,16 +292,16 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Get the mining reward. /// @param _shardId The shard id. - /// @param _blockNumber The block number. + /// @param _blockNum The block number. /// @return The mining reward. - function miningReward(uint256 _shardId, uint256 _blockNumber) public view returns (uint256) { - uint256 minedTs = getMinedTs(_blockNumber); + function miningReward(uint256 _shardId, uint256 _blockNum) public view returns (uint256) { + uint256 minedTs = getMinedTs(_blockNum); (,, uint256 minerReward) = _miningReward(_shardId, minedTs); return minerReward; } /// @notice Mine a block. - /// @param _blockNumber The block number. + /// @param _blockNum The block number. /// @param _shardId The shard id. /// @param _miner The miner address. /// @param _nonce The nonce. @@ -311,7 +311,7 @@ abstract contract StorageContract is DecentralizedKV { /// @param _inclusiveProofs The inclusive proofs. /// @param _decodeProof The decode proof. function mine( - uint256 _blockNumber, + uint256 _blockNum, uint256 _shardId, address _miner, uint256 _nonce, @@ -322,15 +322,7 @@ abstract contract StorageContract is DecentralizedKV { bytes[] calldata _decodeProof ) public virtual { _mine( - _blockNumber, - _shardId, - _miner, - _nonce, - _encodedSamples, - _masks, - _randaoProof, - _inclusiveProofs, - _decodeProof + _blockNum, _shardId, _miner, _nonce, _encodedSamples, _masks, _randaoProof, _inclusiveProofs, _decodeProof ); } @@ -354,7 +346,7 @@ abstract contract StorageContract is DecentralizedKV { /// to decoded one. The decoded samples will be used to perform inclusive check with on-chain datahashes. /// The encoded samples will be used to calculate the solution hash, and if the hash passes the difficulty check, /// the miner, or say the storage provider, shall be rewarded by the token number from out economic models - /// @param _blockNumber The block number. + /// @param _blockNum The block number. /// @param _shardId The shard id. /// @param _miner The miner address. /// @param _nonce The nonce. @@ -364,7 +356,7 @@ abstract contract StorageContract is DecentralizedKV { /// @param _inclusiveProofs The inclusive proofs. /// @param _decodeProof The decode proof. function _mine( - uint256 _blockNumber, + uint256 _blockNum, uint256 _shardId, address _miner, uint256 _nonce, @@ -374,11 +366,11 @@ abstract contract StorageContract is DecentralizedKV { bytes[] calldata _inclusiveProofs, bytes[] calldata _decodeProof ) internal virtual { - require(blockNumber() - _blockNumber <= MAX_L1_MINING_DRIFT, "StorageContract: block number too old"); + require(_blockNumber() - _blockNum <= MAX_L1_MINING_DRIFT, "StorageContract: block number too old"); // To avoid stack too deep, we resue the hash0 instead of using randao - bytes32 hash0 = getRandao(_blockNumber, _randaoProof); + bytes32 hash0 = _getRandao(_blockNum, _randaoProof); // Estimate block timestamp - uint256 mineTs = getMinedTs(_blockNumber); + uint256 mineTs = getMinedTs(_blockNum); // Given a blockhash and a miner, we only allow sampling up to nonce limit times. require(_nonce < nonceLimit, "StorageContract: nonce too big"); @@ -396,25 +388,25 @@ abstract contract StorageContract is DecentralizedKV { } /// @notice Get the current block number - function blockNumber() internal view virtual returns (uint256) { + function _blockNumber() internal view virtual returns (uint256) { return block.number; } /// @notice Get the current block timestamp - function blockTs() internal view virtual returns (uint256) { + function _blockTs() internal view virtual returns (uint256) { return block.timestamp; } /// @notice Get the randao value by block number. - function getRandao(uint256 _blockNumber, bytes calldata _headerRlpBytes) internal view virtual returns (bytes32) { - bytes32 bh = blockhash(_blockNumber); + function _getRandao(uint256 _blockNum, bytes calldata _headerRlpBytes) internal view virtual returns (bytes32) { + bytes32 bh = blockhash(_blockNum); require(bh != bytes32(0), "StorageContract: failed to obtain blockhash"); return RandaoLib.verifyHeaderAndGetRandao(bh, _headerRlpBytes); } /// @notice Get the mined timestamp - function getMinedTs(uint256 _blockNumber) internal view returns (uint256) { - return blockTs() - (blockNumber() - _blockNumber) * 12; + function getMinedTs(uint256 _blockNum) internal view returns (uint256) { + return _blockTs() - (_blockNumber() - _blockNum) * 12; } /// @notice Return the sample size bits. diff --git a/contracts/test/TestEthStorageContract.sol b/contracts/test/TestEthStorageContract.sol index 83ef852..fc5ecce 100644 --- a/contracts/test/TestEthStorageContract.sol +++ b/contracts/test/TestEthStorageContract.sol @@ -145,8 +145,8 @@ contract TestEthStorageContract is EthStorageContract { bytes[] calldata inclusiveProofs, bytes[] calldata decodeProof ) internal { - require(super.blockNumber() - blockNumber <= MAX_L1_MINING_DRIFT, "block number too old"); - bytes32 hash0 = getRandao(blockNumber, randaoProof); + require(_blockNumber() - blockNumber <= MAX_L1_MINING_DRIFT, "block number too old"); + bytes32 hash0 = _getRandao(blockNumber, randaoProof); uint256 mineTs = getMinedTs(blockNumber); // Given a blockhash and a miner, we only allow sampling up to nonce limit times. From 6cc9bc51f795e4e9df852e7cebe43b67172a793e Mon Sep 17 00:00:00 2001 From: syntrust Date: Thu, 29 Aug 2024 11:21:50 +0800 Subject: [PATCH 15/16] fix comments --- contracts/DecentralizedKV.sol | 6 ++-- contracts/StorageContract.sol | 37 +++++++-------------- contracts/test/EthStorageContractTest.t.sol | 2 +- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/contracts/DecentralizedKV.sol b/contracts/DecentralizedKV.sol index caaa383..bb1ff26 100644 --- a/contracts/DecentralizedKV.sol +++ b/contracts/DecentralizedKV.sol @@ -114,8 +114,8 @@ contract DecentralizedKV is OwnableUpgradeable { return _upfrontPayment(block.timestamp); } - /// @notice Checks before appending the key-value. - function _prepareAppend(uint256 _batchSize) internal virtual { + /// @notice Checks while appending the key-value. + function _checkAppend(uint256 _batchSize) internal virtual { require(msg.value >= upfrontPayment() * _batchSize, "DecentralizedKV: not enough batch payment"); } @@ -152,7 +152,7 @@ contract DecentralizedKV is OwnableUpgradeable { res[i] = paddr.kvIdx; } - _prepareAppend(batchPaymentSize); + _checkAppend(batchPaymentSize); return res; } diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 8ab0eef..e7277ca 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -145,31 +145,9 @@ abstract contract StorageContract is DecentralizedKV { /// @notice People can sent ETH to the contract. function sendValue() public payable {} - /// @notice Checks the payment using the last mine time. - function _prepareAppendWithTimestamp(uint256 _timestamp, uint256 _batchSize) internal { - uint256 kvEntryCountOld = kvEntryCount - _batchSize; // kvEntryCount already increased - uint256 totalPayment = _upfrontPaymentInBatch(kvEntryCountOld, _batchSize); - require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); - uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard id after the batch - if (shardId > (kvEntryCountOld >> SHARD_ENTRY_BITS)) { - // Open a new shard and mark the shard is ready to mine. - // (TODO): Setup shard difficulty as current difficulty / factor? - infos[shardId].lastMineTime = _timestamp; - } - } - /// @notice Upfront payment for the next insertion function upfrontPayment() public view virtual override returns (uint256) { - uint256 totalEntries = kvEntryCount + 1; // include the one to be put - uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; - // shard0 is already opened in constructor - if ((totalEntries % (1 << SHARD_ENTRY_BITS)) == 1 && shardId != 0) { - // Open a new shard if the KV is the first one of the shard - // (TODO): Setup shard difficulty as current difficulty / factor? - return _upfrontPayment(block.timestamp); - } else { - return _upfrontPayment(infos[shardId].lastMineTime); - } + return _upfrontPaymentInBatch(kvEntryCount, 1); } /// @notice Upfront payment for a batch insertion @@ -195,8 +173,17 @@ abstract contract StorageContract is DecentralizedKV { } /// @inheritdoc DecentralizedKV - function _prepareAppend(uint256 _batchSize) internal virtual override { - return _prepareAppendWithTimestamp(block.timestamp, _batchSize); + function _checkAppend(uint256 _batchSize) internal virtual override { + uint256 kvEntryCountPrev = kvEntryCount - _batchSize; // kvEntryCount already increased + uint256 totalPayment = _upfrontPaymentInBatch(kvEntryCountPrev, _batchSize); + require(msg.value >= totalPayment, "StorageContract: not enough batch payment"); + + uint256 shardId = kvEntryCount >> SHARD_ENTRY_BITS; // shard id after the batch + if (shardId > (kvEntryCountPrev >> SHARD_ENTRY_BITS)) { + // Open a new shard and mark the shard is ready to mine. + // (TODO): Setup shard difficulty as current difficulty / factor? + infos[shardId].lastMineTime = block.timestamp; + } } /// @notice Verify the samples of the BLOBs by the miner (storage provider) including diff --git a/contracts/test/EthStorageContractTest.t.sol b/contracts/test/EthStorageContractTest.t.sol index 3189338..0e2564b 100644 --- a/contracts/test/EthStorageContractTest.t.sol +++ b/contracts/test/EthStorageContractTest.t.sol @@ -35,7 +35,7 @@ contract EthStorageContractTest is Test { uint256 insufficientCost = storageContract.upfrontPayment() - 1; - // Expect the specific revert reason from _prepareAppend due to insufficient msg.value + // Expect the specific revert reason from _checkAppend due to insufficient msg.value vm.expectRevert("StorageContract: not enough batch payment"); storageContract.putBlob{value: insufficientCost}(key, blobIdx, length); From 31eb3fc3b374ba27cd08bd902c9f72fb51073c1c Mon Sep 17 00:00:00 2001 From: syntrust Date: Thu, 29 Aug 2024 11:36:56 +0800 Subject: [PATCH 16/16] fix comments --- contracts/StorageContract.sol | 6 +++--- contracts/test/TestEthStorageContract.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 54afcef..ffe940a 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -282,7 +282,7 @@ abstract contract StorageContract is DecentralizedKV { /// @param _blockNum The block number. /// @return The mining reward. function miningReward(uint256 _shardId, uint256 _blockNum) public view returns (uint256) { - uint256 minedTs = getMinedTs(_blockNum); + uint256 minedTs = _getMinedTs(_blockNum); (,, uint256 minerReward) = _miningReward(_shardId, minedTs); return minerReward; } @@ -357,7 +357,7 @@ abstract contract StorageContract is DecentralizedKV { // To avoid stack too deep, we resue the hash0 instead of using randao bytes32 hash0 = _getRandao(_blockNum, _randaoProof); // Estimate block timestamp - uint256 mineTs = getMinedTs(_blockNum); + uint256 mineTs = _getMinedTs(_blockNum); // Given a blockhash and a miner, we only allow sampling up to nonce limit times. require(_nonce < nonceLimit, "StorageContract: nonce too big"); @@ -392,7 +392,7 @@ abstract contract StorageContract is DecentralizedKV { } /// @notice Get the mined timestamp - function getMinedTs(uint256 _blockNum) internal view returns (uint256) { + function _getMinedTs(uint256 _blockNum) internal view returns (uint256) { return _blockTs() - (_blockNumber() - _blockNum) * 12; } diff --git a/contracts/test/TestEthStorageContract.sol b/contracts/test/TestEthStorageContract.sol index fc5ecce..fecae4e 100644 --- a/contracts/test/TestEthStorageContract.sol +++ b/contracts/test/TestEthStorageContract.sol @@ -147,7 +147,7 @@ contract TestEthStorageContract is EthStorageContract { ) internal { require(_blockNumber() - blockNumber <= MAX_L1_MINING_DRIFT, "block number too old"); bytes32 hash0 = _getRandao(blockNumber, randaoProof); - uint256 mineTs = getMinedTs(blockNumber); + uint256 mineTs = _getMinedTs(blockNumber); // Given a blockhash and a miner, we only allow sampling up to nonce limit times. require(nonce < nonceLimit, "nonce too big");