From dc9b40bb60c30b2f9e045eac378002f933d21ca2 Mon Sep 17 00:00:00 2001 From: syntrust Date: Mon, 9 Sep 2024 15:46:03 +0800 Subject: [PATCH 1/9] withdraw treasury --- contracts/StorageContract.sol | 41 ++++++++++++++++++------ contracts/test/StorageContractTest.t.sol | 8 ++--- contracts/test/TestStorageContract.sol | 2 +- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index ffe940a..204f359 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -81,6 +81,9 @@ abstract contract StorageContract is DecentralizedKV { /// @notice uint256 public prepaidLastMineTime; + /// @notice Fund tracker for prepaid + uint256 public totalPrepaidAmount; + // TODO: Reserve extra slots (to a total of 50?) in the storage layout for future upgrades /// @notice Emitted when a block is mined. @@ -143,7 +146,9 @@ abstract contract StorageContract is DecentralizedKV { } /// @notice People can sent ETH to the contract. - function sendValue() public payable {} + function sendValue() public payable { + totalPrepaidAmount += msg.value; + } /// @notice Upfront payment for the next insertion function upfrontPayment() public view virtual override returns (uint256) { @@ -230,17 +235,19 @@ abstract contract StorageContract is DecentralizedKV { function _rewardMiner(uint256 _shardId, address _miner, uint256 _minedTs, uint256 _diff) internal { // Mining is successful. // Send reward to coinbase and miner. - (bool updatePrepaidTime, uint256 treasuryReward, uint256 minerReward) = _miningReward(_shardId, _minedTs); + (bool updatePrepaidTime, uint256 prepaidAmountSaved, uint256 treasuryReward, uint256 minerReward) = + _miningReward(_shardId, _minedTs); if (updatePrepaidTime) { prepaidLastMineTime = _minedTs; } - + if (prepaidAmountSaved > 0) { + totalPrepaidAmount += prepaidAmountSaved; + } + totalPrepaidAmount += treasuryReward; // Update mining info. MiningLib.update(infos[_shardId], _minedTs, _diff); - require(treasuryReward + minerReward <= address(this).balance, "StorageContract: not enough balance"); - // TODO: avoid reentrancy attack - payable(treasury).transfer(treasuryReward); + require(minerReward <= address(this).balance, "StorageContract: not enough balance"); payable(_miner).transfer(minerReward); emit MinedBlock(_shardId, _diff, infos[_shardId].blockMined, _minedTs, _miner, minerReward); } @@ -251,11 +258,16 @@ abstract contract StorageContract is DecentralizedKV { /// @return updatePrepaidTime Whether to update the prepaid time. /// @return treasuryReward The treasury reward. /// @return minerReward The miner reward. - function _miningReward(uint256 _shardId, uint256 _minedTs) internal view returns (bool, uint256, uint256) { + function _miningReward(uint256 _shardId, uint256 _minedTs) + internal + view + returns (bool, uint256, uint256, uint256) + { MiningLib.MiningInfo storage info = infos[_shardId]; uint256 lastShardIdx = kvEntryCount > 0 ? (kvEntryCount - 1) >> SHARD_ENTRY_BITS : 0; - uint256 reward = 0; bool updatePrepaidTime = false; + uint256 prepaidAmountSaved = 0; + uint256 reward = 0; if (_shardId < lastShardIdx) { reward = _paymentIn(STORAGE_COST << SHARD_ENTRY_BITS, info.lastMineTime, _minedTs); } else if (_shardId == lastShardIdx) { @@ -265,6 +277,8 @@ abstract contract StorageContract is DecentralizedKV { uint256 prepaidAmountCap = STORAGE_COST * ((1 << SHARD_ENTRY_BITS) - kvEntryCount % (1 << SHARD_ENTRY_BITS)); if (prepaidAmountCap > prepaidAmount) { + prepaidAmountSaved = _paymentIn(prepaidAmountCap, prepaidLastMineTime, _minedTs) + - _paymentIn(prepaidAmount, prepaidLastMineTime, _minedTs); prepaidAmountCap = prepaidAmount; } reward += _paymentIn(prepaidAmountCap, prepaidLastMineTime, _minedTs); @@ -274,7 +288,7 @@ abstract contract StorageContract is DecentralizedKV { uint256 treasuryReward = (reward * TREASURY_SHARE) / 10000; uint256 minerReward = reward - treasuryReward; - return (updatePrepaidTime, treasuryReward, minerReward); + return (updatePrepaidTime, prepaidAmountSaved, treasuryReward, minerReward); } /// @notice Get the mining reward. @@ -283,7 +297,7 @@ abstract contract StorageContract is DecentralizedKV { /// @return The mining reward. function miningReward(uint256 _shardId, uint256 _blockNum) public view returns (uint256) { uint256 minedTs = _getMinedTs(_blockNum); - (,, uint256 minerReward) = _miningReward(_shardId, minedTs); + (,,, uint256 minerReward) = _miningReward(_shardId, minedTs); return minerReward; } @@ -374,6 +388,13 @@ abstract contract StorageContract is DecentralizedKV { _rewardMiner(_shardId, _miner, mineTs, diff); } + /// @notice Withdraw treasury fund + function withdraw(uint256 _amount) public { + require(totalPrepaidAmount + _amount >= prepaidAmount, "Not enough prepaid amount"); + totalPrepaidAmount -= _amount; + payable(treasury).transfer(_amount); + } + /// @notice Get the current block number function _blockNumber() internal view virtual returns (uint256) { return block.number; diff --git a/contracts/test/StorageContractTest.t.sol b/contracts/test/StorageContractTest.t.sol index 74197c0..5ae02e3 100644 --- a/contracts/test/StorageContractTest.t.sol +++ b/contracts/test/StorageContractTest.t.sol @@ -22,22 +22,22 @@ contract StorageContractTest is Test { function testMiningReward() public { // no key-value stored on EthStorage, only use prepaid amount as the reward - (,, uint256 reward) = storageContract.miningRewards(0, 1); + (,,, uint256 reward) = storageContract.miningRewards(0, 1); assertEq(reward, storageContract.paymentIn(PREPAID_AMOUNT, 0, 1)); // 1 key-value stored on EthStorage storageContract.setKvEntryCount(1); - (,, reward) = storageContract.miningRewards(0, 1); + (,,, reward) = storageContract.miningRewards(0, 1); assertEq(reward, storageContract.paymentIn(PREPAID_AMOUNT + STORAGE_COST * 1, 0, 1)); // 2 key-value stored on EthStorage storageContract.setKvEntryCount(2); - (,, reward) = storageContract.miningRewards(0, 1); + (,,, reward) = storageContract.miningRewards(0, 1); assertEq(reward, storageContract.paymentIn(PREPAID_AMOUNT + STORAGE_COST * 2, 0, 1)); // 3 key-value stored on EthStorage, but the reward is capped with 4 * STORAGE_COST storageContract.setKvEntryCount(3); - (,, reward) = storageContract.miningRewards(0, 1); + (,,, reward) = storageContract.miningRewards(0, 1); assertEq(reward, storageContract.paymentIn(PREPAID_AMOUNT + STORAGE_COST * 2, 0, 1)); } } diff --git a/contracts/test/TestStorageContract.sol b/contracts/test/TestStorageContract.sol index f6ff4f9..722b12e 100644 --- a/contracts/test/TestStorageContract.sol +++ b/contracts/test/TestStorageContract.sol @@ -38,7 +38,7 @@ contract TestStorageContract is StorageContract { return _paymentIn(_x, _fromTs, _toTs); } - function miningRewards(uint256 _shardId, uint256 _minedTs) public view returns (bool, uint256, uint256) { + function miningRewards(uint256 _shardId, uint256 _minedTs) public view returns (bool, uint256, uint256, uint256) { return _miningReward(_shardId, _minedTs); } } From ffe27248a62be7c2042b97e4080beea93c11f50f Mon Sep 17 00:00:00 2001 From: syntrust Date: Mon, 9 Sep 2024 19:11:12 +0800 Subject: [PATCH 2/9] tests and fix --- contracts/StorageContract.sol | 2 +- contracts/test/StorageContractTest.t.sol | 60 ++++++++++++++++++++++++ contracts/test/TestStorageContract.sol | 4 ++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 204f359..4cf0ac0 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -390,7 +390,7 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Withdraw treasury fund function withdraw(uint256 _amount) public { - require(totalPrepaidAmount + _amount >= prepaidAmount, "Not enough prepaid amount"); + require(totalPrepaidAmount >= prepaidAmount + _amount, "Not enough prepaid amount"); totalPrepaidAmount -= _amount; payable(treasury).transfer(_amount); } diff --git a/contracts/test/StorageContractTest.t.sol b/contracts/test/StorageContractTest.t.sol index 5ae02e3..fd11260 100644 --- a/contracts/test/StorageContractTest.t.sol +++ b/contracts/test/StorageContractTest.t.sol @@ -40,4 +40,64 @@ contract StorageContractTest is Test { (,,, reward) = storageContract.miningRewards(0, 1); assertEq(reward, storageContract.paymentIn(PREPAID_AMOUNT + STORAGE_COST * 2, 0, 1)); } + + function setUp1() public { + storageContract = new TestStorageContract( + StorageContract.Config(MAX_KV_SIZE, SHARD_SIZE_BITS, 2, 0, 0, 100), 0, STORAGE_COST, 0 + ); + storageContract.initialize(0, PREPAID_AMOUNT, 0, vm.addr(1), address(0x1)); + } + + function testWithdraw() public { + setUp1(); + + uint256 valueToSent = 3000; + uint256 withdrawAmount = 800; + + storageContract.sendValue{value: valueToSent}(); + assertEq(storageContract.totalPrepaidAmount(), valueToSent); + + storageContract.withdraw(withdrawAmount); + assertEq(storageContract.totalPrepaidAmount(), valueToSent - withdrawAmount); + assertEq(storageContract.treasury().balance, withdrawAmount); + } + + function testWithdrawRewardMiner() public { + setUp1(); + + uint256 valueToSent = 5000; + uint256 withdrawAmount = 800; + uint256 mineTs = 10000; + uint40 kvEntryCount = 1; + uint256 shardEntry = 1 << (SHARD_SIZE_BITS - MAX_KV_SIZE); + + storageContract.sendValue{value: valueToSent}(); + storageContract.setKvEntryCount(kvEntryCount); + uint256 reward = storageContract.paymentIn(STORAGE_COST * kvEntryCount, 0, mineTs); + uint256 prepaidReward = storageContract.paymentIn(PREPAID_AMOUNT, 0, mineTs); + reward += prepaidReward; + uint256 treasureReward = (reward * storageContract.treasuryShare()) / 10000; + uint256 minerReward = reward - treasureReward; + uint256 prepaidAmountCap = STORAGE_COST * (shardEntry - kvEntryCount); + uint256 prepaidAmountSaved = storageContract.paymentIn(prepaidAmountCap, 0, mineTs) - prepaidReward; + + storageContract.rewardMiner(0, vm.addr(2), mineTs, 1); + uint256 totalPrepaid = valueToSent + treasureReward + prepaidAmountSaved; + assertEq(storageContract.totalPrepaidAmount(), totalPrepaid); + + storageContract.withdraw(withdrawAmount); + assertEq(storageContract.totalPrepaidAmount(), totalPrepaid - withdrawAmount); + assertEq(storageContract.treasury().balance, withdrawAmount); + assertEq(address(storageContract).balance, valueToSent - minerReward - withdrawAmount); + } + + function testWithdrawInsufficientFunds() public { + uint256 valueToSent = 3000; + uint256 withdrawAmount = 1500; + + storageContract.sendValue{value: valueToSent}(); + + vm.expectRevert("Not enough prepaid amount"); + storageContract.withdraw(withdrawAmount); + } } diff --git a/contracts/test/TestStorageContract.sol b/contracts/test/TestStorageContract.sol index 722b12e..151a041 100644 --- a/contracts/test/TestStorageContract.sol +++ b/contracts/test/TestStorageContract.sol @@ -41,4 +41,8 @@ contract TestStorageContract is StorageContract { function miningRewards(uint256 _shardId, uint256 _minedTs) public view returns (bool, uint256, uint256, uint256) { return _miningReward(_shardId, _minedTs); } + + function rewardMiner(uint256 _shardId, address _miner, uint256 _minedTs, uint256 _diff) public { + return _rewardMiner(_shardId, _miner, _minedTs, _diff); + } } From aa6d6426ab6e23df8d3744d1afc95c8eaa63f620 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 10 Sep 2024 10:09:59 +0800 Subject: [PATCH 3/9] fix require msg --- contracts/StorageContract.sol | 3 ++- contracts/test/StorageContractTest.t.sol | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 4cf0ac0..f4791d0 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -390,8 +390,9 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Withdraw treasury fund function withdraw(uint256 _amount) public { - require(totalPrepaidAmount >= prepaidAmount + _amount, "Not enough prepaid amount"); + require(totalPrepaidAmount >= prepaidAmount + _amount, "StorageContract: not enough prepaid amount"); totalPrepaidAmount -= _amount; + require(address(this).balance >= _amount, "StorageContract: not enough balance"); payable(treasury).transfer(_amount); } diff --git a/contracts/test/StorageContractTest.t.sol b/contracts/test/StorageContractTest.t.sol index fd11260..35b59aa 100644 --- a/contracts/test/StorageContractTest.t.sol +++ b/contracts/test/StorageContractTest.t.sol @@ -97,7 +97,7 @@ contract StorageContractTest is Test { storageContract.sendValue{value: valueToSent}(); - vm.expectRevert("Not enough prepaid amount"); + vm.expectRevert("StorageContract: not enough prepaid amount"); storageContract.withdraw(withdrawAmount); } } From e71e293ce04978e58a49c06fa74726634b3860c7 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 10 Sep 2024 11:41:05 +0800 Subject: [PATCH 4/9] doc --- contracts/StorageContract.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index f4791d0..d0913d5 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -256,6 +256,7 @@ abstract contract StorageContract is DecentralizedKV { /// @param _shardId The shard id. /// @param _minedTs The mined timestamp. /// @return updatePrepaidTime Whether to update the prepaid time. + /// @return prepaidAmountSaved The capped part of prepaid amount. /// @return treasuryReward The treasury reward. /// @return minerReward The miner reward. function _miningReward(uint256 _shardId, uint256 _minedTs) From c6f178764c892c6881a80b16a99b04876add1fb2 Mon Sep 17 00:00:00 2001 From: syntrust Date: Mon, 23 Sep 2024 16:05:19 +0800 Subject: [PATCH 5/9] rename --- contracts/StorageContract.sol | 12 ++++++------ contracts/test/StorageContractTest.t.sol | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index d0913d5..e96aff3 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -82,7 +82,7 @@ abstract contract StorageContract is DecentralizedKV { uint256 public prepaidLastMineTime; /// @notice Fund tracker for prepaid - uint256 public totalPrepaidAmount; + uint256 public accPrepaidAmount; // TODO: Reserve extra slots (to a total of 50?) in the storage layout for future upgrades @@ -147,7 +147,7 @@ abstract contract StorageContract is DecentralizedKV { /// @notice People can sent ETH to the contract. function sendValue() public payable { - totalPrepaidAmount += msg.value; + accPrepaidAmount += msg.value; } /// @notice Upfront payment for the next insertion @@ -241,9 +241,9 @@ abstract contract StorageContract is DecentralizedKV { prepaidLastMineTime = _minedTs; } if (prepaidAmountSaved > 0) { - totalPrepaidAmount += prepaidAmountSaved; + accPrepaidAmount += prepaidAmountSaved; } - totalPrepaidAmount += treasuryReward; + accPrepaidAmount += treasuryReward; // Update mining info. MiningLib.update(infos[_shardId], _minedTs, _diff); @@ -391,8 +391,8 @@ abstract contract StorageContract is DecentralizedKV { /// @notice Withdraw treasury fund function withdraw(uint256 _amount) public { - require(totalPrepaidAmount >= prepaidAmount + _amount, "StorageContract: not enough prepaid amount"); - totalPrepaidAmount -= _amount; + require(accPrepaidAmount >= prepaidAmount + _amount, "StorageContract: not enough prepaid amount"); + accPrepaidAmount -= _amount; require(address(this).balance >= _amount, "StorageContract: not enough balance"); payable(treasury).transfer(_amount); } diff --git a/contracts/test/StorageContractTest.t.sol b/contracts/test/StorageContractTest.t.sol index 35b59aa..57865cc 100644 --- a/contracts/test/StorageContractTest.t.sol +++ b/contracts/test/StorageContractTest.t.sol @@ -55,10 +55,10 @@ contract StorageContractTest is Test { uint256 withdrawAmount = 800; storageContract.sendValue{value: valueToSent}(); - assertEq(storageContract.totalPrepaidAmount(), valueToSent); + assertEq(storageContract.accPrepaidAmount(), valueToSent); storageContract.withdraw(withdrawAmount); - assertEq(storageContract.totalPrepaidAmount(), valueToSent - withdrawAmount); + assertEq(storageContract.accPrepaidAmount(), valueToSent - withdrawAmount); assertEq(storageContract.treasury().balance, withdrawAmount); } @@ -83,10 +83,10 @@ contract StorageContractTest is Test { storageContract.rewardMiner(0, vm.addr(2), mineTs, 1); uint256 totalPrepaid = valueToSent + treasureReward + prepaidAmountSaved; - assertEq(storageContract.totalPrepaidAmount(), totalPrepaid); + assertEq(storageContract.accPrepaidAmount(), totalPrepaid); storageContract.withdraw(withdrawAmount); - assertEq(storageContract.totalPrepaidAmount(), totalPrepaid - withdrawAmount); + assertEq(storageContract.accPrepaidAmount(), totalPrepaid - withdrawAmount); assertEq(storageContract.treasury().balance, withdrawAmount); assertEq(address(storageContract).balance, valueToSent - minerReward - withdrawAmount); } From b1e2dfc8a2c59de446ae5c5a5c21acf172ff64a5 Mon Sep 17 00:00:00 2001 From: DL Date: Tue, 24 Sep 2024 10:06:07 +0800 Subject: [PATCH 6/9] Apply suggestions from code review Co-authored-by: Qiang Zhu --- contracts/StorageContract.sol | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index df116b5..ae4969b 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -284,7 +284,13 @@ abstract contract StorageContract is DecentralizedKV, ReentrancyGuardTransient { uint256 prepaidAmountCap = STORAGE_COST * ((1 << SHARD_ENTRY_BITS) - kvEntryCount % (1 << SHARD_ENTRY_BITS)); if (prepaidAmountCap > prepaidAmount) { - prepaidAmountSaved = _paymentIn(prepaidAmountCap, prepaidLastMineTime, _minedTs) +if (prepaidLastMineTime < _minedTs) { + fullReward = _paymentIn(STORAGE_COST << SHARD_ENTRY_BITS, info.lastMineTime, _minedTs); + prepaidAmountIn = _paymentIn(prepaidAmount, prepaidLastMineTime, _minedTs); + if (prepaidAmountIn > fullReward - reward) { + prepaidAmountIn = fullReward - reward; + } +} - _paymentIn(prepaidAmount, prepaidLastMineTime, _minedTs); prepaidAmountCap = prepaidAmount; } From 9bfb294c523563106f55a29ee82fbb5a0ba15ea1 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 24 Sep 2024 17:59:41 +0800 Subject: [PATCH 7/9] fix comments --- contracts/StorageContract.sol | 20 +++++++------------- contracts/test/StorageContractTest.t.sol | 20 +++++++++----------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index ae4969b..779e1d2 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -281,20 +281,14 @@ abstract contract StorageContract is DecentralizedKV, ReentrancyGuardTransient { 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)); - if (prepaidAmountCap > prepaidAmount) { -if (prepaidLastMineTime < _minedTs) { - fullReward = _paymentIn(STORAGE_COST << SHARD_ENTRY_BITS, info.lastMineTime, _minedTs); - prepaidAmountIn = _paymentIn(prepaidAmount, prepaidLastMineTime, _minedTs); - if (prepaidAmountIn > fullReward - reward) { - prepaidAmountIn = fullReward - reward; - } -} - - _paymentIn(prepaidAmount, prepaidLastMineTime, _minedTs); - prepaidAmountCap = prepaidAmount; + uint256 fullReward = _paymentIn(STORAGE_COST << SHARD_ENTRY_BITS, info.lastMineTime, _minedTs); + uint256 prepaidAmountIn = _paymentIn(prepaidAmount, prepaidLastMineTime, _minedTs); + uint256 rewardCap = fullReward - reward; + if (prepaidAmountIn > rewardCap) { + prepaidAmountSaved = prepaidAmountIn - rewardCap; + prepaidAmountIn = rewardCap; } - reward += _paymentIn(prepaidAmountCap, prepaidLastMineTime, _minedTs); + reward += prepaidAmountIn; updatePrepaidTime = true; } } diff --git a/contracts/test/StorageContractTest.t.sol b/contracts/test/StorageContractTest.t.sol index a6157b0..a8d4bc5 100644 --- a/contracts/test/StorageContractTest.t.sol +++ b/contracts/test/StorageContractTest.t.sol @@ -65,25 +65,23 @@ contract StorageContractTest is Test { uint256 valueToSent = 50000000; uint256 withdrawAmount = 8000000; uint256 mineTs = 10000; - uint40 kvEntryCount = 1; uint256 shardEntry = 1 << (SHARD_SIZE_BITS - MAX_KV_SIZE); - + address miner = vm.addr(2); storageContract.sendValue{value: valueToSent}(); - storageContract.setKvEntryCount(kvEntryCount); - uint256 reward = storageContract.paymentIn(STORAGE_COST * kvEntryCount, 0, mineTs); + + // a little half + storageContract.setKvEntryCount(1); + uint256 reward = storageContract.paymentIn(STORAGE_COST, 0, mineTs); uint256 prepaidReward = storageContract.paymentIn(PREPAID_AMOUNT, 0, mineTs); reward += prepaidReward; + storageContract.rewardMiner(0, miner, mineTs, 1); uint256 treasureReward = (reward * storageContract.treasuryShare()) / 10000; uint256 minerReward = reward - treasureReward; - uint256 prepaidAmountCap = STORAGE_COST * (shardEntry - kvEntryCount); - uint256 prepaidAmountSaved = storageContract.paymentIn(prepaidAmountCap, 0, mineTs) - prepaidReward; - - storageContract.rewardMiner(0, vm.addr(2), mineTs, 1); - uint256 totalPrepaid = valueToSent + treasureReward + prepaidAmountSaved; - assertEq(storageContract.accPrepaidAmount(), totalPrepaid); + assertEq(miner.balance, minerReward); + assertEq(storageContract.accPrepaidAmount(), valueToSent + treasureReward); storageContract.withdraw(withdrawAmount); - assertEq(storageContract.accPrepaidAmount(), totalPrepaid - withdrawAmount); + assertEq(storageContract.accPrepaidAmount(), valueToSent + treasureReward - withdrawAmount); assertEq(storageContract.treasury().balance, withdrawAmount); assertEq(address(storageContract).balance, valueToSent - minerReward - withdrawAmount); } From c1442435f8cb4cb10b2ec9d699b2662f079c7aa3 Mon Sep 17 00:00:00 2001 From: syntrust Date: Tue, 24 Sep 2024 19:00:23 +0800 Subject: [PATCH 8/9] test --- contracts/test/StorageContractTest.t.sol | 29 ++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/contracts/test/StorageContractTest.t.sol b/contracts/test/StorageContractTest.t.sol index a8d4bc5..4763a2d 100644 --- a/contracts/test/StorageContractTest.t.sol +++ b/contracts/test/StorageContractTest.t.sol @@ -65,7 +65,6 @@ contract StorageContractTest is Test { uint256 valueToSent = 50000000; uint256 withdrawAmount = 8000000; uint256 mineTs = 10000; - uint256 shardEntry = 1 << (SHARD_SIZE_BITS - MAX_KV_SIZE); address miner = vm.addr(2); storageContract.sendValue{value: valueToSent}(); @@ -74,9 +73,10 @@ contract StorageContractTest is Test { uint256 reward = storageContract.paymentIn(STORAGE_COST, 0, mineTs); uint256 prepaidReward = storageContract.paymentIn(PREPAID_AMOUNT, 0, mineTs); reward += prepaidReward; - storageContract.rewardMiner(0, miner, mineTs, 1); uint256 treasureReward = (reward * storageContract.treasuryShare()) / 10000; uint256 minerReward = reward - treasureReward; + + storageContract.rewardMiner(0, miner, mineTs, 1); assertEq(miner.balance, minerReward); assertEq(storageContract.accPrepaidAmount(), valueToSent + treasureReward); @@ -86,6 +86,31 @@ contract StorageContractTest is Test { assertEq(address(storageContract).balance, valueToSent - minerReward - withdrawAmount); } + function testWithdrawRewardMinerSaved() public { + uint256 valueToSent = 50000000; + uint256 withdrawAmount = 8000000; + uint256 mineTs = 10000; + address miner = vm.addr(2); + storageContract.sendValue{value: valueToSent}(); + + // more than half + storageContract.setKvEntryCount(3); + uint256 rewardFull = storageContract.paymentIn(STORAGE_COST << (SHARD_SIZE_BITS - MAX_KV_SIZE), 0, mineTs); + (, uint256 saved,, uint256 reward) = storageContract.miningRewards(0, mineTs); + assertEq(rewardFull, reward); + uint256 treasureReward = (reward * storageContract.treasuryShare()) / 10000; + uint256 minerReward = reward - treasureReward; + + storageContract.rewardMiner(0, miner, mineTs, 1); + assertEq(miner.balance, minerReward); + assertEq(storageContract.accPrepaidAmount(), valueToSent + treasureReward + saved); + + storageContract.withdraw(withdrawAmount); + assertEq(storageContract.accPrepaidAmount(), valueToSent + treasureReward + saved - withdrawAmount); + assertEq(storageContract.treasury().balance, withdrawAmount); + assertEq(address(storageContract).balance, valueToSent - minerReward - withdrawAmount); + } + function testRewardMiner() public { address miner = vm.addr(2); uint256 mineTs = 10000; From a63abfb7cf45340815c5520c00f4e8d82eb564a6 Mon Sep 17 00:00:00 2001 From: syntrust Date: Thu, 26 Sep 2024 19:18:42 +0800 Subject: [PATCH 9/9] fix comments --- contracts/StorageContract.sol | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/contracts/StorageContract.sol b/contracts/StorageContract.sol index 779e1d2..497dd26 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -242,14 +242,11 @@ abstract contract StorageContract is DecentralizedKV, ReentrancyGuardTransient { if (updatePrepaidTime) { prepaidLastMineTime = _minedTs; } - if (prepaidAmountSaved > 0) { - accPrepaidAmount += prepaidAmountSaved; - } - accPrepaidAmount += treasuryReward; + accPrepaidAmount += prepaidAmountSaved + treasuryReward; // Update mining info. MiningLib.update(infos[_shardId], _minedTs, _diff); - require(treasuryReward + minerReward <= address(this).balance, "StorageContract: not enough balance"); + require(minerReward <= address(this).balance, "StorageContract: not enough balance"); // Actually `transfer` is limited by the amount of gas allocated, which is not sufficient to enable reentrancy attacks. // However, this behavior may restrict the extensibility of scenarios where the receiver is a contract that requires // additional gas for its fallback functions of proper operations.