Skip to content

Commit

Permalink
feat: Multiple channelIDs per state.
Browse files Browse the repository at this point in the history
Signed-off-by: Sophia Koehler <sophia@perun.network>
  • Loading branch information
sophia1ch committed Sep 17, 2024
1 parent f2cc98d commit afba2e1
Show file tree
Hide file tree
Showing 9 changed files with 3,545 additions and 2,168 deletions.
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ Chair of Applied Cryptography, Technische Universität Darmstadt, Germany
Oliver Tale-Yazdi <oliver@perun.network>
Marius van der Wijden <marius@perun.network>
Ilja von Hoessle <ilja@perun.network>
Sophia Koehler <sophia@perun.network>
53 changes: 39 additions & 14 deletions contracts/Adjudicator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ contract Adjudicator {
locked[s],
_channel.state
);
require(subAlloc.ID == _state.channelID, "invalid sub-channel id");
require(Channel.AreBytes32ArraysEqual(subAlloc.ID, _state.channelID), "invalid sub-channel id");

uint256[] memory _outcome;
(_outcome, nextIndex) = registerRecursive(
Expand Down Expand Up @@ -163,7 +163,10 @@ contract Adjudicator {
}

// If registered, require newer version and refutation timeout not passed.
(Dispute memory dispute, bool registered) = getDispute(state.channelID);
(Dispute memory dispute, bool registered) = getDispute(state.channelID[Channel.FindBackendIndex(
state.channelID,
state.outcome.backends
)]);
if (registered) {
if (dispute.stateHash == hashState(state)) {
// Skip if same state.
Expand Down Expand Up @@ -207,7 +210,10 @@ contract Adjudicator {
uint256 actorIdx,
bytes memory sig
) external {
Dispute memory dispute = requireGetDispute(state.channelID);
Dispute memory dispute = requireGetDispute(state.channelID[Channel.FindBackendIndex(
state.channelID,
state.outcome.backends
)]);
if (dispute.phase == uint8(DisputePhase.DISPUTE)) {
// solhint-disable-next-line not-rely-on-time
require(block.timestamp >= dispute.timeout, "timeout not passed");
Expand Down Expand Up @@ -290,7 +296,10 @@ contract Adjudicator {
Channel.validateSignatures(params, state, sigs);

// If registered, require not concluded.
(Dispute memory dispute, bool registered) = getDispute(state.channelID);
(Dispute memory dispute, bool registered) = getDispute(state.channelID[Channel.FindBackendIndex(
state.channelID,
state.outcome.backends
)]);
if (registered) {
require(
dispute.phase != uint8(DisputePhase.CONCLUDED),
Expand Down Expand Up @@ -338,7 +347,11 @@ contract Adjudicator {
Channel.Params memory params,
Channel.State memory state
) internal pure {
require(state.channelID == channelID(params), "invalid params");
uint256 zeroIndex = Channel.FindBackendIndex(
state.channelID,
state.outcome.backends
);
require(state.channelID[zeroIndex] == channelID(params), "invalid params");
}

/**
Expand All @@ -353,7 +366,11 @@ contract Adjudicator {
Channel.State memory state,
DisputePhase disputePhase
) internal {
(Dispute memory dispute, bool registered) = getDispute(state.channelID);
uint256 zeroIndex = Channel.FindBackendIndex(
state.channelID,
state.outcome.backends
);
(Dispute memory dispute, bool registered) = getDispute(state.channelID[zeroIndex]);

dispute.challengeDuration = uint64(params.challengeDuration);
dispute.version = state.version;
Expand All @@ -376,7 +393,7 @@ contract Adjudicator {
);
}

setDispute(state.channelID, dispute);
setDispute(state.channelID[zeroIndex], dispute);
}

/**
Expand Down Expand Up @@ -455,7 +472,11 @@ contract Adjudicator {
* Reverts if the channel is already concluded.
*/
function concludeSingle(Channel.State memory state) internal {
Dispute memory dispute = requireGetDispute(state.channelID);
uint64 zeroIndex = Channel.FindBackendIndex(
state.channelID,
state.outcome.backends
);
Dispute memory dispute = requireGetDispute(state.channelID[zeroIndex]);
require(dispute.stateHash == hashState(state), "invalid channel state");
require(
dispute.phase != uint8(DisputePhase.CONCLUDED),
Expand All @@ -471,7 +492,7 @@ contract Adjudicator {
require(block.timestamp >= dispute.timeout, "timeout not passed yet");
dispute.phase = uint8(DisputePhase.CONCLUDED);

setDispute(state.channelID, dispute);
setDispute(state.channelID[zeroIndex], dispute);
}

/**
Expand Down Expand Up @@ -506,7 +527,7 @@ contract Adjudicator {
for (uint256 i = 0; i < locked.length; i++) {
Channel.SubAlloc memory subAlloc = locked[i];
Channel.State memory subState = subStates[nextIndex++];
require(subAlloc.ID == subState.channelID, "invalid subchannel id");
require(Channel.AreBytes32ArraysEqual(subAlloc.ID, subState.channelID), "invalid subchannel id");

uint256[][] memory subOutcome;
(subOutcome, nextIndex) = forceConcludeRecursive(
Expand All @@ -532,19 +553,23 @@ contract Adjudicator {
* Reverts if the channel is not registered.
*/
function forceConcludeSingle(Channel.State memory state) internal {
Dispute memory dispute = requireGetDispute(state.channelID);
uint64 zeroIndex = Channel.FindBackendIndex(
state.channelID,
state.outcome.backends
);
Dispute memory dispute = requireGetDispute(state.channelID[zeroIndex]);
require(dispute.stateHash == hashState(state), "invalid channel state");
if (dispute.phase != uint8(DisputePhase.CONCLUDED)) {
dispute.phase = uint8(DisputePhase.CONCLUDED);
setDispute(state.channelID, dispute);
setDispute(state.channelID[zeroIndex], dispute);
}
}

/**
* @dev pushOutcome sets the outcome at the asset holders.
*/
function pushOutcome(
bytes32 channel,
bytes32[] memory channel,
Channel.Asset[] memory assets,
Channel.Participant[] memory participants,
uint256[][] memory outcome
Expand All @@ -555,7 +580,7 @@ contract Adjudicator {
if (asset.ethHolder != address(0)) {
// solhint-disable-next-line calls-loop
AssetHolder(asset.ethHolder).setOutcome(
channel,
channel[a],
participants,
outcome[a]
);
Expand Down
34 changes: 31 additions & 3 deletions contracts/Channel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ library Channel {
}

struct State {
bytes32 channelID;
bytes32[] channelID;
uint64 version;
Allocation outcome;
bytes appData;
Expand All @@ -53,14 +53,15 @@ library Channel {

struct Allocation {
Asset[] assets;
uint256[] backends;
// Outer dimension are assets, inner dimension are the participants.
uint256[][] balances;
SubAlloc[] locked;
}

struct SubAlloc {
// ID is the channelID of the subchannel
bytes32 ID; // solhint-disable-line var-name-mixedcase
bytes32[] ID; // solhint-disable-line var-name-mixedcase
// balances holds the total balance of the subchannel of every asset.
uint256[] balances;
// indexMap maps each sub-channel participant to a parent channel
Expand Down Expand Up @@ -126,11 +127,38 @@ library Channel {
SubAlloc memory a,
SubAlloc memory b
) internal pure {
require(a.ID == b.ID, "SubAlloc: unequal ID");
require(AreBytes32ArraysEqual(a.ID, b.ID), "SubAlloc: unequal ID");
Array.requireEqualUint256Array(a.balances, b.balances);
Array.requireEqualUint16Array(a.indexMap, b.indexMap);
}

// @dev AreBytes32ArraysEqual checks if two arrays of bytes32 are equal.
function AreBytes32ArraysEqual(bytes32[] memory arr1, bytes32[] memory arr2) internal pure returns (bool) {

Check warning on line 136 in contracts/Channel.sol

View workflow job for this annotation

GitHub Actions / Build & Test

Function name must be in mixedCase
if (arr1.length != arr2.length) {
return false;
}
for (uint i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}

/**
* @dev FindBackendIndex finds out which of the channelIDs corresponds to the ethereum encoded channelID.
* Reverts if none of the channelIDs has a zero backend.
* Optimized for the common case of only one zero backend.
*/
function FindBackendIndex(bytes32[] memory channel, uint256[] memory backends) internal pure returns (uint64) {

Check warning on line 153 in contracts/Channel.sol

View workflow job for this annotation

GitHub Actions / Build & Test

Function name must be in mixedCase
require(channel.length == backends.length, "Array lengths mismatch");

if (backends[0] == 1) return 0;
if (backends.length > 1 && backends[1] == 1) return 1;

return 0;
}

/// @dev Asserts that a and b are equal.
function requireEqualAsset(Asset memory a, Asset memory b) internal pure {
require(a.chainID == b.chainID, "Asset: unequal chainID");
Expand Down
10 changes: 9 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ import "@nomicfoundation/hardhat-verify";
import "hardhat-gas-reporter";
import "solidity-coverage";
const config: HardhatUserConfig = {
solidity: "0.8.24",
solidity: {
version: "0.8.24",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
};

export default config;
Loading

0 comments on commit afba2e1

Please sign in to comment.