Skip to content
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

Merged
merged 4 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions contracts/utils/FreezeLog.sol
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);
}
}
252 changes: 252 additions & 0 deletions tests/utils/test_FreezeLog.py
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どの様な観点のケースなのかを書く様にした方が良いです。

Copy link
Member Author

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.

# 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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

書き込まれたステートを確認する必要があると思います。
recordLog後、getLogを実行して、書き込まれた内容と一致することをassertするのが良いと思います。

Copy link
Member Author

Choose a reason for hiding this comment

The 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"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revertされた状態で、getLogをして、ステートの状態がupdate前の状態になっていることを確認するケースにした方が良いと思います

Copy link
Member Author

Choose a reason for hiding this comment

The 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