diff --git a/contracts/test/EthStorageContractTest.t.sol b/contracts/test/EthStorageContractTest.t.sol new file mode 100644 index 0000000..25ca938 --- /dev/null +++ b/contracts/test/EthStorageContractTest.t.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED +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 { + uint256 constant STORAGE_COST = 1000; + uint256 constant SHARD_SIZE_BITS = 19; + uint256 constant MAX_KV_SIZE = 17; + uint256 constant PREPAID_AMOUNT = 2 * STORAGE_COST; + + TestEthStorageContract storageContract; + address owner = address(0x1); + + function setUp() public { + TestEthStorageContract imp = new TestEthStorageContract( + StorageContract.Config(MAX_KV_SIZE, SHARD_SIZE_BITS, 2, 0, 0, 0), 0, STORAGE_COST, 0 + ); + 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 = TestEthStorageContract(address(proxy)); + } + + function testPutBlobs() public { + bytes32[] memory keys = new bytes32[](2); + keys[0] = bytes32(uint256(0)); + keys[1] = bytes32(uint256(1)); + uint256[] memory blobIdxs = new uint256[](2); + blobIdxs[0] = 0; + blobIdxs[1] = 0; + uint256[] memory lengths = new uint256[](2); + lengths[0] = 10; + lengths[1] = 20; + + uint256 insufficientCost = storageContract.upfrontPayment(); + + // Expect the specific revert reason from _prepareBatchAppend due to insufficient msg.value + vm.expectRevert("DecentralizedKV: not enough batch payment"); + storageContract.putBlobs{value: insufficientCost}(keys, blobIdxs, lengths); + + // Enough storage cost + uint256 sufficientCost = 2 * storageContract.upfrontPayment(); + storageContract.putBlobs{value: sufficientCost}(keys, blobIdxs, lengths); + + assertEq(storageContract.kvEntryCount(), 2); + assertEq(storageContract.hash(keys[0]), bytes32(uint256(1 << 8 * 8))); + assertEq(storageContract.hash(keys[1]), bytes32(uint256(2 << 8 * 8))); + assertEq(storageContract.size(keys[0]), 10); + assertEq(storageContract.size(keys[1]), 20); + + // Still pass two keys, but only appending a new key-value + lengths[0] = 30; + keys[1] = bytes32(uint256(2)); + lengths[1] = 30; + + sufficientCost = storageContract.upfrontPayment(); + storageContract.putBlobs{value: sufficientCost}(keys, blobIdxs, lengths); + assertEq(storageContract.kvEntryCount(), 3); + assertEq(storageContract.hash(bytes32(uint256(0))), bytes32(uint256(1 << 8 * 8))); + assertEq(storageContract.hash(bytes32(uint256(1))), bytes32(uint256(2 << 8 * 8))); + assertEq(storageContract.hash(bytes32(uint256(2))), bytes32(uint256(2 << 8 * 8))); + assertEq(storageContract.size(bytes32(uint256(0))), 30); + assertEq(storageContract.size(bytes32(uint256(1))), 20); + assertEq(storageContract.size(bytes32(uint256(2))), 30); + } +} diff --git a/contracts/test/TestEthStorageContract.sol b/contracts/test/TestEthStorageContract.sol index 0a1bdf9..d023815 100644 --- a/contracts/test/TestEthStorageContract.sol +++ b/contracts/test/TestEthStorageContract.sol @@ -13,12 +13,9 @@ contract TestEthStorageContract is EthStorageContract { bytes32[] proofs; } - constructor( - Config memory _config, - uint256 _startTime, - uint256 _storageCost, - uint256 _dcfFactor - ) EthStorageContract(_config, _startTime, _storageCost, _dcfFactor) {} + constructor(Config memory _config, uint256 _startTime, uint256 _storageCost, uint256 _dcfFactor) + EthStorageContract(_config, _startTime, _storageCost, _dcfFactor) + {} function setTimestamp(uint256 ts) public { require(ts > currentTimestamp, "ts"); @@ -30,6 +27,27 @@ contract TestEthStorageContract is EthStorageContract { _putInternal(key, dataHash, data.length); } + function putBlobs(bytes32[] memory _keys, uint256[] memory _blobIdxs, uint256[] memory _lengths) + public + payable + override + { + require(_keys.length == _blobIdxs.length, "EthStorageContract: key length mismatch"); + require(_keys.length == _lengths.length, "EthStorageContract: length length mismatch"); + + bytes32[] memory dataHashes = new bytes32[](_blobIdxs.length); + for (uint256 i = 0; i < _blobIdxs.length; i++) { + dataHashes[i] = bytes32(i + 1 << 8 * 8); // dummy data hash + require(dataHashes[i] != 0, "EthStorageContract: failed to get blob hash"); + } + + uint256[] memory kvIdxs = _putBatchInternal(_keys, dataHashes, _lengths); + + for (uint256 i = 0; i < _blobIdxs.length; i++) { + emit PutBlob(kvIdxs[i], _lengths[i], dataHashes[i]); + } + } + function getEncodingKey(uint256 kvIdx, address miner) public view returns (bytes32) { return keccak256(abi.encode(kvMap[idxMap[kvIdx]].hash, miner, kvIdx)); } @@ -132,18 +150,9 @@ contract TestEthStorageContract is EthStorageContract { bytes[] calldata inclusiveProofs, bytes[] calldata decodeProof ) public virtual override { - return - _mineWithoutDiffCompare( - blockNumber, - shardId, - miner, - nonce, - encodedSamples, - masks, - randaoProof, - inclusiveProofs, - decodeProof - ); + return _mineWithoutDiffCompare( + blockNumber, shardId, miner, nonce, encodedSamples, masks, randaoProof, inclusiveProofs, decodeProof + ); } function _mineWithFixedHash0(