Skip to content
This repository was archived by the owner on Oct 14, 2020. It is now read-only.

Commit a907aab

Browse files
authored
Merge pull request #516 from jhoffner/8-17/ethereum
Ethereum JS
2 parents 8075db6 + ba95fc3 commit a907aab

File tree

13 files changed

+991
-18
lines changed

13 files changed

+991
-18
lines changed

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ services:
1010
volumes:
1111
- ./lib:/runner/lib
1212
- ./examples:/runner/examples
13-
- ./frameworks:/runner/frameworks
13+
- ./frameworks/javascript:/runner/frameworks/javascript
1414
- ./test:/runner/test
1515
- ./tsconfig.json:/runner/tsconfig.json
1616

docker/node.docker

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ RUN npm install -gq "typings@^2"
2525
# Install TypeScript
2626
RUN npm -g install "typescript@^2.4"
2727

28+
# Install test ethereum client so that we can test basic network actions
29+
RUN npm -g install "ethereumjs-testrpc@4.0.1"
30+
2831
RUN set -ex; \
2932
mkdir -p /runner/frameworks/javascript/angular; \
3033
cd /runner/frameworks/javascript/angular; \
@@ -105,7 +108,6 @@ RUN set -ex; \
105108
https://code.angularjs.org/1.6.5/angular.js; \
106109
chmod -R a+r .;
107110

108-
109111
RUN ln -s /home/codewarrior /workspace
110112
ENV NPM_CONFIG_LOGLEVEL warn
111113

@@ -127,12 +129,19 @@ COPY test/runner.js test/
127129
COPY test/runners/javascript/ test/runners/javascript/
128130
COPY test/runners/coffeescript_spec.js test/runners/
129131
COPY test/runners/typescript_spec.js test/runners/
130-
COPY frameworks/javascript/*.js frameworks/javascript/
132+
COPY frameworks/javascript/ frameworks/javascript/
131133

132134
# add display as if it is a node module, so that it can easily be included via require("display")
133135
COPY frameworks/javascript/display.js /runner/node_modules/display/index.js
134136
COPY frameworks/javascript/chai-display.js /runner/node_modules/chai-display/index.js
135137
COPY frameworks/javascript/display.js /runner/node_modules/chai-display/display.js
138+
COPY frameworks/ethereum/contracts frameworks/ethereum/contracts
139+
140+
# precompile some example contracts to use with ethereum based challenges
141+
RUN node frameworks/javascript/ethereum/compile-contract BlindAuction
142+
RUN node frameworks/javascript/ethereum/compile-contract Purchase
143+
RUN node frameworks/javascript/ethereum/compile-contract Ballot
144+
RUN node frameworks/javascript/ethereum/compile-contract SimpleAuction
136145

137146
# Add entire typings/ for now for backward compatibility
138147
COPY typings /runner/typings
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
pragma solidity ^0.4.11;
2+
3+
/// @title Voting with delegation.
4+
contract Ballot {
5+
// This declares a new complex type which will
6+
// be used for variables later.
7+
// It will represent a single voter.
8+
struct Voter {
9+
uint weight; // weight is accumulated by delegation
10+
bool voted; // if true, that person already voted
11+
address delegate; // person delegated to
12+
uint vote; // index of the voted proposal
13+
}
14+
15+
// This is a type for a single proposal.
16+
struct Proposal {
17+
bytes32 name; // short name (up to 32 bytes)
18+
uint voteCount; // number of accumulated votes
19+
}
20+
21+
address public chairperson;
22+
23+
// This declares a state variable that
24+
// stores a `Voter` struct for each possible address.
25+
mapping(address => Voter) public voters;
26+
27+
// A dynamically-sized array of `Proposal` structs.
28+
Proposal[] public proposals;
29+
30+
/// Create a new ballot to choose one of `proposalNames`.
31+
function Ballot(bytes32[] proposalNames) {
32+
chairperson = msg.sender;
33+
voters[chairperson].weight = 1;
34+
35+
// For each of the provided proposal names,
36+
// create a new proposal object and add it
37+
// to the end of the array.
38+
for (uint i = 0; i < proposalNames.length; i++) {
39+
// `Proposal({...})` creates a temporary
40+
// Proposal object and `proposals.push(...)`
41+
// appends it to the end of `proposals`.
42+
proposals.push(Proposal({
43+
name: proposalNames[i],
44+
voteCount: 0
45+
}));
46+
}
47+
}
48+
49+
// Give `voter` the right to vote on this ballot.
50+
// May only be called by `chairperson`.
51+
function giveRightToVote(address voter) {
52+
// If the argument of `require` evaluates to `false`,
53+
// it terminates and reverts all changes to
54+
// the state and to Ether balances. It is often
55+
// a good idea to use this if functions are
56+
// called incorrectly. But watch out, this
57+
// will currently also consume all provided gas
58+
// (this is planned to change in the future).
59+
require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0));
60+
voters[voter].weight = 1;
61+
}
62+
63+
/// Delegate your vote to the voter `to`.
64+
function delegate(address to) {
65+
// assigns reference
66+
Voter storage sender = voters[msg.sender];
67+
require(!sender.voted);
68+
69+
// Self-delegation is not allowed.
70+
require(to != msg.sender);
71+
72+
// Forward the delegation as long as
73+
// `to` also delegated.
74+
// In general, such loops are very dangerous,
75+
// because if they run too long, they might
76+
// need more gas than is available in a block.
77+
// In this case, the delegation will not be executed,
78+
// but in other situations, such loops might
79+
// cause a contract to get "stuck" completely.
80+
while (voters[to].delegate != address(0)) {
81+
to = voters[to].delegate;
82+
83+
// We found a loop in the delegation, not allowed.
84+
require(to != msg.sender);
85+
}
86+
87+
// Since `sender` is a reference, this
88+
// modifies `voters[msg.sender].voted`
89+
sender.voted = true;
90+
sender.delegate = to;
91+
Voter storage delegate = voters[to];
92+
if (delegate.voted) {
93+
// If the delegate already voted,
94+
// directly add to the number of votes
95+
proposals[delegate.vote].voteCount += sender.weight;
96+
} else {
97+
// If the delegate did not vote yet,
98+
// add to her weight.
99+
delegate.weight += sender.weight;
100+
}
101+
}
102+
103+
/// Give your vote (including votes delegated to you)
104+
/// to proposal `proposals[proposal].name`.
105+
function vote(uint proposal) {
106+
Voter storage sender = voters[msg.sender];
107+
require(!sender.voted);
108+
sender.voted = true;
109+
sender.vote = proposal;
110+
111+
// If `proposal` is out of the range of the array,
112+
// this will throw automatically and revert all
113+
// changes.
114+
proposals[proposal].voteCount += sender.weight;
115+
}
116+
117+
/// @dev Computes the winning proposal taking all
118+
/// previous votes into account.
119+
function winningProposal() constant
120+
returns (uint winningProposal)
121+
{
122+
uint winningVoteCount = 0;
123+
for (uint p = 0; p < proposals.length; p++) {
124+
if (proposals[p].voteCount > winningVoteCount) {
125+
winningVoteCount = proposals[p].voteCount;
126+
winningProposal = p;
127+
}
128+
}
129+
}
130+
131+
// Calls winningProposal() function to get the index
132+
// of the winner contained in the proposals array and then
133+
// returns the name of the winner
134+
function winnerName() constant
135+
returns (bytes32 winnerName)
136+
{
137+
winnerName = proposals[winningProposal()].name;
138+
}
139+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
pragma solidity ^0.4.11;
2+
3+
contract BlindAuction {
4+
struct Bid {
5+
bytes32 blindedBid;
6+
uint deposit;
7+
}
8+
9+
address public beneficiary;
10+
uint public auctionStart;
11+
uint public biddingEnd;
12+
uint public revealEnd;
13+
bool public ended;
14+
15+
mapping(address => Bid[]) public bids;
16+
17+
address public highestBidder;
18+
uint public highestBid;
19+
20+
// Allowed withdrawals of previous bids
21+
mapping(address => uint) pendingReturns;
22+
23+
event AuctionEnded(address winner, uint highestBid);
24+
25+
/// Modifiers are a convenient way to validate inputs to
26+
/// functions. `onlyBefore` is applied to `bid` below:
27+
/// The new function body is the modifier's body where
28+
/// `_` is replaced by the old function body.
29+
modifier onlyBefore(uint _time) { require(now < _time); _; }
30+
modifier onlyAfter(uint _time) { require(now > _time); _; }
31+
32+
function BlindAuction(
33+
uint _biddingTime,
34+
uint _revealTime,
35+
address _beneficiary
36+
) {
37+
beneficiary = _beneficiary;
38+
auctionStart = now;
39+
biddingEnd = now + _biddingTime;
40+
revealEnd = biddingEnd + _revealTime;
41+
}
42+
43+
/// Place a blinded bid with `_blindedBid` = keccak256(value,
44+
/// fake, secret).
45+
/// The sent ether is only refunded if the bid is correctly
46+
/// revealed in the revealing phase. The bid is valid if the
47+
/// ether sent together with the bid is at least "value" and
48+
/// "fake" is not true. Setting "fake" to true and sending
49+
/// not the exact amount are ways to hide the real bid but
50+
/// still make the required deposit. The same address can
51+
/// place multiple bids.
52+
function bid(bytes32 _blindedBid)
53+
payable
54+
onlyBefore(biddingEnd)
55+
{
56+
bids[msg.sender].push(Bid({
57+
blindedBid: _blindedBid,
58+
deposit: msg.value
59+
}));
60+
}
61+
62+
/// Reveal your blinded bids. You will get a refund for all
63+
/// correctly blinded invalid bids and for all bids except for
64+
/// the totally highest.
65+
function reveal(
66+
uint[] _values,
67+
bool[] _fake,
68+
bytes32[] _secret
69+
)
70+
onlyAfter(biddingEnd)
71+
onlyBefore(revealEnd)
72+
{
73+
uint length = bids[msg.sender].length;
74+
require(_values.length == length);
75+
require(_fake.length == length);
76+
require(_secret.length == length);
77+
78+
uint refund;
79+
for (uint i = 0; i < length; i++) {
80+
var bid = bids[msg.sender][i];
81+
var (value, fake, secret) =
82+
(_values[i], _fake[i], _secret[i]);
83+
if (bid.blindedBid != keccak256(value, fake, secret)) {
84+
// Bid was not actually revealed.
85+
// Do not refund deposit.
86+
continue;
87+
}
88+
refund += bid.deposit;
89+
if (!fake && bid.deposit >= value) {
90+
if (placeBid(msg.sender, value))
91+
refund -= value;
92+
}
93+
// Make it impossible for the sender to re-claim
94+
// the same deposit.
95+
bid.blindedBid = bytes32(0);
96+
}
97+
msg.sender.transfer(refund);
98+
}
99+
100+
// This is an "internal" function which means that it
101+
// can only be called from the contract itself (or from
102+
// derived contracts).
103+
function placeBid(address bidder, uint value) internal
104+
returns (bool success)
105+
{
106+
if (value <= highestBid) {
107+
return false;
108+
}
109+
if (highestBidder != 0) {
110+
// Refund the previously highest bidder.
111+
pendingReturns[highestBidder] += highestBid;
112+
}
113+
highestBid = value;
114+
highestBidder = bidder;
115+
return true;
116+
}
117+
118+
/// Withdraw a bid that was overbid.
119+
function withdraw() {
120+
uint amount = pendingReturns[msg.sender];
121+
if (amount > 0) {
122+
// It is important to set this to zero because the recipient
123+
// can call this function again as part of the receiving call
124+
// before `send` returns (see the remark above about
125+
// conditions -> effects -> interaction).
126+
pendingReturns[msg.sender] = 0;
127+
128+
msg.sender.transfer(amount);
129+
}
130+
}
131+
132+
/// End the auction and send the highest bid
133+
/// to the beneficiary.
134+
function auctionEnd()
135+
onlyAfter(revealEnd)
136+
{
137+
require(!ended);
138+
AuctionEnded(highestBidder, highestBid);
139+
ended = true;
140+
// We send all the money we have, because some
141+
// of the refunds might have failed.
142+
beneficiary.transfer(this.balance);
143+
}
144+
}

0 commit comments

Comments
 (0)