Created
June 17, 2019 12:24
-
-
Save snario/03e42a1f0abc0aebd9944dc52dceb90f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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