-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add log storage contract with frozen past logs #380
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/** | ||
* Copyright BOOSTRY Co., Ltd. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
/// @title Freeze Log | ||
contract FreezeLog { | ||
// Event: log recorded | ||
event Recorded( | ||
address indexed recorder, | ||
uint256 index, | ||
uint256 _freezingGraceBlockCount | ||
); | ||
|
||
// Event: log updated | ||
event Updated(address indexed recorder, uint256 index); | ||
|
||
struct Log { | ||
address recorder; // Recorder | ||
string log; // Log text | ||
uint256 createdBlockNumber; // Created block number | ||
uint256 freezingGraceBlockCount; // Freezing grace block count | ||
} | ||
|
||
/// Recorder -> Index | ||
mapping(address => uint256) public last_log_index; | ||
/// Recorder -> Index -> Log | ||
mapping(address => mapping(uint256 => Log)) public logs; | ||
|
||
// [CONSTRUCTOR] | ||
constructor() {} | ||
|
||
/// @notice Get last index | ||
/// @param _recorder Recorder | ||
/// @return _index Last index | ||
function lastLogIndex(address _recorder) | ||
public | ||
view | ||
returns (uint256 _index) | ||
{ | ||
return last_log_index[_recorder]; | ||
} | ||
|
||
/// @notice Record new logs | ||
/// @param _log Log text | ||
/// @param _freezingGraceBlockCount Freezing grace block count | ||
function recordLog(string memory _log, uint256 _freezingGraceBlockCount) | ||
public | ||
{ | ||
Log storage log = logs[msg.sender][last_log_index[msg.sender]]; | ||
|
||
log.recorder = msg.sender; | ||
log.log = _log; | ||
log.createdBlockNumber = block.number; | ||
log.freezingGraceBlockCount = _freezingGraceBlockCount; | ||
last_log_index[msg.sender]++; | ||
|
||
emit Recorded( | ||
msg.sender, | ||
last_log_index[msg.sender] - 1, | ||
_freezingGraceBlockCount | ||
); | ||
} | ||
|
||
/// @notice Update recorded logs | ||
/// @param _index Index | ||
/// @param _log Log to be updated | ||
function updateLog(uint256 _index, string memory _log) public { | ||
Log storage storageLog = logs[msg.sender][_index]; | ||
|
||
require( | ||
storageLog.createdBlockNumber + | ||
storageLog.freezingGraceBlockCount >= | ||
block.number, | ||
"frozen" | ||
); | ||
|
||
storageLog.log = _log; | ||
|
||
emit Updated(msg.sender, _index); | ||
} | ||
|
||
/// @notice Get log | ||
/// @param _recorder Recorder | ||
/// @param _index Index | ||
function getLog(address _recorder, uint256 _index) | ||
public | ||
view | ||
returns ( | ||
uint256 _createdBlockNumber, | ||
uint256 _freezingGraceBlockCount, | ||
string memory _log | ||
) | ||
{ | ||
Log storage log = logs[_recorder][_index]; | ||
return (log.createdBlockNumber, log.freezingGraceBlockCount, log.log); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
""" | ||
Copyright BOOSTRY Co., Ltd. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, | ||
software distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
SPDX-License-Identifier: Apache-2.0 | ||
""" | ||
import brownie | ||
|
||
|
||
class TestRecordLog: | ||
|
||
########################################################## | ||
# Normal | ||
########################################################## | ||
|
||
# Normal_1 | ||
# Add a record once. | ||
def test_normal_1(self, FreezeLog, users, web3): | ||
admin = users["admin"] | ||
user = users["user1"] | ||
|
||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
test_block = web3.eth.block_number + 1 | ||
test_message = "test_message" | ||
test_freezing_grace_block_count = 5 | ||
|
||
# Record Log | ||
tx = freeze_log.recordLog( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 書き込まれたステートを確認する必要があると思います。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some codes after “recordLog” and “updateLog” that ensure chain state changed properly. |
||
test_message, test_freezing_grace_block_count, {"from": user} | ||
) | ||
|
||
# Assertion | ||
assert tx.events["Recorded"]["recorder"] == user | ||
|
||
log = freeze_log.getLog(user.address, 0, {"from": user}) | ||
assert log == (test_block, test_freezing_grace_block_count, test_message) | ||
|
||
# Normal_2 | ||
# 2 person add a record each. | ||
def test_normal_2(self, FreezeLog, users, web3): | ||
admin = users["admin"] | ||
user1 = users["user1"] | ||
user2 = users["user2"] | ||
|
||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
test_block = web3.eth.block_number + 1 | ||
test_message = "test_message" | ||
test_freezing_grace_block_count = 5 | ||
|
||
# Record Log | ||
tx = freeze_log.recordLog( | ||
test_message, test_freezing_grace_block_count, {"from": user1} | ||
) | ||
|
||
# Assertion | ||
assert tx.events["Recorded"]["recorder"] == user1.address | ||
log = freeze_log.getLog(user1.address, 0, {"from": user1}) | ||
assert log == (test_block, test_freezing_grace_block_count, test_message) | ||
|
||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
test_block = web3.eth.block_number + 1 | ||
test_message = "test_message" | ||
test_freezing_grace_block_count = 5 | ||
|
||
# Record Log | ||
tx = freeze_log.recordLog( | ||
test_message, test_freezing_grace_block_count, {"from": user2} | ||
) | ||
|
||
# Assertion | ||
assert tx.events["Recorded"]["recorder"] == user2.address | ||
log = freeze_log.getLog(user2.address, 0, {"from": user2}) | ||
assert log == (test_block, test_freezing_grace_block_count, test_message) | ||
|
||
|
||
class TestLastLogIndex: | ||
|
||
########################################################## | ||
# Normal | ||
########################################################## | ||
|
||
# Normal_1 | ||
# Get last_log_index after adding a record. | ||
def test_normal_1(self, FreezeLog, users, web3): | ||
admin = users["admin"] | ||
user1 = users["user1"] | ||
user2 = users["user2"] | ||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
test_block = web3.eth.block_number + 1 | ||
test_message = "test_message" | ||
test_freezing_grace_block_count = 5 | ||
|
||
# Record Log | ||
tx = freeze_log.recordLog( | ||
test_message, test_freezing_grace_block_count, {"from": user1} | ||
) | ||
|
||
# get last_log_index of user1 | ||
user1_last_log_index = freeze_log.lastLogIndex(user1, {"from": user2}) | ||
assert user1_last_log_index == 1 | ||
# get last_log_index of user2 | ||
user2_last_log_index = freeze_log.lastLogIndex(user2, {"from": user2}) | ||
assert user2_last_log_index == 0 | ||
|
||
|
||
class TestUpdateLog: | ||
|
||
########################################################## | ||
# Normal | ||
########################################################## | ||
|
||
# Normal_1 | ||
# After added a record, updating the record message. | ||
def test_normal_1(self, FreezeLog, users, web3): | ||
admin = users["admin"] | ||
user = users["user1"] | ||
|
||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
test_block = web3.eth.block_number + 1 | ||
test_message = "test_message" | ||
test_freezing_grace_block_count = 5 | ||
|
||
# Record Log | ||
tx = freeze_log.recordLog( | ||
test_message, test_freezing_grace_block_count, {"from": user} | ||
) | ||
# Assertion | ||
assert tx.events["Recorded"]["recorder"] == user | ||
|
||
user_last_log_index = freeze_log.lastLogIndex(user, {"from": user}) | ||
|
||
tx = freeze_log.updateLog( | ||
user_last_log_index - 1, test_message[::-1], {"from": user} | ||
) | ||
|
||
# Assertion | ||
assert tx.events["Updated"]["recorder"] == user | ||
assert tx.events["Updated"]["index"] == user_last_log_index - 1 | ||
|
||
log = freeze_log.getLog(user.address, user_last_log_index - 1, {"from": user}) | ||
assert log[0] == test_block | ||
assert log[1] == test_freezing_grace_block_count | ||
assert log[2] == test_message[::-1] | ||
|
||
########################################################## | ||
# Error | ||
########################################################## | ||
|
||
# Error_1 | ||
# Trying to update log of non-existent index, but fail. | ||
def test_error_1(self, FreezeLog, users, web3): | ||
admin = users["admin"] | ||
user1 = users["user1"] | ||
|
||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
|
||
test_idx = 10 | ||
test_message = "test1 message" | ||
with brownie.reverts(): | ||
freeze_log.updateLog(test_idx, test_message, {"from": user1}) | ||
log = freeze_log.getLog(user1.address, 0, {"from": user1}) | ||
assert log == (0, 0, "") | ||
|
||
# Error_2 | ||
# Trying to update a frozen log, but fail. | ||
def test_error_2(self, FreezeLog, users, web3): | ||
admin = users["admin"] | ||
user1 = users["user1"] | ||
|
||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
|
||
test1_block = web3.eth.block_number + 1 | ||
test1_message = "test1 message" | ||
test1_freezing_grace_block_count = 2 | ||
|
||
# Write Log | ||
tx1 = freeze_log.recordLog( | ||
test1_message, test1_freezing_grace_block_count, {"from": user1} | ||
) | ||
log = freeze_log.getLog(user1.address, 0, {"from": user1}) | ||
|
||
# Assertion | ||
assert tx1.events["Recorded"]["recorder"] == user1.address | ||
assert log[0] == test1_block | ||
assert log[1] == test1_freezing_grace_block_count | ||
assert log[2] == test1_message | ||
|
||
# Add logs to freeze first log. | ||
for i in range(test1_freezing_grace_block_count): | ||
tx1 = freeze_log.recordLog( | ||
test1_message, test1_freezing_grace_block_count, {"from": user1} | ||
) | ||
|
||
# Trying to update a frozen log. | ||
with brownie.reverts(revert_msg="frozen"): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. revertされた状態で、getLogをして、ステートの状態がupdate前の状態になっていることを確認するケースにした方が良いと思います There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added codes that checks chain state. |
||
test1_message_update = "test1 message updated" | ||
freeze_log.updateLog(0, test1_message_update, {"from": user1}) | ||
|
||
log = freeze_log.getLog(user1.address, 0, {"from": user1}) | ||
assert log[0] == test1_block | ||
assert log[1] == test1_freezing_grace_block_count | ||
assert log[2] == test1_message | ||
|
||
|
||
class TestGetLog: | ||
|
||
########################################################## | ||
# Normal | ||
########################################################## | ||
|
||
# Normal_1 | ||
# Get a log record by index. | ||
def test_normal_1(self, FreezeLog, users, web3): | ||
admin = users["admin"] | ||
user1 = users["user1"] | ||
|
||
# Deploy contract | ||
freeze_log = admin.deploy(FreezeLog) | ||
test1_block = web3.eth.block_number + 1 | ||
test1_message = "test1 message" | ||
test1_freezing_grace_block_count = 1 | ||
|
||
# Write Log | ||
tx = freeze_log.recordLog( | ||
test1_message, test1_freezing_grace_block_count, {"from": user1} | ||
) | ||
|
||
# Assertion | ||
assert tx.events["Recorded"]["recorder"] == user1.address | ||
|
||
user1_last_log_index = freeze_log.lastLogIndex(user1.address) | ||
|
||
log = freeze_log.getLog( | ||
user1.address, user1_last_log_index - 1, {"from": user1} | ||
) | ||
assert log[0] == test1_block | ||
assert log[1] == test1_freezing_grace_block_count | ||
assert log[2] == test1_message |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
どの様な観点のケースなのかを書く様にした方が良いです。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a summary comment to each test case.