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

SafeCore now stores only recent state roots #546

Merged
merged 5 commits into from
Dec 18, 2018

Conversation

schemar
Copy link
Contributor

@schemar schemar commented Dec 13, 2018

The safe core now only tracks a fixed number of most recent state roots.
The number of state roots to track can be set on construction.
The safe core has a ring buffer that tracks the last n state roots and
from n + 1 starts overwriting the oldest state root whenever a new
state root is committed.

I created a separate contract for the circular buffer in order to
decouple the concept of a circular buffer and the concept of a state
root storage. There were three options of how to add the circular buffer
logic to the state root:

  1. Deploy the buffer as a separate contract (on core construction) and
    call to that contract for storage.
  2. Use dependency injection and pass the address of the buffer when
    constructing the core.
  3. Extend the buffer with the core to make the logic available.
  4. Make the buffer a library.

Option 4. was quickly ruled out, as the storage part of the buffer is
essential to its logic and inner workings.

Options 1. and 2. are somewhat similar. They very well decouple the
contracts and make testing easy. From an architectural point of view,
option 2. is the easy choice. However, in solidity it does cost actual
money to split contracts and make external calls. So that's the
drawback.

Option 3. puts everything into a single contract and makes all calls to
the buffer internal, which is good from an economical point of view. The
drawback is of cours that the contracts are very tightly coupled and
testing is a pain, as a wrapper contract must be written for the buffer
to "externalize" its methods.

After a discussion with @benjaminbollen and @pgev, I was convinced to
choose the economically more viable approach over the better
architectural approach. Core now extends the buffer and makes internal
calls to it. I decided to leave the contract name in front of all the
method names to make it clear to the reader of the contract when the
buffer is used.

I added tests for the circular buffer and also added a test specific to
the state roots to the safe core.

Fixes #504

The safe core now only tracks a fixed number of most recent state roots.
The number of state roots to track can be set on construction.
The safe core has a ring buffer that tracks the last `n` state roots and
from `n + 1` starts overwriting the oldest state root whenever a new
state root is committed.

I created a separate contract for the circular buffer in order to
decouple the concept of a circular buffer and the concept of a state
root storage. There were three options of how to add the circular buffer
logic to the state root:

1. Deploy the buffer as a separate contract (on core construction) and
   call to that contract for storage.
2. Use dependency injection and pass the address of the buffer when
   constructing the core.
3. Extend the buffer with the core to make the logic available.
4. Make the buffer a library.

Option 4. was quickly ruled out, as the storage part of the buffer is
essential to its logic and inner workings.

Options 1. and 2. are somewhat similar. They very well decouple the
contracts and make testing easy. From an architectural point of view,
option 2. is the easy choice. However, in solidity it does cost actual
money to split contracts and make external calls. So that's the
drawback.

Option 3. puts everything into a single contract and makes all calls to
the buffer internal, which is good from an economical point of view. The
drawback is of cours that the contracts are very tightly coupled and
testing is a pain, as a wrapper contract must be written for the buffer
to "externalize" its methods.

After a discussion with @benjaminbollen and @pgev, I was convinced to
choose the economically more viable approach over the better
architectural approach. Core now extends the buffer and makes `internal`
calls to it. I decided to leave the contract name in front of all the
method names to make it clear to the reader of the contract when the
buffer is used.

I added tests for the circular buffer and also added a test specific to
the state roots to the safe core.

Fixes OpenST#504
Copy link
Contributor

@0xsarvesh 0xsarvesh left a comment

Choose a reason for hiding this comment

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

Very Clean and elegant design 💯 🥇
Look really good to me 🔥 ⚡️

test/lib/circular_buffer_uint/constructor.js Show resolved Hide resolved
test/lib/circular_buffer_uint/constructor.js Show resolved Hide resolved
Copy link
Contributor

@gulshanvasnani gulshanvasnani left a comment

Choose a reason for hiding this comment

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

Great work 👍 💯
Few comments. Please see inline.

contracts/lib/CircularBufferUint.sol Outdated Show resolved Hide resolved
contracts/lib/CircularBufferUint.sol Show resolved Hide resolved
test/gateway/gateway_base/prove_gateway.js Outdated Show resolved Hide resolved
contracts/test/lib/TestCircularBufferUint.sol Outdated Show resolved Hide resolved
"Given block height is lower or equal to highest committed state root block height."
);

stateRoots[_blockHeight] = _stateRoot;
latestStateRootBlockHeight = _blockHeight;
uint256 oldestStoredBlockHeight = CircularBufferUint.store(_blockHeight);
delete stateRoots[oldestStoredBlockHeight];

emit StateRootAvailable(_blockHeight, _stateRoot);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think to emit an event for block height which is overridden.It will ensure from JS layer we wont call getStateRoot for the block height which is deleted.
What do you think ?
@sarvesh-ost @deepesh-kn

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could be helpful. But at the same time, the JS layer should switch to a newer state root as it becomes available. Or is that not always feasible?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes,it is feasible.

test/lib/circular_buffer_uint/constructor.js Outdated Show resolved Hide resolved
Copy link
Contributor

@deepesh-kn deepesh-kn left a comment

Choose a reason for hiding this comment

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

🤝 🙌 Yes, logic is nice.
Just a few inline comments.

@@ -38,7 +39,7 @@ import "../lib/SafeMath.sol";
* by the workers that are registered as part of the `Organized`
* interface.
*/
contract SafeCore is StateRootInterface, Organized {
contract SafeCore is StateRootInterface, Organized, CircularBufferUint {
using SafeMath for uint256;
Copy link
Contributor

Choose a reason for hiding this comment

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

As per style guide use the following


    /* Usings */

Copy link
Contributor

Choose a reason for hiding this comment

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

All section are declared as /** abc */.
eg /** Events */, we need to change all to /* abc */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done and done 👍

contracts/gateway/SafeCore.sol Outdated Show resolved Hide resolved
contracts/lib/CircularBufferUint.sol Outdated Show resolved Hide resolved
contracts/test/core/MockSafeCore.sol Outdated Show resolved Hide resolved
contracts/test/lib/TestCircularBufferUint.sol Outdated Show resolved Hide resolved
Copy link
Contributor Author

@schemar schemar left a comment

Choose a reason for hiding this comment

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

All updated, please check 👍

@@ -38,7 +39,7 @@ import "../lib/SafeMath.sol";
* by the workers that are registered as part of the `Organized`
* interface.
*/
contract SafeCore is StateRootInterface, Organized {
contract SafeCore is StateRootInterface, Organized, CircularBufferUint {
using SafeMath for uint256;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done and done 👍

contracts/gateway/SafeCore.sol Outdated Show resolved Hide resolved
"Given block height is lower or equal to highest committed state root block height."
);

stateRoots[_blockHeight] = _stateRoot;
latestStateRootBlockHeight = _blockHeight;
uint256 oldestStoredBlockHeight = CircularBufferUint.store(_blockHeight);
delete stateRoots[oldestStoredBlockHeight];

emit StateRootAvailable(_blockHeight, _stateRoot);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could be helpful. But at the same time, the JS layer should switch to a newer state root as it becomes available. Or is that not always feasible?

contracts/lib/CircularBufferUint.sol Outdated Show resolved Hide resolved
contracts/lib/CircularBufferUint.sol Show resolved Hide resolved
contracts/test/core/MockSafeCore.sol Outdated Show resolved Hide resolved
contracts/test/lib/TestCircularBufferUint.sol Outdated Show resolved Hide resolved
test/lib/circular_buffer_uint/constructor.js Show resolved Hide resolved
test/lib/circular_buffer_uint/constructor.js Outdated Show resolved Hide resolved
test/lib/circular_buffer_uint/constructor.js Show resolved Hide resolved
@gulshanvasnani
Copy link
Contributor

gulshanvasnani commented Dec 18, 2018

LGTM 👍
Thank you for the changes.

Copy link
Contributor

@deepesh-kn deepesh-kn left a comment

Choose a reason for hiding this comment

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

LGTM 🎉

@deepesh-kn deepesh-kn merged commit c6b7d09 into OpenST:develop Dec 18, 2018
@schemar schemar deleted the core_storage branch December 18, 2018 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Optimise storage for simple core.
4 participants