Skip to content
Snippets Groups Projects
Commit a1dd3763 authored by Robert Martin-Legene's avatar Robert Martin-Legene
Browse files

Introducing the Gas Well. Distribution still does not work.

parent 1c48fcac
No related branches found
No related tags found
No related merge requests found
......@@ -24,6 +24,13 @@
- If the text can not be found, it is because your insert transaction still isn't in the blockchain. Wait a bit and try again.
- Try the basic `explorer.sh`. It follows "latest" by default, but you can specify a block number as argument, e.g. `explorer.sh 0` to see genesis (block 0).
- Try out `walker.pl`
12. Install node.js so you can do better scripts locally:
`sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 68576280`
`sudo apt-add-repository "deb https://deb.nodesource.com/node_7.x $(lsb_release -sc) main"`
`sudo apt-get update`
`sudo apt-get install nodejs`
`npm init -y`
`npm install web3`
There are other "interesting" programs in the bin/ and src/ directories.
......
#!/usr/bin/node
var port = 14349; // cloud
port = 16437; // bc
const contractaddr= '0x283bc55557c08dbc4f902b2b8479a2201a47e1c1';
const readline = require('readline');
const rl = readline.createInterface( { input: process.stdin, output: process.stdout } );
const Web3 = require('web3');
const fs = require('fs');
const Personal = require('web3-eth-personal');
var web3;
var GasWell;
var from;
function init()
{
if ( process.env.BFAHOME == undefined )
{
console.log( "$BFAHOME not set. Did you source bfa/bin/env ?" );
process.exit( 1 );
}
web3 = new Web3( "http://127.0.0.1:"+port );
//accounts = new Accounts( web3 );
var abi = "";
var abifile = fs.createReadStream(
process.env.BFAHOME+"/network5445/contracts/"+contractaddr+"/abi" )
.on('readable', () => {
var data;
while (data = abifile.read())
abi += data;
})
.on('end', () => {
GasWell = new web3.eth.Contract( JSON.parse(abi), contractaddr );
getlist();
})
;
getAccount();
}
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function isAddr(n) {
return n.length == 42 && n.substring(0,2) == "0x";
}
function getAccount()
{
var personal = new Personal( web3 );
personal.getAccounts().then(
function gotPersonalAccounts(obj) {
console.log("Setting default account to "+ obj[0]);
from = obj[0];
web3.eth.defaultAccount = obj[0];
},
function failedGettingPersonalAccounts(err){
console.log("Did you remember to allow 'personal' via the RPC port? " + err)
process.exit( 1 );
}
);
}
function getpos(i)
{
var arr;
var p = GasWell.methods.atPosition(i).call()
.then(
function gotOneAccount(result) {
console.log( "CC: "+result);
console.log( "C1: "+result[0]);
arr = result;
},
function gotOneAccountError(err) {
console.log( "DD: "+err);
console.log(err);
}
);
return arr;
}
function wellSort(a,b)
{
if ( b == undefined )
{
if ( a == undefined )
return 0;
else
return -1;
}
if ( a == undefined )
return 1;
var strA = a[0].toLowerCase();
var strB = b[0].toLowerCase();
if ( strA < strB )
return -1;
if ( strA > strB )
return 1;
return 0;
}
function requestBalances( count )
{
var well = new Array;
var proms = new Array;
var i;
for ( i=0; i<count; i++ )
{
proms.push(
GasWell.methods.atPosition(i).call( {} )
.then(
function(res){
well.push(res);
},
function(err) { console.log("Fetching position data failed: "+err) }
)
);
}
Promise.all( proms ).then(
function(x) {
// The Well has now been filled out
// so we will ask for the balances of the found accounts
var p2 = new Array();
for ( i=0; i<count; i++ )
{
var cb = setBal.bind(null,well,i);
p2.push( web3.eth.getBalance( well[i][0] ).then(cb) );
}
Promise.all( p2 ).then(
function allbalances(a) { displayBalances( well ) },
function(err) { console.log("Getting balances failed: "+err) }
);
},
function(err) { console.log("Getting account list failed: "+err) }
);
}
function setBal( arr, idx, val )
{
arr[idx][2]=val;
}
function editAccount( entry, well )
{
if ( entry == undefined )
return;
var acct;
var value;
// it is an existing account address?
if ( isAddr(entry) )
{
var i = 0;
var n = well.length;
while ( i < n )
{
if ( (""+well[i][0]).toLowerCase() == (""+entry).toLowerCase() )
entry = i;
i++;
}
}
// it is a new account address?
if ( isAddr(entry) )
{
acct = entry;
value = 0;
}
else
if ( isNumeric(entry) && entry < well.length )
{
acct = well[entry][0];
value = well[entry][1];
}
else
if ( entry == "x" )
{
// trigger Gas Well distribution
GasWell.methods.distribute().send( {"from": from, "gas": 1000000 } )
.then(
function distOK(x) {
console.log("Distribute returned succesfully in block# "+x.blockNumber+" using "+x.gasUsed+" gas.");
getlist();
},
function distFail(x) {
console.log("Distribute returned errors in block# "+x.blockNumber+" using "+x.gasUsed+" gas.");
console.log(x);
process.exit( 1 );
}
);
return;
}
else
{
console.log("I don't know what to do with \""+entry+"\"." );
process.exit( 1 );
}
rl.question("Adjust the Mwei fill value of "+acct+" (setting to 0 is the same as deleting)\nAmount?: ", (answer) => {
if ( isNumeric(answer) )
{
answer *= 1000000;
console.log("Sending update to the Gas Well...");
GasWell.methods.setEtherAllowance(acct,answer).send( {"from": from } )
.then(
function(a){
console.log("Update accepted.")
getlist();
},
function(b){
console.log("\nMaybe you are not authorized:\n"+b+"\n\n\nI think you should leave now.\n");
process.exit( 1 );
}
)
;
}
else
{
console.log( "I have no idea what to do with \""+answer+"\"." );
process.exit( 1 );
}
rl.close;
});
}
function displayBalances( well )
{
var n = well.length;
var i;
if ( well == undefined )
{
console.log( "Bank is not defined." );
process.exit( 1 );
}
well.sort(wellSort);
console.log("");
var longestbefore = 1;
var longestafter = 9;
for ( i=0; i<n; i++ )
{
var len = Math.floor(well[i][1]/1000000).toString().length;
if ( len > longestbefore )
longestbefore = len;
}
for ( i=0; i<n; i++ )
{
var entry = well[i];
if ( entry == undefined )
console.log( i+": <undef>" );
else
{
var numstr = ""+well[i][1]/1000000;
var before = numstr;
var after = "";
if ( numstr.search(/\./) > -1 )
{
before = numstr.replace(/\.[0-9]+$/, '');
after = numstr.replace(/^[0-9]*\./, '');
}
while ( before.length < longestbefore )
before = " "+before;
while ( after.length < longestafter )
after += "0";
numstr = before+"."+after;
var has = well[i][2].toString();
if ( has.length > 9 )
has = has.replace(/[0-9]{9}$/, ".$&");
console.log( i+": "+well[i][0]+" fills to "+numstr+" Mwei (has "+has+")." );
}
}
console.log("");
rl.question("Which account to edit (enter index number of full account number)?: ",
(answer) => {
if ( answer != undefined )
{
if ( (""+answer).toUpperCase() == 'Q' )
process.exit( 0 );
else
editAccount(answer, well);
}
rl.close;
}
)
}
function getlist()
{
GasWell.methods.numberOfBeneficiaries()
.call()
.then(
requestBalances,
function beneficiaryFail(x){console.log(x)}
);
}
init();
#!/bin/bash
if [ -z "${BFAHOME}" ]; then echo "\$BFAHOME not set. Did you source bfa/bin/env ?" >&2; exit 1; fi
source ${BFAHOME}/bin/libbfa.sh || exit 1
# get number of accounts
# get limit of every account
# allow scroll up/down to select an account
# press enter to edit an account's limit
# press ins to add an account
# press del to delete an account (set limit = 0)
# press Q to quit.
js=$( mktemp )
out=$( mktemp )
cleanup $js $out
yes | head -1000
function admin
{
while :
do
(
echo var contract = $( contract GasWell )
echo "var pos = contract.numberOfBeneficiaries.call();"
echo "while (pos-- > 0) { var r=contract.atPosition(pos); console.log(r[0]+':'+r[1]);}"
) > $js
cat $js
geth_attach < $js > $out
cat $out
readarray -t < $out
# Delete last entry, because that's the "return value from our call to geth".
unset MAPFILE[$(( ${#MAPFILE[*]} - 1 ))]
if [ ${#MAPFILE[*]} -gt 0 ]
then
printf "%5s %-42s %10s\n" Index "Account address" Value
echo ''
for key in ${!MAPFILE[*]}
do
val=${MAPFILE[$key]}
printf "%5d %-42s %10s\n" $key ${val%:*} ${val#*:}
done
echo
echo '["+"=New entry, Q=quit, index number]'
read -p "Change which one of these: "
else
echo "No accounts found."
REPLY="+"
fi
if [ "${REPLY^^}" = "Q" -o -z "${REPLY}" ]
then
return
elif [ "${REPLY}" = "+" ]
then
read -p "Enter new account address: " acct
if [ ${#acct} -eq 40 -a "${acct:0:2}" != "0x" ]
then
acct="0x${acct}"
fi
amount=0
elif [ "${REPLY}" != '+' ]; REPLY=$( echo ${REPLY} | sed 's/[^0-9]//' ); [ -n "${REPLY}" ]
then
if [ ${#MAPFILE[*]} -gt "${REPLY}" ]
then
amount=${MAPFILE[${REPLY}]}
acct=${amount%:*}
amount=${amount#*:}
fi
else
return
fi
echo '["DEL"=delete]'
read -i "${amount}" -p "Set value for account ${acct}: " amount
if [ "${amount^^:0:3}" = "DEL" ]
then
amount=0
fi
contractSendTx GasWell setEtherAllowance \"$acct\" ${amount} | geth_attach
done
}
admin
#void setEtherAllowance( address, uint256 )
#uint256 numberOfBeneficiaries( )
#void distribute( )
#uint256 getEtherAllowance( address )
#!/bin/bash
if [ -z "${BFAHOME}" ]; then echo "\$BFAHOME not set. Did you source bfa/bin/env ?" >&2; exit 1; fi
source ${BFAHOME}/bin/libbfa.sh || exit 1
bfaconfig network
contract="${BFANETWORKDIR}/contracts/GasWell"
realdir=$( realpath "${contract}" )
test -r "${realdir}"
address=$( basename "${realdir}" )
test -n "${address}"
contractSendTx GasWell selfDestruct | tee /dev/tty | geth_attach
#!/bin/bash
if [ -z "${BFAHOME}" ]; then echo "\$BFAHOME not set. Did you source bfa/bin/env ?" >&2; exit 1; fi
source ${BFAHOME}/bin/libbfa.sh || exit 1
bfaconfig network
contract="${BFANETWORKDIR}/contracts/GasWell"
realdir=$( realpath "${contract}" )
test -r "${realdir}"
address=$( basename "${realdir}" )
test -n "${address}"
contractSendTx GasWell distribute | tee /dev/tty | geth_attach
// vim:filetype=javascript
pragma solidity ^0.4.24;
contract GasWell {
address owner;
struct Allowances {
address beneficiary;
uint256 value;
}
Allowances[] thelist;
// We use distpos to remember where we were stopped processing last time
// we were called. The idea is, that if we have too many accounts to take
// care of, and too little gasleft, then we stop before we run out of
// gas, since that would undo all the transactions we had already handled.
// Also, we don't want to favour anyone in particular in the list, such
// that some would have first priority in getting ether at every invocation
// So we continue checking the list from the same place where we left off
// at the previous invocation of distribute()
uint256 distpos;
constructor() public payable {
owner = msg.sender;
}
modifier onlyOwner {
require( msg.sender == owner );
_;
}
// Using this function, you can find out how long thelist is.
function numberOfBeneficiaries() public view returns (uint256) {
return thelist.length;
}
// Using this function, you get the address and "value" at a given position in thelist.
function atPosition( uint256 idx ) public view returns (address,uint256) {
require( idx <= thelist.length, "There are not that many addresses in the list." );
return (thelist[idx].beneficiary,thelist[idx].value);
}
// Returns a (signed) position of where an address can be found in thelist.
// Or returns -1 if the address is not found in thelist.
function _beneficiaryPosition( address beneficiary ) internal view returns (int256) {
uint256 pos = thelist.length & 0x7fffffffffffffffffffffffffffffff;
while ( pos-- > 0 )
if ( beneficiary == thelist[pos].beneficiary )
return int256( pos & 0x7fffffffffffffffffffffffffffffff );
return -1;
}
// This function returns the "allowance" that a given address is set to.
// Using this function, you don't have to cycle through atPosition() until
// you find the address you want to know about.
function getEtherAllowance( address beneficiary ) public view returns (uint256) {
int256 pos = _beneficiaryPosition( beneficiary );
if ( pos == -1 )
return 0;
return thelist[uint256(pos)].value;
}
// This admin (ownerOnly) function allows the creator of the contract to
// add/change/delete "allowances" per address.
function setEtherAllowance( address beneficiary, uint256 value ) public onlyOwner {
int256 pos = _beneficiaryPosition( beneficiary );
if ( value > 0 ) {
if ( pos == -1 )
// Add the address to thelist if it was not already there
thelist.push( Allowances(beneficiary, value) );
else
// Simple update the value of this address
thelist[uint256(pos)].value = value;
return;
} else {
// The beneficiary is set to have 0 Ether, so we
// delete the beneficiary from the list
uint i = uint256(pos) + 1;
while ( i++ < thelist.length )
thelist[i] = thelist[i+1];
// If distpos was past the position that we removed,
// then move that back one.
if ( distpos > uint256(pos) )
distpos--;
// Shorten the list
thelist.length--;
}
}
function selfDestruct() public onlyOwner {
selfdestruct( owner );
}
function distribute() external {
uint256 listlength = thelist.length;
// Is there anything to do at all
if ( listlength == 0 )
return;
// Has the list gotten shorter since we we were last time?
// This shouldn't happen, but it's better to be safe than to be sorry.
if ( distpos >= listlength )
distpos = 0;
uint256 wheretostop = distpos;
while ( gasleft() > 54321 ) {
// Did we get to the end of the list, then start over
if ( ++distpos >= listlength )
distpos = 0;
uint256 balance = thelist[distpos].beneficiary.balance;
uint256 value = thelist[distpos].value;
if ( balance < value && (value+diff) > 0 )
{
uint256 diff = value - balance;
// we use send() instead of transfer(), because
// transfer() can throw(), and we don't want
// to stop processing because of a single error.
thelist[distpos].beneficiary.send( diff ) || true;
}
if ( wheretostop == distpos )
return;
}
}
function () external payable {
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment