Commit 58111850 authored by didi's avatar didi
Browse files

added frontend for multi-account testing

parent f4610ec0
......@@ -6,7 +6,8 @@
set -e
set -u
truffle migrate
# --reset makes sure a new instance of the contract is deployed
truffle migrate --reset
node apply_contract_update.js build/contracts/Streem.json ../frontend/js/streem_contract.js
echo "update applied"
// TODO: use the SafeMath lib of openzeppelin (implicit overflow checks etc)?
pragma solidity ^0.4.13;
pragma solidity ^0.4.11;
// Status of this contract: PoC of a basic ERC-20 token with streaming functionality (1 outgoing/incoming stream per account)
// TODO: decide on vocabularity for start/open, stop/close, dry/run out of funds/underwater (distinguish between low and no current funding)
......@@ -46,20 +46,14 @@ contract Streem {
streams.push(Stream(0,0,0,0)); // empty first element for implicit null-like semantics
}
// TODO: this is just for the test token. Issuance mechanism for mainnet token to be decided.
function issueTo(address receiver, uint256 amount) {
require(msg.sender == owner);
settledBalances[receiver] += amount;
totalSupply += amount;
}
/*
* opens a stream from the transaction sender to the given receiver with the given speed.
* Will succeed only if the sender has no stream open and if it has funds for at least one second
* Will succeed only if the sender has no stream open and isn't underfunded
*/
function openStream(address receiver, uint256 perSecond) {
assert(! exists(getOutStreamOf(msg.sender)));
assert(balanceOf(msg.sender) > perSecond);
//assert(balanceOf(msg.sender) > perSecond);
assert(balanceOf(msg.sender) >= 0); //TODO: what initial requirement makes most sense?
// now is an alias to block.timestamp. See http://solidity.readthedocs.io/en/develop/units-and-global-variables.html?highlight=blocknumber
var streamId = streams.push(Stream(msg.sender, receiver, perSecond, now)) - 1; // id = array_length - 1
outStreamPtrs[msg.sender] = streamId;
......@@ -204,4 +198,49 @@ contract Streem {
}
// TODO: implement the empty function?
// ####################### dev / testing helpers #############################
// TODO: this is just for the test token. Issuance mechanism for mainnet token to be decided.
function dev_issueTo(address receiver, uint256 amount) {
require(msg.sender == owner);
settledBalances[receiver] += amount;
totalSupply += amount;
}
// TODO: this is just for testing purposes
// reset to initial state. Needs to be called by every address involved so far
function dev_reset() {
settledBalances[msg.sender] = 0;
outStreamPtrs[msg.sender] = 0;
inStreamPtrs[msg.sender] = 0;
if(msg.sender == owner) {
settledBalances[msg.sender] = totalSupply;
delete streams;
streams.push(Stream(0,0,0,0));
}
}
function dev_streamsLength() constant returns (uint) {
return streams.length;
}
function dev_settledBalance() constant returns (int) {
return int(settledBalances[msg.sender]);
}
// returns sender, speed and age (in seconds)
function dev_inStream() constant returns (address, uint, uint) {
var s = getInStreamOf(msg.sender);
return (s.sender, s.perSecond, now-s.startTimestamp);
}
// returns receiver, speed and age (in seconds)
function dev_outStream() constant returns (address, uint, uint) {
var s = getOutStreamOf(msg.sender);
return (s.receiver, s.perSecond, now - s.startTimestamp);
}
}
......@@ -63,7 +63,6 @@ function showBalance(address, updateUi) {
window.balance = ret
const bal = web3.toDecimal(ret)
console.log(`balance is ${bal}`)
//resultElem.innerHTML = bal
updateUi(bal)
})
}
......
This diff is collapsed.
// for contract metadata (format as produced by truffle)
function initContract() {
const abi = contract.abi;
// get address - in case of multiple network entries, the last seems to be a safe bet
for(let networkId in contract.networks) {
//console.log(contract.networks[networkId])
var address = contract.networks[networkId].address
}
var Web3 = require('web3');
if (typeof web3 !== 'undefined') {
// Mist / Metamask
console.log('web3 connects to provider')
web3 = new Web3(web3.currentProvider);
} else {
// standalone
//alert("This Dapp needs web3 injected (e.g. through the Metamask plugin.");
console.log('web3 connects to rpc')
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
// TODO: use the async getAccounts() instead
web3.eth.defaultAccount = web3.eth.accounts[0]
}
// contract is the contract template based on our abi
const Streem = web3.eth.contract(abi);
streem = Streem.at(address);
console.log(`contract loaded at ${address} for defaultAccount ${web3.eth.defaultAccount}`)
web3.eth.getBlockNumber((err, ret) => {
console.log("Block: " + ret);
});
// callback on block advance
web3.eth.filter('latest').watch((err, hash) => {
//console.log(`new block: ${hash}`)
if (typeof onNextBlock == 'function') {
onNextBlock(hash)
}
})
}
// for f it expects a function which takes an address as argument
function execForTestAccs(f) {
for(let i=0; i<3; i++) {
f(web3.eth.accounts[i])
}
}
function updateDetailTable() {
execForTestAccs( (addr, i) => {
streem.balanceOf(addr, (err, ret) => {
document.getElementById(`${addr}-bal`).innerHTML = web3.toDecimal(ret)
})
streem.dev_settledBalance({from: addr}, (err, ret) => {
document.getElementById(`${addr}-settled-bal`).innerHTML = web3.toDecimal(ret)
})
streem.dev_inStream({from: addr}, (err, ret) => {
const sender = ret[0], speed = web3.toDecimal(ret[1]), age = web3.toDecimal(ret[2])
document.getElementById(`${addr}-instream-speed`).innerHTML = speed
document.getElementById(`${addr}-instream-age`).innerHTML = speed == 0 ? 0 : age
})
streem.dev_outStream({from: addr}, (err, ret) => {
const receiver = ret[0], speed = web3.toDecimal(ret[1]), age = web3.toDecimal(ret[2])
document.getElementById(`${addr}-outstream-speed`).innerHTML = speed
document.getElementById(`${addr}-outstream-age`).innerHTML = speed == 0 ? 0 : age
})
})
}
// callback for new block event
function onNextBlock(hash) {
if(! viewFrozen) {
document.getElementById('blocknr').innerHTML = web3.eth.blockNumber
document.getElementById('nrstreams').innerHTML = streem.dev_streamsLength()
updateDetailTable()
}
}
// trigger block creation every <blocktime> seconds by transferring 1 wei (useful for testrpc).
function keepBlockchainAlive(blocktime) {
window.setTimeout( () => {
web3.eth.sendTransaction( {to: web3.eth.accounts[0], value: 1})
keepBlockchainAlive(blocktime)
}, blocktime * 1000)
}
// UI init
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM loaded')
// dynamically wire all buttons (convention: handler name == id)
const buttons = document.querySelectorAll('button')
for(let button of buttons) {
button.onclick = this[button.id]
console.log(`${button.id} wired`)
}
})
function initTableRowFor(addr) {
let table = document.getElementById('detail-table')
let row = document.createElement('tr')
row.innerHTML = `
<td id="${addr}">${addr}</td><td id="${addr}-bal">-</td><td id="${addr}-settled-bal">-</td>
<td id="${addr}-instream-speed">-</td><td id="${addr}-instream-age">-</td>
<td id="${addr}-outstream-speed">-</td><td id="${addr}-outstream-age">-</td>
`
table.appendChild(row)
}
window.addEventListener('load', () => {
console.log('window loaded')
initContract()
if(web3.eth.defaultAccount == undefined) {
alert("no Ethereum account found")
}
web3.eth.getBalance(web3.eth.defaultAccount, (err, ret) => {
if(! err) {
console.log(`Ether balance is ${web3.fromWei(web3.toDecimal(ret))}`)
} else {
alert("Something went wront: can't read Ether balance. Blockchain node connected?")
}
})
if(web3.version.node.toLocaleLowerCase().indexOf("testrpc") != -1) {
keepBlockchainAlive(5) // testrpc seems to have a lower limit of 5 sec
}
if(web3.eth.accounts.length < 3) {
alert("ERROR: less than 3 accounts available")
}
execForTestAccs( addr => initTableRowFor(addr) )
})
// ############################################################################
// closes the outgoing streams of the test accounts
// this will throw an expection for accounts without outstream. Doesn't hurt us.
function close() {
execForTestAccs( addr => {streem.closeStream({from: addr, gas: 200000}, (err, ret) => {
if(err) console.log(`closeStream failed for ${addr}`)
})})
}
var viewFrozen = false
// toggle logic
function freeze() {
viewFrozen = viewFrozen == true ? false : true
document.getElementById('freeze').innerHTML = viewFrozen == true ? 'Un-freeze view' : 'Freeze view'
}
function reset() {
execForTestAccs( addr => streem.dev_reset( {from: addr, gas: 2000000} )) //note that this is more gas!
}
// test: balances are correct
function test1() {
// stream1: speed 1 from addr0 to addr1
console.log('starting stream1')
streem.openStream(web3.eth.accounts[1], 1, {from: web3.eth.accounts[0], gas: 200000})
// stream2: speed 1 from addr1 to addr2
console.log('starting stream2')
streem.openStream(web3.eth.accounts[2], 1, {from: web3.eth.accounts[1], gas: 200000})
}
// test: dryed out stream
function test1a() {
// ...
// close stream1
// wait 5
// close stream2
// assert: bal(addr1) is 0, stream2 stalls
}
// test: underfunded stream
function test1b() {
// ...
test1()
// stream with speed 1 from addr0 to addr1
// stream with speed 2 from addr1 to addr2
// wait 5
// assert: bal(addr1) is 0, lab(addr2) is 5
// close stream1
// close stream2
// assert: bal(addr1) is 0, lab(addr2) is 5
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>Streem Test</title>
<meta charset="UTF-8">
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
</head>
<body>
Blockchain height: <span id="blocknr">-</span><br>
Size of streams array: <span id="nrstreams">-</span><br>
<hr>
<h3>Balances:</h3>
<table id="detail-table">
<tr>
<th>address</th><th>balance</th><th>settled balance</th>
<th>instream speed</th><th>instream age</th>
<th>outstream speed</th><th>outstream age</th>
</tr>
</table>
<hr>
<button id="close">Close streams</button><br>
<button id="freeze">Freeze view</button><br>
<button id="reset">Reset</button><br>
<hr>
<button id="test1">Test1</button><br>
<button id="test2">Test1a</button><br>
<button id="test3">Test1b</button><br>
<script type="text/javascript" src="lib/web3.js"></script>
<script type="text/javascript" src="js/streem_contract.js"></script>
<script type="text/javascript" src="js/test.js"></script>
</body>
</html>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment