Skip to content
This repository has been archived by the owner on Dec 13, 2019. It is now read-only.

Commit

Permalink
[contract] Interpreters
Browse files Browse the repository at this point in the history
  • Loading branch information
ldct committed May 10, 2019
1 parent a81a8dc commit b8bc4d2
Show file tree
Hide file tree
Showing 95 changed files with 881 additions and 1,453 deletions.
46 changes: 13 additions & 33 deletions packages/apps/contracts/HighRollerApp.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
pragma solidity 0.5.8;
pragma experimental "ABIEncoderV2";

import "@counterfactual/contracts/contracts/CounterfactualApp.sol";
import "@counterfactual/contracts/contracts/interfaces/CounterfactualApp.sol";
import "@counterfactual/contracts/contracts/interfaces/TwoPartyOutcome.sol";


/// @title High Roller App
Expand Down Expand Up @@ -33,7 +34,6 @@ contract HighRollerApp is CounterfactualApp {
}

struct AppState {
address[2] playerAddrs;
Stage stage;
bytes32 salt;
bytes32 commitHash;
Expand Down Expand Up @@ -120,59 +120,39 @@ contract HighRollerApp is CounterfactualApp {
return abi.encode(nextState);
}

function resolve(bytes calldata encodedState, Transfer.Terms calldata terms)
function resolve(bytes calldata encodedState)
external
pure
returns (Transfer.Transaction memory)
returns (bytes memory)
{
AppState memory appState = abi.decode(encodedState, (AppState));

uint256[] memory amounts = new uint256[](2);
address[] memory to = new address[](2);
to[0] = appState.playerAddrs[0];
to[1] = appState.playerAddrs[1];
bytes32 expectedCommitHash = keccak256(
abi.encodePacked(appState.salt, appState.playerFirstNumber)
);
if (expectedCommitHash == appState.commitHash) {
amounts = getWinningAmounts(
appState.playerFirstNumber, appState.playerSecondNumber, terms.limit
);
return abi.encode(getWinningAmounts(
appState.playerFirstNumber, appState.playerSecondNumber
));
} else {
amounts[0] = 0;
amounts[1] = terms.limit;
return abi.encode(TwoPartyOutcome.Resolution.SEND_TO_ADDR_TWO);
}

bytes[] memory data = new bytes[](2);

return Transfer.Transaction(
terms.assetType,
terms.token,
to,
amounts,
data
);
}

function getWinningAmounts(uint256 num1, uint256 num2, uint256 termsLimit)
function getWinningAmounts(uint256 num1, uint256 num2)
internal
pure
returns (uint256[] memory)
returns (TwoPartyOutcome.Resolution)
{
uint256[] memory amounts = new uint256[](2);
bytes32 randomSalt = calculateRandomSalt(num1, num2);
(uint8 playerFirstTotal, uint8 playerSecondTotal) = highRoller(randomSalt);
if (playerFirstTotal > playerSecondTotal) {
amounts[0] = termsLimit;
amounts[1] = 0;
return TwoPartyOutcome.Resolution.SEND_TO_ADDR_ONE;
} else if (playerFirstTotal < playerSecondTotal) {
amounts[0] = 0;
amounts[1] = termsLimit;
return TwoPartyOutcome.Resolution.SEND_TO_ADDR_TWO;
} else {
amounts[0] = termsLimit / 2;
amounts[1] = termsLimit / 2;
return TwoPartyOutcome.Resolution.SPLIT_AND_SEND_TO_BOTH_ADDRS;
}
return amounts;
}

function highRoller(bytes32 randomness)
Expand Down
35 changes: 10 additions & 25 deletions packages/apps/contracts/NimApp.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
pragma solidity 0.5.8;
pragma experimental "ABIEncoderV2";

import "@counterfactual/contracts/contracts/CounterfactualApp.sol";
import "@counterfactual/contracts/contracts/interfaces/CounterfactualApp.sol";
import "@counterfactual/contracts/contracts/interfaces/TwoPartyOutcome.sol";


/*
Expand All @@ -16,7 +17,6 @@ contract NimApp is CounterfactualApp {
}

struct AppState {
address[2] players;
uint256 turnNum;
uint256[3] pileHeights;
}
Expand Down Expand Up @@ -64,35 +64,20 @@ contract NimApp is CounterfactualApp {
return abi.encode(ret);
}

function resolve(
bytes calldata encodedState, Transfer.Terms calldata terms
)
function resolve(bytes calldata encodedState)
external
pure
returns (Transfer.Transaction memory)
returns (bytes memory)
{
AppState memory state = abi.decode(encodedState, (AppState));

require(isWin(state), "Resolution state was not in a winning position");
address loser = state.players[state.turnNum % 2];
address winner = state.players[1 - (state.turnNum % 2)];

uint256[] memory amounts = new uint256[](2);
amounts[0] = terms.limit;
amounts[1] = 0;

address[] memory to = new address[](2);
to[0] = loser;
to[1] = winner;
bytes[] memory data = new bytes[](2);

return Transfer.Transaction(
terms.assetType,
terms.token,
to,
amounts,
data
);

if (state.turnNum % 2 == 0) {
return abi.encode(TwoPartyOutcome.Resolution.SEND_TO_ADDR_ONE);
} else {
return abi.encode(TwoPartyOutcome.Resolution.SEND_TO_ADDR_TWO);
}
}

function isWin(AppState memory state)
Expand Down
59 changes: 19 additions & 40 deletions packages/apps/contracts/TicTacToeApp.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pragma solidity 0.5.8;
pragma experimental "ABIEncoderV2";

import "@counterfactual/contracts/contracts/CounterfactualApp.sol";
import "@counterfactual/contracts/contracts/interfaces/Interpreter.sol";
import "@counterfactual/contracts/contracts/interfaces/CounterfactualApp.sol";
import "@counterfactual/contracts/contracts/interfaces/TwoPartyOutcome.sol";


contract TicTacToeApp is CounterfactualApp {
Expand Down Expand Up @@ -37,7 +39,6 @@ contract TicTacToeApp is CounterfactualApp {
uint256 constant EMPTY_SQUARE = 0;

struct AppState {
address[2] players;
uint256 turnNum;
uint256 winner;
uint256[3][3] board;
Expand Down Expand Up @@ -102,50 +103,20 @@ contract TicTacToeApp is CounterfactualApp {
return abi.encode(postState);
}

function resolve(bytes calldata encodedState, Transfer.Terms calldata terms)
function resolve(bytes calldata encodedState)
external
pure
returns (Transfer.Transaction memory)
returns (bytes memory)
{
AppState memory state = abi.decode(encodedState, (AppState));
require(state.winner != 0, "Winner was set to 0; invalid");

uint256[] memory amounts = new uint256[](2);
address[] memory to = new address[](2);
bytes[] memory data = new bytes[](2);

if (state.winner == 3) {
amounts[0] = terms.limit / 2;
amounts[1] = terms.limit / 2;

to[0] = state.players[0];
to[1] = state.players[1];

return Transfer.Transaction(
terms.assetType,
terms.token,
to,
amounts,
data
);

} else {
address winner = state.players[state.winner - 1];
address loser = state.players[2 - state.winner];

amounts[0] = terms.limit;
amounts[1] = 0;

to[0] = winner;
to[1] = loser;

return Transfer.Transaction(
terms.assetType,
terms.token,
to,
amounts,
data
);
if (state.winner == 2) {
return abi.encode(TwoPartyOutcome.Resolution.SEND_TO_ADDR_TWO);
} else if (state.winner == 1) {
return abi.encode(TwoPartyOutcome.Resolution.SEND_TO_ADDR_ONE);
} else /* state.winner == 3, or fallback */ {
return abi.encode(TwoPartyOutcome.Resolution.SPLIT_AND_SEND_TO_BOTH_ADDRS);
}

}
Expand Down Expand Up @@ -223,4 +194,12 @@ contract TicTacToeApp is CounterfactualApp {
}
}

function resolveType()
external
pure
returns (uint256)
{
return uint256(Interpreter.ResolutionType.TWO_PARTY_OUTCOME);
}

}
60 changes: 9 additions & 51 deletions packages/apps/test/high-roller-app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ enum HighRollerStage {
}

type HighRollerAppState = {
playerAddrs: string[];
stage: HighRollerStage;
salt: string;
commitHash: string;
Expand All @@ -56,7 +55,6 @@ function decodeBytesToAppState(encodedAppState: string): HighRollerAppState {
return defaultAbiCoder.decode(
[
`tuple(
address[2] playerAddrs,
uint8 stage,
bytes32 salt,
bytes32 commitHash,
Expand All @@ -76,7 +74,6 @@ describe("HighRollerApp", () => {
[
`
tuple(
address[2] playerAddrs,
uint8 stage,
bytes32 salt,
bytes32 commitHash,
Expand Down Expand Up @@ -114,8 +111,12 @@ describe("HighRollerApp", () => {
);
}

async function resolve(state: SolidityABIEncoderV2Type, terms: Terms) {
return await highRollerApp.functions.resolve(encodeState(state), terms);
async function computeResolution(state: SolidityABIEncoderV2Type) {
const [decodedResult] = defaultAbiCoder.decode(
["uint256"],
await highRollerApp.functions.resolve(encodeState(state))
);
return decodedResult;
}

before(async () => {
Expand All @@ -127,7 +128,6 @@ describe("HighRollerApp", () => {
describe("applyAction", () => {
it("can start game", async () => {
const preState: HighRollerAppState = {
playerAddrs: [AddressZero, AddressZero],
stage: HighRollerStage.PRE_GAME,
salt: HashZero,
commitHash: HashZero,
Expand All @@ -148,7 +148,6 @@ describe("HighRollerApp", () => {

it("can commit to hash", async () => {
const preState: HighRollerAppState = {
playerAddrs: [AddressZero, AddressZero],
stage: HighRollerStage.COMMITTING_HASH,
salt: HashZero,
commitHash: HashZero,
Expand Down Expand Up @@ -180,7 +179,6 @@ describe("HighRollerApp", () => {
const hash = computeCommitHash(numberSalt, playerFirstNumber);

const preState: HighRollerAppState = {
playerAddrs: [AddressZero, AddressZero],
stage: HighRollerStage.COMMITTING_NUM,
salt: HashZero,
commitHash: hash,
Expand All @@ -207,7 +205,6 @@ describe("HighRollerApp", () => {
const hash = computeCommitHash(numberSalt, playerFirstNumber);

const preState: HighRollerAppState = {
playerAddrs: [AddressZero, AddressZero],
stage: HighRollerStage.REVEALING,
salt: HashZero,
commitHash: hash,
Expand Down Expand Up @@ -236,27 +233,14 @@ describe("HighRollerApp", () => {
const hash = computeCommitHash(numberSalt, playerFirstNumber);

const preState: HighRollerAppState = {
playerAddrs: [AddressZero, AddressZero],
stage: HighRollerStage.DONE,
salt: numberSalt,
commitHash: hash,
playerFirstNumber: 1,
playerSecondNumber: 2
};

const terms: Terms = {
assetType: AssetType.ETH,
limit: parseEther("2"),
token: AddressZero
};
const transaction: Transaction = await resolve(preState, terms);

expect(transaction.assetType).to.eq(AssetType.ETH);
expect(transaction.token).to.eq(AddressZero);
expect(transaction.to).to.deep.eq([AddressZero, AddressZero]);
expect(transaction.value[0]).to.eq(Zero);
expect(transaction.value[1]).to.eq(parseEther("2"));
expect(transaction.data).to.deep.eq(["0x", "0x"]);
expect(await computeResolution(preState)).to.eq(1);
});

/**
Expand All @@ -271,27 +255,14 @@ describe("HighRollerApp", () => {
const hash = computeCommitHash(numberSalt, playerFirstNumber);

const preState: HighRollerAppState = {
playerAddrs: [AddressZero, AddressZero],
stage: HighRollerStage.DONE,
salt: numberSalt,
commitHash: hash,
playerFirstNumber: 75,
playerSecondNumber: 45
};

const terms: Terms = {
assetType: AssetType.ETH,
limit: parseEther("2"),
token: AddressZero
};
const transaction: Transaction = await resolve(preState, terms);

expect(transaction.assetType).to.eq(AssetType.ETH);
expect(transaction.token).to.eq(AddressZero);
expect(transaction.to).to.deep.eq([AddressZero, AddressZero]);
expect(transaction.value[0]).to.eq(parseEther("1"));
expect(transaction.value[1]).to.eq(parseEther("1"));
expect(transaction.data).to.deep.eq(["0x", "0x"]);
expect(await computeResolution(preState)).to.eq(2);
});

it("can end game - playerFirst wins", async () => {
Expand All @@ -301,27 +272,14 @@ describe("HighRollerApp", () => {
const hash = computeCommitHash(numberSalt, playerFirstNumber);

const preState: HighRollerAppState = {
playerAddrs: [AddressZero, AddressZero],
stage: HighRollerStage.DONE,
salt: numberSalt,
commitHash: hash,
playerFirstNumber: 3,
playerSecondNumber: 2
};

const terms: Terms = {
assetType: AssetType.ETH,
limit: parseEther("2"),
token: AddressZero
};
const transaction: Transaction = await resolve(preState, terms);

expect(transaction.assetType).to.eq(AssetType.ETH);
expect(transaction.token).to.eq(AddressZero);
expect(transaction.to).to.deep.equal([AddressZero, AddressZero]);
expect(transaction.value[0]).to.eq(parseEther("2"));
expect(transaction.value[1]).to.eq(Zero);
expect(transaction.data).to.deep.equal(["0x", "0x"]);
expect(await computeResolution(preState)).to.eq(0);
});
});
});
Loading

0 comments on commit b8bc4d2

Please sign in to comment.