Skip to content

Instantly share code, notes, and snippets.

@snario
Created June 17, 2019 12:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snario/03e42a1f0abc0aebd9944dc52dceb90f to your computer and use it in GitHub Desktop.
Save snario/03e42a1f0abc0aebd9944dc52dceb90f to your computer and use it in GitHub Desktop.
pragma solidity ^0.5.2;
pragma experimental ABIEncoderV2;
import "fmg-core/contracts/Commitment.sol";
import "fmg-core/contracts/Rules.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
/**
* Liam's comments:
*
* 1. Outcome could be arbitrary bytes for an interpreter to interpret
* 2. appAttributes should be stored as hash of the bytes, not the bytes
* 3. Removed the following:
address[] destination;
uint256[] allocation;
* 4. Contract inheritance is more efficient than libraries
* 5. bytes signatures is more efficient than struct
*/
contract ModdedNitroAdjudicator is ForceMoveGameLogic {
struct ChannelState {
address appDefinition;
uint32 nonce;
address[] participants;
uint8 commitmentType;
uint32 turnNum;
uint32 commitmentCount;
bytes appAttributes;
}
using Commitment for ChannelState;
using SafeMath for uint;
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
struct ConclusionProof {
ChannelState penultimateCommitment;
Signature penultimateSignature;
ChannelState ultimateCommitment;
Signature ultimateSignature;
}
// Liam Modification:
// - MaybeOutcome is not just (address, uint256) pair, it is bytes
// - Will be interpreted by Interpretrer later
// - Named it MaybeOutcome because it may or may not be finalized
struct MaybeOutcome {
uint256 finalizedAt;
bytes outcome;
ChannelState challengeCommitment;
}
mapping(address => MaybeOutcome) public outcomes;
event ChallengeCreated(
address channelId,
ChannelState commitment,
uint256 finalizedAt
);
event Concluded(
address channelId
);
event Refuted(
address channelId,
ChannelState refutation
);
event RespondedWithMove(
address channelId,
ChannelState response,
uint8 v,
bytes32 r,
bytes32 ss
);
event RespondedWithAlternativeMove(
ChannelState alternativeResponse
);
function conclude(ConclusionProof memory proof) public {
_conclude(proof);
}
function forceMove(
ChannelState memory agreedCommitment,
ChannelState memory challengeCommitment,
address guaranteedChannel,
Signature[] memory signatures
) public {
require(
!isChannelClosed(channelId(agreedCommitment)),
"ForceMove: channel must be open"
);
require(
moveAuthorized(agreedCommitment, signatures[0]),
"ForceMove: agreedCommitment not authorized"
);
require(
moveAuthorized(challengeCommitment, signatures[1]),
"ForceMove: challengeCommitment not authorized"
);
require(
Rules.validTransition(agreedCommitment, challengeCommitment)
);
// if (guaranteedChannel == zeroAddress) {
// // If the guaranteeChannel is the zeroAddress, this outcome
// // is an allocation
// require(
// agreedCommitment.allocation.length > 0,
// "ForceMove: allocation outcome must have resolution"
// );
// } else {
// // The non-zeroness of guaranteeChannel indicates that this outcome
// // is a guarantee
// require(
// challengeCommitment.allocation.length == 0,
// "ForceMove: guarantee outcome cannot have allocation"
// );
// }
address channelId = channelId(agreedCommitment);
// Liam Modification:
// - Computes the outcome based on the challenge state and the computeOutcome function
bytes outcome = CounterfactualApp(agreedCommitment.appDefinition)
.computeOutcome(challengeCommitment.appAttributes);
outcomes[channelId] = MaybeOutcome(
block.number + CHALLENGE_DURATION,
outcome,
challengeCommitment
);
emit ChallengeCreated(
channelId,
challengeCommitment,
block.number + CHALLENGE_DURATION
);
}
uint t = block.number;
function refute(
ChannelState memory refutationCommitment,
Signature memory signature
)
public
{
address channel = channelId(refutationCommitment);
require(
!isChannelClosed(channel),
"Refute: channel must be open"
);
require(
moveAuthorized(refutationCommitment, signature),
"Refute: move must be authorized"
);
require(
Rules.validRefute(
outcomes[channel].challengeCommitment,
refutationCommitment,
signature.v,
signature.r,
signature.s
),
"Refute: must be a valid refute"
);
emit Refuted(channel, refutationCommitment);
// Liam Modification:
// - Computes the outcome based on the challenge state and the computeOutcome function
bytes outcome = CounterfactualApp(agreedCommitment.appDefinition)
.computeOutcome(refutationCommitment.appAttributes);
MaybeOutcome memory updatedOutcome = MaybeOutcome(
0,
outcome,
refutationCommitment
);
outcomes[channel] = updatedOutcome;
}
function respondWithMove(
ChannelState memory responseCommitment,
Signature memory signature
)
public
{
address channel = channelId(responseCommitment);
require(
!isChannelClosed(channel),
"RespondWithMove: channel must be open"
);
require(
moveAuthorized(responseCommitment, signature),
"RespondWithMove: move must be authorized"
);
require(
Rules.validRespondWithMove(
outcomes[channel].challengeCommitment,
responseCommitment,
signature.v,
signature.r,
signature.s
),
"RespondWithMove: must be a valid response"
);
emit RespondedWithMove(
channel,
responseCommitment,
signature.v,
signature.r,
signature.s
);
// Liam Modification:
// - Computes the outcome based on the challenge state and the computeOutcome function
bytes outcome = CounterfactualApp(
outcomes[channel].challengeCommitment.appDefinition
).computeOutcome(responseCommitment.appAttributes);
MaybeOutcome memory updatedOutcome = MaybeOutcome(
0,
outcome,
responseCommitment
);
outcomes[channel] = updatedOutcome;
}
function alternativeRespondWithMove(
ChannelState memory _alternativeCommitment,
ChannelState memory _responseCommitment,
Signature memory _alternativeSignature,
Signature memory _responseSignature
)
public
{
address channel = channelId(_responseCommitment);
require(
!isChannelClosed(channel),
"AlternativeRespondWithMove: channel must be open"
);
require(
moveAuthorized(_responseCommitment, _responseSignature),
"AlternativeRespondWithMove: move must be authorized"
);
uint8[] memory v = new uint8[](2);
v[0] = _alternativeSignature.v;
v[1] = _responseSignature.v;
bytes32[] memory r = new bytes32[](2);
r[0] = _alternativeSignature.r;
r[1] = _responseSignature.r;
bytes32[] memory s = new bytes32[](2);
s[0] = _alternativeSignature.s;
s[1] = _responseSignature.s;
require(
Rules.validAlternativeRespondWithMove(
outcomes[channel].challengeCommitment,
_alternativeCommitment,
_responseCommitment,
v,
r,
s
),
"RespondWithMove: must be a valid response"
);
emit RespondedWithAlternativeMove(_responseCommitment);
// Liam Modification:
// - Computes the outcome based on the challenge state and the computeOutcome function
bytes outcome = CounterfactualApp(_alternativeCommitment.appDefinition)
.computeOutcome(_responseCommitment.appAttributes);
MaybeOutcome memory updatedOutcome = MaybeOutcome(
0,
outcome,
_responseCommitment
);
outcomes[channel] = updatedOutcome;
}
// ************************
// ForceMove Protocol Logic
// ************************
function _conclude(ConclusionProof memory proof) internal {
address channelId = proof.channelId(penultimateCommitment);
require(
(
outcomes[channelId].finalizedAt > block.number ||
outcomes[channelId].finalizedAt == 0
),
"Conclude: channel must not be finalized"
);
// Liam Modification:
// - Computes the outcome based on the challenge state and the computeOutcome function
bytes outcome = CounterfactualApp(proof.penultimateCommitment.appDefinition)
.computeOutcome(proof.penultimateCommitment.appAttributes);
outcomes[channelId] = MaybeOutcome(
block.number,
outcome,
proof.penultimateCommitment
);
emit Concluded(channelId);
}
// ****************
// Helper functions
// ****************
function isChannelClosed(address channel) internal view returns (bool) {
return (
outcomes[channel].finalizedAt < block.number &&
outcomes[channel].finalizedAt > 0
);
}
function moveAuthorized(
ChannelState memory _commitment,
Signature memory signature
)
internal
pure
returns (bool)
{
return turnTakerFromCommitment(_commitment) == recoverSigner(
abi.encode(_commitment),
signature.v,
signature.r,
signature.s
);
}
function recoverSigner(
bytes memory _d,
uint8 _v,
bytes32 _r,
bytes32 _s
)
internal
pure
returns(address)
{
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 h = keccak256(_d);
bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, h));
address a = ecrecover(prefixedHash, _v, _r, _s);
return(a);
}
function channelIdFromCommitment(ChannelState c)
internal
pure
returns (address)
{
bytes32 h = keccak256(
abi.encodePacked(c.appDefinition, c.nonce, c.participants)
);
address addr;
assembly {
mstore(0, h)
addr := mload(0)
}
return addr;
}
function turnTakerFromCommitment(ChannelState memory c)
internal
pure
returns (address)
{
return c.participants[
c.turnNum % c.participants.length
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment