diff --git a/contracts/EthStorageContractL2.sol b/contracts/EthStorageContractL2.sol index a3017b8..9ec0c44 100644 --- a/contracts/EthStorageContractL2.sol +++ b/contracts/EthStorageContractL2.sol @@ -28,62 +28,29 @@ 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) { + 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 e7277ca..ffe940a 100644 --- a/contracts/StorageContract.sol +++ b/contracts/StorageContract.sol @@ -164,7 +164,7 @@ abstract contract StorageContract is DecentralizedKV { uint256 totalPayment = 0; if ((totalEntries >> SHARD_ENTRY_BITS) > shardId) { uint256 kvCountNew = totalEntries % (1 << SHARD_ENTRY_BITS); - totalPayment += _upfrontPayment(block.timestamp) * kvCountNew; + totalPayment += _upfrontPayment(_blockTs()) * kvCountNew; totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * (_batchSize - kvCountNew); } else { totalPayment += _upfrontPayment(infos[shardId].lastMineTime) * _batchSize; @@ -182,7 +182,7 @@ abstract contract StorageContract is DecentralizedKV { 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; + infos[shardId].lastMineTime = _blockTs(); } } @@ -279,16 +279,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 = block.timestamp - (block.number - _blockNumber) * 12; + 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. @@ -298,7 +298,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, @@ -309,15 +309,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 ); } @@ -341,7 +333,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. @@ -351,7 +343,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, @@ -361,12 +353,11 @@ abstract contract StorageContract is DecentralizedKV { 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"); + 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 = RandaoLib.verifyHistoricalRandao(_blockNumber, _randaoProof); + bytes32 hash0 = _getRandao(_blockNum, _randaoProof); // Estimate block timestamp - uint256 mineTs = block.timestamp - (block.number - _blockNumber) * 12; + 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"); @@ -383,6 +374,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 _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 _blockNum) internal view returns (uint256) { + return _blockTs() - (_blockNumber() - _blockNum) * 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..fecae4e 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(_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); }); });