-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from aeternity/feat/contract
feat: add contract project
- Loading branch information
Showing
15 changed files
with
7,763 additions
and
1 deletion.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
docker-compose.override.yml | ||
node_modules/ | ||
.env.production | ||
.env.production | ||
Makefile |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
module.exports = { | ||
env: { | ||
browser : true, | ||
commonjs : true, | ||
es2021 : true, | ||
mocha : true, | ||
}, | ||
extends: [ | ||
'eslint:recommended', | ||
], | ||
parser : '@typescript-eslint/parser', | ||
parserOptions : { | ||
ecmaVersion: 'latest', | ||
}, | ||
plugins: [ | ||
'@typescript-eslint', | ||
'mocha', | ||
], | ||
rules: { | ||
'no-console' : 'off', | ||
'comma-dangle' : 'off', | ||
indent : [ 'warn', 4, { | ||
SwitchCase : 1, | ||
ignoreComments : true, | ||
} ], | ||
|
||
'object-curly-spacing' : [ 'warn', 'always' ], | ||
'object-curly-newline' : [ 'warn', { consistent: true, multiline: true } ], | ||
'key-spacing' : [ 'warn', { | ||
align: { | ||
beforeColon : true, | ||
afterColon : true, | ||
on : 'colon', | ||
}, | ||
} ], | ||
|
||
'space-before-blocks' : [ 'warn' ], | ||
'keyword-spacing' : [ 'warn' ], | ||
'no-multiple-empty-lines' : [ 'warn', { max: 1, maxEOF: 1 } ], | ||
semi : [ 'warn', 'never' ], | ||
'no-unused-vars' : [ 'warn', { ignoreRestSiblings: true } ], | ||
'arrow-spacing' : [ 'warn', { before: true, after: true } ], | ||
'comma-spacing' : [ 'warn', { before: false, after: true } ], | ||
'space-infix-ops' : [ 'warn' ], | ||
'space-in-parens' : [ 'warn', 'always' ], | ||
'array-bracket-spacing' : [ 'warn', 'always' ], | ||
'prefer-const' : 'warn', | ||
'require-await' : 'error' | ||
}, | ||
"globals": { | ||
"__dirname": true, | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Makefile | ||
node_modules | ||
build/ |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
|
||
## Description | ||
|
||
[RockPaperScissors](https://github.com/aeternity/state-channel-demo/tree/develop/contract) | ||
|
||
## Installation | ||
|
||
```bash | ||
$ npm install | ||
``` | ||
|
||
## Container | ||
``` | ||
# to deploy | ||
npx aeproject env init | ||
# to stop container | ||
npx aeproject env --stop | ||
``` | ||
|
||
## Running the tests | ||
|
||
```bash | ||
# tests | ||
$ npm run test | ||
|
||
# tets bailing at first fail | ||
$ npm run test:bail | ||
|
||
# debug mode | ||
$ npm run test:debug | ||
|
||
``` | ||
|
||
## Deploy | ||
|
||
```bash | ||
# deploy on local node | ||
$ npm run deploy | ||
|
||
# TODO: work in progress | ||
# deploy on testnet | ||
$ npm run deploy:testnet | ||
|
||
# TODO: work in progress | ||
# deploy on testnet | ||
$ npm run deploy:mainnet | ||
``` | ||
|
||
## Using it in JS projects | ||
|
||
```bash | ||
import * as RockPaperScissors from '@aeternity/rock-paper-scissors' | ||
|
||
# or | ||
|
||
const RockPaperScissors = require('@aeternity/rock-paper-scissors') | ||
|
||
``` |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
include "String.aes" | ||
include "Option.aes" | ||
|
||
payable contract RockPaperScissors = | ||
datatype move = Paper | Rock | Scissors | ||
|
||
record state = { player0 : address | ||
, player1 : address | ||
, hash : option(hash) | ||
, last_move_timestamp : int | ||
, player1_move : option(move) | ||
, stake : int | ||
, reaction_time : int | ||
, debug_timestamp : option(int) | ||
} | ||
datatype event | ||
= Init (address /*player0*/ , address /*player1*/ , int /*reaction_time*/ ) | ||
| Player0Won (address /*player0*/ , int /*amount won*/ ) | ||
| Player1Won (address /*player1*/ , int /*amount won*/ ) | ||
| Draw (int /*player0 won*/ , int /*player1 won*/ , string /*player0|player1*/ ) | ||
| Player0ProvidedHash (hash /*hash*/ , int /*stake*/ ) | ||
| Player0Revealed (string /*move*/ ) | ||
| Player1Moved (string /*move*/ , int /*state*/ ) | ||
| Player0WonDispute (address /*player0*/ , int /*stake*/ ) | ||
| Player1WonDispute (address /*player1*/ , int /*stake*/ ) | ||
|
||
entrypoint get_state() = state | ||
|
||
entrypoint init(player0: address, player1: address, reaction_time: int, debug_timestamp: option(int)) : state = | ||
require(player0 != player1,"use_different_addresses") | ||
require(Call.value == 0, "no_deposit") | ||
Chain.event(Init(player0, player1, reaction_time)) | ||
{ player0 = player0 | ||
, player1 = player1 | ||
, hash = None | ||
, last_move_timestamp = 0 | ||
, player1_move = None | ||
, stake = 0 | ||
, reaction_time = reaction_time | ||
, debug_timestamp = debug_timestamp | ||
} | ||
|
||
payable stateful entrypoint provide_hash(hash: hash): unit = | ||
require_player0() | ||
require(state.hash == None, "already_has_hash") | ||
require(Call.value > 0, "no_stake") | ||
put(state { hash = Some(hash) | ||
, stake = Call.value | ||
, last_move_timestamp = get_timestamp() | ||
}) | ||
Chain.event(Player0ProvidedHash(hash,Call.value)) | ||
|
||
payable stateful entrypoint player1_move(move_str: string): unit = | ||
require_player1() | ||
ensure_player1_turn_to_move() | ||
let move = str_to_move(move_str) | ||
require(Call.value == state.stake, | ||
String.concat("wrong_stake, expected ", Int.to_str(state.stake))) | ||
put(state { player1_move = Some(move) | ||
, last_move_timestamp = get_timestamp() | ||
}) | ||
Chain.event(Player1Moved(move_str,Call.value)) | ||
|
||
stateful entrypoint reveal(key: string, move_str: string): option(address) = | ||
require_player0() | ||
ensure_player0_turn_to_reveal() | ||
let player0_move = str_to_move(move_str) | ||
ensure_if_key_is_valid(key, move_str) | ||
let Some(player1_move) = state.player1_move | ||
let stake = state.stake | ||
Chain.event(Player0Revealed(move_str)) | ||
reset_state() | ||
let (player0, player1) = (state.player0, state.player1) | ||
switch(get_winner(player0_move, player1_move)) | ||
None => | ||
let player0_refund = Contract.balance / 2 | ||
Chain.spend(player0, player0_refund) | ||
let player1_refund = Contract.balance | ||
Chain.spend(player1, player1_refund) // send the rest of it | ||
Chain.event(Draw(player0_refund, player1_refund, | ||
String.concat( | ||
Address.to_str(player0), | ||
String.concat("|",Address.to_str(player1)) | ||
) | ||
)) | ||
None | ||
Some(true) => | ||
Chain.event(Player0Won(player0,Contract.balance)) | ||
Chain.spend(player0, Contract.balance) | ||
Some(player0) | ||
Some(false) => | ||
Chain.event(Player1Won(player1,Contract.balance)) | ||
Chain.spend(player1, Contract.balance) | ||
Some(player1) | ||
|
||
stateful entrypoint player1_dispute_no_reveal(): unit = | ||
require_player1() | ||
ensure_player0_turn_to_reveal() | ||
ensure_reaction_time() | ||
Chain.event(Player1WonDispute(state.player1,Contract.balance)) | ||
Chain.spend(state.player1, Contract.balance) | ||
reset_state() | ||
|
||
stateful entrypoint player0_dispute_no_move(): unit = | ||
require_player0() | ||
ensure_player1_turn_to_move() | ||
ensure_reaction_time() | ||
Chain.event(Player0WonDispute(state.player0,Contract.balance)) | ||
Chain.spend(state.player0, Contract.balance) | ||
reset_state() | ||
|
||
entrypoint compute_hash(key: string, move: string) : hash = | ||
String.sha256(String.concat(key, move)) | ||
|
||
function ensure_reaction_time() = | ||
require(state.last_move_timestamp + state.reaction_time < get_timestamp(), "not_yet_allowed") | ||
|
||
//for debug / test purposes | ||
stateful entrypoint set_timestamp(timestamp: int): unit = | ||
require(state.debug_timestamp != None,"not_debug_mode") | ||
require(timestamp >= 0,"not_positive") | ||
put(state { debug_timestamp = Some(timestamp) }) | ||
|
||
// internal functions | ||
function get_timestamp() = Option.default(Chain.timestamp, state.debug_timestamp) | ||
|
||
function ensure_player1_turn_to_move() = | ||
require(state.hash != None, "no_hash") | ||
require(state.player1_move == None, "there_is_a_move_already") | ||
|
||
function ensure_player0_turn_to_reveal() = | ||
require(state.player1_move != None, "there_is_no_move") | ||
|
||
function require_player1() = | ||
require(Call.caller == state.player1, "not_player1") | ||
|
||
function require_player0() = | ||
require(Call.caller == state.player0, "not_player0") | ||
|
||
function ensure_if_key_is_valid(key: string, move: string) = | ||
let computed_hash = compute_hash(key, move) | ||
let Some(stored_hash) = state.hash | ||
require(stored_hash == computed_hash, "invalid_key_and_answer") | ||
|
||
function | ||
move_to_str:(move) => string | ||
move_to_str (Rock) = "rock" | ||
move_to_str (Paper) = "paper" | ||
move_to_str (Scissors) = "scissors" | ||
|
||
function | ||
str_to_move:(string) => move | ||
str_to_move ("rock") = Rock | ||
str_to_move ("paper") = Paper | ||
str_to_move ("scissors") = Scissors | ||
str_to_move (_) = abort("invalid_move") | ||
|
||
function | ||
get_winner:(move , move) => option(bool) | ||
get_winner (a , b) | a == b = None | ||
get_winner (Rock , Scissors) = Some(true) | ||
get_winner (Scissors , Paper) = Some(true) | ||
get_winner (Paper , Rock) = Some(true) | ||
get_winner (_ , _) = Some(false) | ||
|
||
stateful function reset_state() = | ||
put(state { hash = None | ||
, last_move_timestamp = 0 | ||
, player1_move = None | ||
, stake = 0 | ||
}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
version: '3.6' | ||
services: | ||
|
||
aeproject_node: | ||
image: aeternity/aeternity:${NODE_TAG}-bundle | ||
hostname: node | ||
environment: | ||
AETERNITY_CONFIG: /home/aeternity/aeternity.yaml | ||
volumes: | ||
- './docker/aeternity.yaml:/home/aeternity/aeternity.yaml' | ||
- './docker/accounts.json:/home/aeternity/node/data/aecore/.genesis/accounts_test.json' | ||
|
||
aeproject_compiler: | ||
image: aeternity/aesophia_http:${COMPILER_TAG} | ||
hostname: compiler | ||
ports: | ||
- '3080:3080' | ||
|
||
aeproject_proxy: | ||
image: nginx:latest | ||
hostname: proxy | ||
ports: | ||
- '3001:3001' | ||
volumes: | ||
- './docker/nginx.conf:/etc/nginx/conf.d/default.conf' |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"ak_fUq2NesPXcYZ1CcqBcGC3StpdnQw3iVxMA3YSeCNAwfN4myQk": 100000000000000000000000000000000, | ||
"ak_tWZrf8ehmY7CyB1JAoBmWJEeThwWnDpU4NadUdzxVSbzDgKjP": 100000000000000000000000000000000, | ||
"ak_FHZrEbRmanKUe9ECPXVNTLLpRP2SeQCLCT6Vnvs9JuVu78J7V": 100000000000000000000000000000000, | ||
"ak_RYkcTuYcyxQ6fWZsL2G3Kj3K5WCRUEXsi76bPUNkEsoHc52Wp": 100000000000000000000000000000000, | ||
"ak_2VvB4fFu7BQHaSuW5EkQ7GCaM5qiA5BsFUHjJ7dYpAaBoeFCZi": 100000000000000000000000000000000, | ||
"ak_286tvbfP6xe4GY9sEbuN2ftx1LpavQwFVcPor9H4GxBtq5fXws": 100000000000000000000000000000000, | ||
"ak_f9bmi44rdvUGKDsTLp3vMCMLMvvqsMQVWyc3XDAYECmCXEbzy": 100000000000000000000000000000000, | ||
"ak_23p6pT7bajYMJRbnJ5BsbFUuYGX2PBoZAiiYcsrRHZ1BUY2zSF": 100000000000000000000000000000000, | ||
"ak_gLYH5tAexTCvvQA6NpXksrkPJKCkLnB9MTDFTVCBuHNDJ3uZv": 100000000000000000000000000000000, | ||
"ak_zPoY7cSHy2wBKFsdWJGXM7LnSjVt6cn1TWBDdRBUMC7Tur2NQ": 100000000000000000000000000000000, | ||
"ak_RdoCvwe7kxPu2VBv2gQAc1V81sGyTTuxFv36AcvNQYZN7qgut": 0 | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
http: | ||
external: | ||
gas_limit: 60000000 | ||
internal: | ||
debug_endpoints: true | ||
listen_address: 0.0.0.0 | ||
endpoints: | ||
dry-run: true | ||
|
||
system: | ||
plugin_path: /home/aeternity/node/plugins | ||
plugins: | ||
- name: aeplugin_dev_mode | ||
config: | ||
keyblock_interval: 0 | ||
microblock_interval: 0 | ||
auto_emit_microblocks: true | ||
|
||
fork_management: | ||
network_id: ae_dev | ||
|
||
chain: | ||
persist: true | ||
consensus: | ||
"0": | ||
name: "on_demand" | ||
|
||
mining: | ||
beneficiary: "ak_RdoCvwe7kxPu2VBv2gQAc1V81sGyTTuxFv36AcvNQYZN7qgut" | ||
beneficiary_reward_delay: 2 | ||
strictly_follow_top: true | ||
|
||
websocket: | ||
channel: | ||
port: 3014 | ||
listen_address: 0.0.0.0 | ||
|
||
logging: | ||
# Controls the overload protection in the logs. | ||
hwm: 50 | ||
level: debug |
Oops, something went wrong.