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

new clock implementation and tests #1

Merged
merged 18 commits into from
Dec 22, 2023
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
20 changes: 20 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Build
on: [push]
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Build, format check and test
working-directory: ./permissionless-arbitration/contracts
run: |
forge build
forge fmt --check
forge test
4 changes: 4 additions & 0 deletions permissionless-arbitration/contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ allow_paths = ['../../machine-emulator-sdk/solidity-step/']
remappings = [
'step/=../../machine-emulator-sdk/solidity-step/',
]

[fmt]
line_length = 80
tab_width = 4
40 changes: 18 additions & 22 deletions permissionless-arbitration/contracts/src/CanonicalConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,37 @@ library ArbitrationConstants {
// Time.Duration constant CENSORSHIP_TOLERANCE =
// Time.Duration.wrap(60 * 60 * 24 * 7);

// maximum time for replaying the computation offchain
// Time.Duration constant VALIDATOR_EFFORT =
// Time.Duration.wrap(60 * 60 * 24 * 7); // TODO
// maximum time for computing the commitments offchain
// Time.Duration constant COMMITMENT_EFFORT =
// Time.Duration.wrap(60 * 60 * 4); // TODO

// Dummy
Time.Duration constant VALIDATOR_EFFORT = Time.Duration.wrap(0);
Time.Duration constant CENSORSHIP_TOLERANCE = Time.Duration.wrap(210);
// maximum time for interacting with a divergence search (match)
// Time.Duration constant MATCH_EFFORT =
// Time.Duration.wrap(60 * 60); // TODO

Time.Duration constant MAX_ALLOWANCE = Time.Duration.wrap(
Time.Duration.unwrap(CENSORSHIP_TOLERANCE)
+ Time.Duration.unwrap(COMMITMENT_EFFORT)
);

Time.Duration constant DISPUTE_TIMEOUT =
Time.Duration.wrap(
Time.Duration.unwrap(CENSORSHIP_TOLERANCE) +
Time.Duration.unwrap(VALIDATOR_EFFORT)
);
// Dummy
Time.Duration constant COMMITMENT_EFFORT = Time.Duration.wrap(0);
Time.Duration constant CENSORSHIP_TOLERANCE = Time.Duration.wrap(60 * 5);
Time.Duration constant MATCH_EFFORT = Time.Duration.wrap(60 * 2);

// 4-level tournament
// 3-level tournament
uint64 constant LEVELS = 3;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we need to update this comment.

Copy link
Collaborator Author

@stephenctw stephenctw Dec 12, 2023

Choose a reason for hiding this comment

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

Sure, what should it be updated to?

// uint64 constant LOG2_MAX_MCYCLE = 63;

/// @return log2step gap of each leaf in the tournament[level]
function log2step(uint64 level) internal pure returns (uint64) {
uint64[LEVELS] memory arr = [
uint64(31),
uint64(16),
uint64(0)
];
uint64[LEVELS] memory arr = [uint64(31), uint64(16), uint64(0)];
return arr[level];
}

/// @return height of the tournament[level] tree which is calculated by subtracting the log2step[level] from the log2step[level - 1]
function height(uint64 level) internal pure returns (uint64) {
uint64[LEVELS] memory arr = [
uint64(32),
uint64(15),
uint64(16)
];
uint64[LEVELS] memory arr = [uint64(32), uint64(15), uint64(16)];
return arr[level];
}
}
135 changes: 74 additions & 61 deletions permissionless-arbitration/contracts/src/Clock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ library Clock {
//
// View/Pure methods
//

function notInitialized(State memory state) internal pure returns (bool) {
return state.allowance.isZero();
}
Expand All @@ -35,31 +34,22 @@ library Clock {

function hasTimeLeft(State memory state) internal view returns (bool) {
if (state.startInstant.isZero()) {
// a paused clock is always considered having time left
return true;
} else {
return
state.allowance.gt(
Time.timeSpan(Time.currentTime(), state.startInstant)
);
// otherwise the allowance must be greater than the timespan from current time to start instant
return state.allowance.gt(
Time.timeSpan(Time.currentTime(), state.startInstant)
);
}
}

/// @return deadline of the two clocks should be the tolerances combined
function deadline(
State memory freshState1,
State memory freshState2
) internal view returns (Time.Instant) {
Time.Duration duration = freshState1.allowance.add(
freshState2.allowance
);
return Time.currentTime().add(duration);
}

/// @return max tolerance of the two clocks
function max(
State memory pausedState1,
State memory pausedState2
) internal pure returns (Time.Duration) {
/// @return max allowance of two paused clocks
function max(State memory pausedState1, State memory pausedState2)
internal
pure
returns (Time.Duration)
{
if (pausedState1.allowance.gt(pausedState2.allowance)) {
return pausedState1.allowance;
} else {
Expand All @@ -68,34 +58,54 @@ library Clock {
}

/// @return duration of time has elapsed since the clock timeout
function timeSinceTimeout(
State memory state
) internal view returns (Time.Duration) {
return
Time.timeSpan(Time.currentTime(), state.startInstant).monus(
state.allowance
function timeSinceTimeout(State memory state)
internal
view
returns (Time.Duration)
{
if (state.startInstant.isZero()) {
revert("a paused clock can't timeout");
}

return Time.timeSpan(Time.currentTime(), state.startInstant).monus(
state.allowance
);
}

function timeLeft(State memory state)
internal
view
returns (Time.Duration)
{
if (state.startInstant.isZero()) {
return state.allowance;
} else {
return state.allowance.monus(
Time.timeSpan(Time.currentTime(), state.startInstant)
);
}
}

//
// Storage methods
//

/// @notice re-initialize a clock with new state
function reInitialized(State storage state, State memory newState)
internal
{
Time.Duration _allowance = timeLeft(newState);
_setNewPaused(state, _allowance);
}

function setNewPaused(
State storage state,
Time.Instant checkinInstant,
Time.Duration initialAllowance
) internal {
Time.Duration allowance = initialAllowance.monus(
Time.currentTime().timeSpan(checkinInstant)
);

if (allowance.isZero()) {
revert("can't create clock with zero time");
}

state.allowance = allowance;
state.startInstant = Time.ZERO_INSTANT;
Time.Duration _allowance =
initialAllowance.monus(Time.currentTime().timeSpan(checkinInstant));
_setNewPaused(state, _allowance);
}

/// @notice Resume the clock from pause state, or pause a clock and update the allowance
Expand All @@ -110,22 +120,26 @@ library Clock {
state.allowance = _timeLeft;
}

function addValidatorEffort(State storage state, Time.Duration deduction) internal {
Time.Duration _timeLeft = state.allowance.monus(
deduction
);
/// @notice Deduct duration from a clock and set it to paused.
/// The clock must have time left after deduction.
function deduct(State storage state, Time.Duration deduction) internal {
Time.Duration _timeLeft = state.allowance.monus(deduction);
_setNewPaused(state, _timeLeft);
}

if (_timeLeft.isZero()) {
revert("can't reset clock with no time left");
}
/// @notice Add `MATCH_EFFORT` to a clock and set it to paused.
/// The new clock allowance is capped by `MAX_ALLOWANCE`.
function addMatchEffort(State storage state) internal {
Time.Duration _timeLeft = timeLeft(state);

Time.Duration _allowance = _timeLeft.add(ArbitrationConstants.VALIDATOR_EFFORT);
if (_allowance.gt(ArbitrationConstants.DISPUTE_TIMEOUT)) {
_allowance = ArbitrationConstants.DISPUTE_TIMEOUT;
Time.Duration _allowance =
_timeLeft.add(ArbitrationConstants.MATCH_EFFORT);

if (_allowance.gt(ArbitrationConstants.MAX_ALLOWANCE)) {
_allowance = ArbitrationConstants.MAX_ALLOWANCE;
}

state.allowance = _allowance;
state.startInstant = Time.ZERO_INSTANT;
_setNewPaused(state, _allowance);
}

function setPaused(State storage state) internal {
Expand All @@ -137,23 +151,22 @@ library Clock {
//
// Private
//

function timeLeft(State memory state) private view returns (Time.Duration) {
if (state.startInstant.isZero()) {
return state.allowance;
} else {
return
state.allowance.monus(
Time.timeSpan(Time.currentTime(), state.startInstant)
);
}
}

function toggleClock(State storage state) private {
if (state.startInstant.isZero()) {
state.startInstant = Time.currentTime();
} else {
state.startInstant = Time.ZERO_INSTANT;
}
}

function _setNewPaused(State storage state, Time.Duration allowance)
private
{
if (allowance.isZero()) {
revert("can't create clock with zero time");
}

state.allowance = allowance;
state.startInstant = Time.ZERO_INSTANT;
}
}
37 changes: 15 additions & 22 deletions permissionless-arbitration/contracts/src/Commitment.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,14 @@ library Commitment {
bytes32[] calldata hashProof
) internal pure {
uint64 treeHeight = ArbitrationConstants.height(level);
Tree.Node expectedCommitment = getRoot(
Machine.Hash.unwrap(state),
treeHeight,
position,
hashProof
);
Tree.Node expectedCommitment =
getRoot(Machine.Hash.unwrap(state), treeHeight, position, hashProof);

require(commitment.eq(expectedCommitment), "commitment state doesn't match");
require(
commitment.eq(expectedCommitment), "commitment state doesn't match"
);
}


function isEven(uint256 x) private pure returns (bool) {
return x % 2 == 0;
}
Expand All @@ -42,23 +39,19 @@ library Commitment {
uint256 position,
bytes32[] calldata siblings
) internal pure returns (Tree.Node) {
uint nodesCount = treeHeight - 1;
assert(nodesCount == siblings.length);
assert(treeHeight == siblings.length);

for (uint i = 0; i < nodesCount; i++) {
for (uint256 i = 0; i < treeHeight; i++) {
if (isEven(position >> i)) {
leaf =
keccak256(abi.encodePacked(leaf, siblings[i]));
leaf = keccak256(abi.encodePacked(leaf, siblings[i]));
} else {
leaf =
keccak256(abi.encodePacked(siblings[i], leaf));
leaf = keccak256(abi.encodePacked(siblings[i], leaf));
}
}

return Tree.Node.wrap(leaf);
}


function requireFinalState(
Tree.Node commitment,
uint64 level,
Expand All @@ -67,23 +60,23 @@ library Commitment {
) internal pure {
uint64 treeHeight = ArbitrationConstants.height(level);
Tree.Node expectedCommitment = getRootForLastLeaf(
treeHeight,
Machine.Hash.unwrap(finalState),
hashProof
treeHeight, Machine.Hash.unwrap(finalState), hashProof
);

require(commitment.eq(expectedCommitment), "commitment last state doesn't match");
require(
commitment.eq(expectedCommitment),
"commitment last state doesn't match"
);
}


function getRootForLastLeaf(
uint64 treeHeight,
bytes32 leaf,
bytes32[] calldata siblings
) internal pure returns (Tree.Node) {
assert(treeHeight == siblings.length);

for (uint i = 0; i < treeHeight; i++) {
for (uint256 i = 0; i < treeHeight; i++) {
leaf = keccak256(abi.encodePacked(siblings[i], leaf));
}

Expand Down
Loading