Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • blockchain/nucleo
  • miguel/nucleo
  • Rcradiologia/nucleo
  • nelson/nucleo
4 results
Show changes
Showing
with 2194 additions and 266 deletions
web3
rusty-rlp
ecdsa
Crypto
dnspython
#!/usr/bin/perl -w
# Robert Martin-Legene <robert@nic.ar>
# 20190423(c) NIC Argentina, GPLv2.
# apt install libclass-accessor-perl libmath-bigint-perl
use strict;
use warnings;
use Carp;
use Math::BigInt;
use JSON;
use IO::Socket::INET;
BEGIN {
die "\$BFAHOME not set. Did you source bfa/bin/env ?\n"
unless exists $ENV{BFAHOME};
}
my %network = (
47525974938 => 'BFA red principal [est. 2018]',
55555000000 => 'BFA red de pruebas numero 2 [est. 2019]',
);
chdir $ENV{BFAHOME} or die $!;
use lib $ENV{'BFAHOME'}.'/bin';
use libbfa;
package error;
use JSON;
use Carp;
sub new
{
my ($class, $json_in) = splice(@_,0,2);
my $opt = pop @_ || {};
my $self = bless {
'_code' => undef,
'_message' => undef,
}, ref $class || $class;
confess "No input JSON received, stopped" unless defined $json_in;
my $json;
if ( ref $json_in eq '' )
{
eval {
$json = decode_json( $json_in );
};
confess $@ if $@;
}
if ( exists $json->{'error'} )
{
$self->code( $json->{'error'}->{'code'} ) if exists $json->{'error'}->{'code'};
$self->message( $json->{'error'}->{'message'} ) if exists $json->{'error'}->{'message'};
confess sprintf( "%s: %s, stopped", $self->code || 'undef', $self->message || 'undef' )
if $opt->{fatal};
}
return $self;
}
sub code
{
my ( $self, $val ) = @_;
$self->{'_code'} = $val if scalar @_ > 1;
return $self->{'_code'};
}
sub message
{
my ( $self, $val ) = @_;
$self->{'_message'} = $val if scalar @_ > 1;
return $self->{'_message'};
}
package block;
use Math::BigInt;
use base qw( Class::Accessor );
__PACKAGE__->mk_accessors(qw(
difficulty extraData gasLimit gasUsed hash logsBloom miner mixHash nonce
number parentHash receiptsRoot sha3Uncles size stateRoot timestamp
totalDifficulty transactions transactionsRoot uncles
));
sub new {
my ($class, $fields) = @_;
my $self = bless {}, ref $class || $class;
foreach my $k ( keys %$fields )
{
$self->$k( ${$fields}{$k} );
}
return $self;
}
sub set
{
my ($self, $key) = splice(@_, 0, 2);
if (@_ == 1)
{
$_[0] = Math::BigInt->new( $_[0] )
if $_[0] =~ /^(?:0x[\da-fA-F]+|\d+)$/;
$self->{$key} = $_[0];
}
elsif (@_ > 1)
{
$self->{$key} = [@_];
}
else
{
$self->_croak("Wrong number of arguments received");
}
}
package main;
my $libbfa = libbfa->new();
my $result;
sub gmt
{
my $ts = shift;
return unless defined $ts;
my @t = gmtime($ts);
$t[5] += 1900;
$t[4] ++;
return sprintf(
'%04d-%02d-%02dT%02d:%02d:%02dZ',
(@t)[5,4,3,2,1,0]
);
}
sub nicetimedelta
{
my $s = shift;
my $t = '';
my %intervals = ( w => 604800, d => 86400, h => 3600, m => 60, s => 1 );
foreach my $letter (qw/ w d h m s /)
{
my $n = $intervals{$letter};
if ( $s >= $n or $t ne '')
{
$t .= sprintf '%02d%s', $s / $n, $letter;
$s %= $n;
}
}
return $t eq '' ? '0s' : $t;
}
sub rpc
{
my ($libbfa, $procedure, @args) = @_;
my $content = $libbfa->rpcreq( $procedure, @args );
confess unless $content;
my $json = decode_json( $content );
confess "$content, stopped" unless defined $json;
error->new( $json, {fatal=>1} );
return $json->{'result'};
}
my $netversion = rpc( $libbfa, 'net_version' );
if ( $netversion )
{
if ( exists $network{$netversion} )
{
printf "Running on network %s (#%s)\n", $network{$netversion}, $netversion;
} else {
printf "Running on network %s\n", $netversion;
}
}
### compare time
use constant days70y => 25567;
my $s = IO::Socket::INET->new(
PeerAddr => "time.nist.gov:37",
Timeout => 3,
);
if ( defined $s )
{
$s->recv(my $data, 8);
my $i = unpack('N', $data) || 0;
if ( $i > 0 )
{
# rfc868 offset (seconds from 1900-01-01 to 1970-01-01)
$i -= 2208988800;
printf "NIST time: %s\n", scalar(localtime($i));
my $heretime = time();
printf "Here time: %s\n", scalar(localtime($i));
if ( abs($i - $heretime) > 5 )
{
print "WHY IS YOUR CLOCK OFF?";
}
}
}
### latest block
$result = rpc( $libbfa, 'eth_getBlockByNumber', '"latest"', "true" );
my $block = block->new( $result );
my $timediff = time()-$block->timestamp;
printf
"Our latest block number is %d. It's timestamp says %s (%s old).\n",
$block->number,
gmt($block->timestamp),
nicetimedelta( $timediff );
### syncing ?
my $syncing = rpc( $libbfa, 'eth_syncing' );
if ( $syncing )
{
my $current = Math::BigInt->new( $syncing->{'currentBlock'} );
my $highest = Math::BigInt->new( $syncing->{'highestBlock'} );
my $starting = Math::BigInt->new( $syncing->{'startingBlock'} );
my $startgap = $highest->copy->bsub( $starting );
my $synced = $current->copy->bsub( $starting );
my $pct = 100.0 * $synced / $startgap;
printf "%d%% done syncing %d blocks.\n", int($pct), $startgap;
}
else
{
if ( $timediff > 90 )
{
print "We are currently not syncing. WHY ARE OUR BLOCKS SO OLD?\n";
} else {
print "We have all the blocks and are not syncing.\n";
}
}
### mining ?
$result = rpc( $libbfa, 'eth_mining' );
if ( $result )
{
printf "We are a sealer and are configured seal.\n";
} else {
print "We do not seal.\n";
}
# List peers
$result = rpc( $libbfa, 'admin_peers' );
# Can be undef if the admin module is not enabled in geth (opentx).
if ( defined $result )
{
my $i = 0;
foreach my $peer ( sort { $a->{'network'}{'remoteAddress'} cmp $b->{'network'}{'remoteAddress'} } @$result )
{
if ( ref $peer->{'protocols'}->{'eth'} eq 'HASH' )
{
print "[I]n/[O]ut [S]tatic [T]rusted\n" if $i == 0;
$i++;
my $ip = $peer->{'network'}->{'remoteAddress'};
$ip =~ s/:\d+$//; #port
$ip =~ s/^\[(.*)\]$/$1/; #ipv6
printf "[%s%s%s] %s\n",
$peer->{'network'}->{'inbound'} ? 'I' : 'O',
$peer->{'network'}->{'static'} ? 'S' : ' ',
$peer->{'network'}->{'trusted'} ? 'T' : ' ',
$ip;
}
}
printf "We are connected to %d peer%s.\n", $i, $i==1?'':'s';
}
# See recent signers - but skip if we are syncing, since that makes little sense.
my %signers;
$result = rpc( $libbfa, 'clique_getSnapshot', '"latest"' );
# Can be undef if the clique module is not enabled in geth (opentx).
if ( defined $result )
{
%signers = %{$result->{'signers'}};
}
if ( defined $result and not defined $syncing )
{
my %recents = %{$result->{'recents'}};
my $i = -98765;
foreach my $s ( sort keys %signers )
{
$signers{$s} = $i--;
}
foreach my $n ( keys %recents )
{
my $actor = $recents{$n};
$signers{$actor} = $n;
}
my $nplusone = int(scalar(keys %signers)/2)+1;
my $threshold = $block->number->copy->binc->bsub($nplusone);
foreach my $s ( sort { $signers{$a} <=> $signers{$b} } keys %signers )
{
my $this = Math::BigInt->new( $signers{$s} );
my $cmp = $threshold->bcmp( $this );
next unless defined $cmp;
if ( $cmp < 0 )
{
printf "Signer %s signed block %d recently.\n", $s, $this;
}
else
{
printf "Signer %s is allowed to sign next block.\n", $s;
}
}
}
## List accounts
$result = rpc( $libbfa, 'eth_accounts' );
if ( $result )
{
my $i = 0;
if ( scalar @$result )
{
foreach my $account ( @$result )
{
my $maymine = '';
$maymine = 'sealer'
if exists $signers{$account};
printf "Locally available account%s:\n", scalar @$result == 1 ? '' : 's'
if $i++ == 0;
my $txn = rpc( $libbfa, 'eth_getTransactionCount', qq("$account"), '"latest"' );
$txn =~ s/^0x([a-fA-F\d]+)$/hex($1)/e;
my $gold = rpc( $libbfa, 'eth_getBalance', qq("$account"), '"latest"' );
$gold = Math::BigInt->new( $gold ) if $gold =~ /^0x/;
printf "Account %d: %s %-6s %3d transaction%s, %s wei.\n", $i, $account, $maymine, $txn, ($txn==1?' ':'s'), $gold;
}
}
else
{
print "No accounts are locally available.\n";
}
}
#!/usr/bin/node
// vim:syntax:filetype=javascript:ai:sm
// vim:expandtab:backspace=indent,eol,start:softtabstop=4
"use strict"
const Libbfa = require( process.env.BFAHOME + '/bin/libbfa.js');
function mayseal()
{
var bfa = new Libbfa();
var web3 = bfa.newweb3();
var me = web3.eth.defaultAccount.toLowerCase();
web3.eth.isMining().then( function(isMining){
web3.eth.getSigners().then( function(x){
var lcsealers = x.map( name => name.toLowerCase() );
// console.log( "Miners are:\n\t" + lcsealers.join("\n\t") );
var isSigner = (lcsealers.indexOf(me) > -1);
var sealerfile = bfa.nodedir + '/miner';
var not = "not ";
if ( isSigner )
not = "";
console.log( "Account " + me + " is " + not + "allowed to seal." );
if ( isSigner )
{
if ( ! bfa.fs.existsSync( sealerfile ) )
{
var fd = bfa.fs.openSync( sealerfile, 'a' );
bfa.fs.close( fd );
console.log( "Created 'sealer' file " + sealerfile );
}
if ( ! isMining )
{
web3.eth.minerstart();
console.log( 'Started to seal.' );
}
else
{
console.log( "You are already sealing. All is well." );
}
}
else
{
if ( bfa.fs.existsSync( sealerfile ) )
{
bfa.fs.unlinkSync( sealerfile );
console.log( "Deleted 'sealer' file " + sealerfile );
}
if ( isMining )
{
web3.eth.minerstop();
console.log( 'I was trying to seal, but am not authorized. Stopped trying.' );
}
else
{
console.log( "I wasn't trying to seal anyway. All is well." );
}
}
});
},function(x){
console.log(x);
process.exit(1);
})
}
mayseal();
......@@ -4,130 +4,220 @@
"use strict"
const Libbfa = require( process.env.BFAHOME + '/bin/libbfa.js');
var bfa = new Libbfa();
var web3 = bfa.newweb3();
var lastUnlock = 0;
const Libbfa = require( process.env.BFAHOME + '/bin/libbfa.js');
const XMLHttpRequest = require("xmlhttprequest-ssl").XMLHttpRequest;
var bfa = new Libbfa();
var web3 = bfa.newweb3();
var lastUnlock = 0;
var netid = 0;
var peerscache = bfa.networkdir + '/peers.cache';
if ( bfa.fs.existsSync( bfa.networkdir + '/cache' ) )
peerscache = bfa.networkdir + '/cache/peers.cache';
function peerlist()
function readPeersCache()
{
web3.eth.bfaAdminpeers().then(
function gotAdminPeers( nodelist ) {
var now = new Date();
var nowpeers = [];
var peers = [];
var newpeers = [];
if ( bfa.fs.existsSync( bfa.networkdir + '/peers.cache' ) )
{
var data = bfa.fs.readFileSync( bfa.networkdir + '/peers.cache' ).toString();
if ( data.length > 0 )
peers = data.split(/\r?\n/);
var i = peers.length;
// for some odd reason, I keep seeing empty entries
while ( i-- > 0 )
if ( peers[i] == '' )
peers.splice(i,1);
}
nodelist.forEach(
function(node) {
if ( 'object' == typeof(node.protocols.eth) )
{
// default info - likely to get overwritten.
var info = "<" + node.id + ">";
var dir = "";
if ( undefined != node.network )
{
if ( 'boolean' == typeof(node.network.inbound) )
{
if ( node.network.inbound )
dir = "in";
else
dir = "out";
}
if ( 'string' == typeof(node.enode) )
{
info = node.enode;
if (peers.indexOf( node.enode ) == -1 && dir == "out" )
newpeers.push( node.enode );
}
else
{
info = "";
if ( undefined != node.id )
info += "<" + node.id + ">";
if ( undefined != node.network.remoteAddress )
{
if ( info != "" )
info+= "@";
info += node.network.remoteAddress;
}
}
}
nowpeers.push( "peer " + dir + ": " + info );
}
}
);
// write network/status
bfa.fs.writeFileSync(
bfa.networkdir + '/status',
"UTC: " + now.toUTCString() + "\n"
+ "BFA peers: " + nowpeers.length + "\n"
+ nowpeers.sort().join("\n") + "\n",
{ mode: 0o644 }
);
// Try to connect to a random node if we have very few peers
if ( nowpeers.length < 5 && peers.length > 0 )
{
var i = Math.floor( Math.random() * peers.length );
var enode = peers[i];
if ( ! bfa.fs.existsSync( peerscache ) )
return [];
var data = bfa.fs.readFileSync( peerscache ).toString();
var p = [];
if ( data.length > 0 )
p = data.split(/\r?\n/);
// for some odd reason, I keep seeing empty entries
for ( var i = p.length; i > 0; i-- )
if ( p[i] == '' )
p.splice(i,1);
return p;
}
function writePeersCache( peers )
{
// max 100 entries, FIFO
if (peers.length > 100)
peers.splice( 0, peers.length - 100 );
// peers.cache is a list of peers we have connected out to in the past.
var txt = peers.join("\n");
if (txt.length > 0 && (txt.substring(txt.length-1) != "\n"))
txt += "\n";
bfa.fs.writeFileSync( peerscache, txt, { mode: 0o644 } );
}
function dnspeercachelookup()
{
if ( netid == 0 )
return;
var dnsquery = new XMLHttpRequest();
// onreadystatechange is called when TCP things happen to the socket.
// We set up the state before we send the query (such that they are
// registered in the object)
dnsquery.onreadystatechange = function() {
// readyStates: 0=UNSENT, 1=OPEN, 2=SENT, 3=LOADING, 4=DONE
if ( this.readyState == 4 )
{
if ( this.status == 200 ) {
var json = JSON.parse(this.responseText);
if ( json.Status != 0 ) // 0 = NOERROR
return;
var i = Math.floor( Math.random() * json.Answer.length );
if ( json.Answer[i].type != 16 ) // 16 = TXT
return;
var enode = json.Answer[i].data;
// strip quotes
if ( enode.substring(0,1) == '"' && enode.substring(enode.length-1) == '"' )
enode = enode.substring(1,enode.length-1);
console.log(
"We have "
+ nowpeers.length
+ " peer" + ( nowpeers.length==1 ? '' : 's' ) + ", so will try to connect to "
"We have no peers, so will try to connect to "
+ enode
+ " found via DNS."
);
web3.eth.bfaAdminaddPeer( enode );
// Q: Can bad DNS data create a problem here?
// A: Geth checks data input validity.
web3.bfa.admin.addPeer( enode );
}
// write network/peers.cache
// peers.cache is a list of peers we have connected out to in the past.
peers = peers.concat( newpeers );
if (peers.length > 100)
peers.splice( 0, peers.length - 100 );
bfa.fs.writeFileSync(
bfa.networkdir + '/peers.cache',
peers.join("\n") + "\n",
{ mode: 0o644 }
);
},
function failedToGetAdminPeers(x)
}
};
// Robert runs a private little cheat registry on his private domain
// for fast update times. Can easily be moved to a hints.bfa.ar zone
// if desired needed.
// There are no new security aspects to consider, as this information
// is public already via bootnodes.
dnsquery.open( 'GET', 'https://cloudflare-dns.com/dns-query?name=hints.'+netid+'.bfa.martin-legene.dk&type=TXT' );
dnsquery.setRequestHeader( 'accept', 'application/dns-json' );
dnsquery.send();
}
function writeStatus( peers )
{
// write network/status
bfa.fs.writeFileSync(
bfa.networkdir + '/status',
"UTC: " + new Date().toUTCString() + "\n"
+ "BFA peers: " + peers.length + "\n"
+ peers.sort().join("\n") + "\n",
{ mode: 0o644 }
);
}
function parsenode( node )
{
if ( !node || !node.protocols || typeof node.protocols.eth != 'object' )
return;
if ( ! node.network )
return { info: "<"+node.id+">" };
var n = {};
if ( typeof node.network.inbound == 'boolean' )
n.dir = node.network.inbound ? "in" : "out";
if ( typeof node.enode == 'string' )
n.info = node.enode;
else
{
if ( node.id )
n.info += "<" + node.id + ">";
if ( node.network.remoteAddress )
{
// ignore connection problems?
if ( n.info )
n.info += "@";
n.info += node.network.remoteAddress;
}
}
return n;
}
function gotAdminPeers( err, nodelist )
{
var nowpeers = [];
var peerscache = readPeersCache();
var newoutpeers = [];
var currentnodes = [];
if ( err )
return;
// The nodelist also contains peers which are not yet validated
// if they even belong to this network. Parsenode returns an
// object or nothing, based on our criteria
nodelist.forEach(
function(node) {
var n = parsenode(node);
if ( n )
currentnodes.push( n );
}
);
currentnodes.forEach(
function(n) {
// Add to list of nowpeers (for stats file)
nowpeers.push( "peer " + ( n.dir ? n.dir : '') + ": " + n.info );
// See if this node reported by geth is already a known peers
// from our peers.cache
if (( peerscache.indexOf( n.info ) == -1 ) && ( n.dir == 'out' ))
newoutpeers.push( n.info );
}
);
writeStatus( nowpeers );
writePeersCache( newoutpeers.concat(peerscache) );
// Try to connect to a random node if we have very few peers
if ( nowpeers.length < 1 )
{
var candidate = [];
// find candidate nodes which we can connect to
// (it comes from peers.cache)
peerscache.forEach(
function(acachedpeer) {
// Add "a cached peer" to "candidate" peers
// if the cached peer is not in the list of currently
// connected nodes.
if ( ! currentnodes.includes( acachedpeer ) )
{
candidate.push( acachedpeer );
}
}
);
if ( candidate.length > 0 )
{
var i = Math.floor( Math.random() * candidate.length );
var enode = candidate[i];
console.log(
"We have "
+ nowpeers.length
+ " peer" + ( nowpeers.length==1 ? '' : 's' )
+ ", so will try to connect to "
+ enode
);
web3.bfa.admin.addPeer( enode );
}
else
if ( nowpeers.length == 0 )
dnspeercachelookup();
}
}
function peerlist()
{
web3.bfa.admin.peers( gotAdminPeers );
}
function mayseal()
// Function to determine if our defaultAccount is allowed to seal/mine.
// It will adjust the behaviour accordingly, i.e. stop or start mining.
function mayseal()
{
var me = web3.eth.defaultAccount;
if ( undefined == me )
{
console.log( "Failed to get default account information." );
return;
}
// Failed to get default account information.
me = 'xxxx'
me = me.toLowerCase();
web3.eth.isMining().
then(
web3.eth.isMining()
.then(
// returns a boolean whether or not we are currently mining/sealing.
function( isMining )
{
// Get a list of clique.getSigners, so we can see if we are
// in the list of authorized sealers.
web3.eth.bfaGetSigners()
.then(
function gotListOfSealers(x)
web3.bfa.clique.getSigners(
function gotListOfSealers(e,x)
{
if (e)
{
console.error( e );
return;
}
var lcsealers = x.map( name => name.toLowerCase() );
var isSigner = (lcsealers.indexOf(me) > -1);
if ( isSigner )
......@@ -135,7 +225,7 @@ function mayseal()
if ( ! isMining )
{
console.log( 'Started to seal.' );
web3.eth.bfaMinerstart();
web3.bfa.miner.start();
}
}
else
......@@ -143,20 +233,16 @@ function mayseal()
if ( isMining )
{
console.log( 'I was trying to seal, but am not authorized. Stopped trying.' );
web3.eth.bfaMinerstop();
web3.bfa.miner.stop();
}
}
},
function failedToGetListOfSealers(x)
{
console.log(x);
}
);
},
function failedToGetIsMiningBool(x)
{
// Probably geth is not running.
//console.log(x);
//throw new Error(x);
}
);
}
......@@ -165,10 +251,11 @@ function unlock()
{
if ( lastUnlock + 600 > Date.now() / 1000 )
return;
web3.eth.personal.bfalistWallets()
.then(
function pushone(x)
var unlockedsomething = false;
web3.bfa.personal.listWallets(
function pushone(e,x)
{
if (e) return;
var i = x.length;
var wallets = new Array();
while ( i-- > 0 )
......@@ -186,22 +273,59 @@ function unlock()
var addr = wallets[i].accounts[j].address;
var promise =
web3.eth.personal.unlockAccount( addr, "", 0 )
.catch( error => { } );
.then( x => {
if ( x )
{
console.log( "Unlocked " + addr );
}
} )
.catch( error => {} );
promises.push( promise );
unlockedsomething = true;
}
}
lastUnlock = Date.now() / 1000;
}
,
function err(x)
{
// we don't care?
Promise.all( promises )
.then(
function()
{
if ( unlockedsomething )
{
web3.eth.isMining()
.then(
function()
{
web3.bfa.miner.stop();
mayseal();
}
)
}
},
function()
{}
);
}
)
);
lastUnlock = Date.now() / 1000;
}
function timer()
{
if ( bfa.sockettype == 'ipc' && ! bfa.fs.existsSync( bfa.socketurl ) )
{
return;
}
if ( netid == 0 )
{
web3.eth.net.getId()
.then( x => {
netid = x;
} )
.catch( err => {
console.log( "monitor.js unable to connect to geth." );
process.exit(1);
});
return;
}
peerlist();
mayseal();
unlock();
......
#!/usr/bin/node
const Web3 = require( 'web3' );
// make sure we die early if this is not installed
const dummy = require( 'web3-eth-accounts' );
const fs = require( 'fs' );
var web3 = new Web3( 'http://localhost:8545/' );
var myArgs = process.argv.slice(2);
var myaccount = myArgs[0];
var proof = myArgs[1];
var password = myArgs[2] || '';
var contractaddr;
var networkid;
var chainid;
var rawdata;
var estimate;
if ( myArgs.length < 2 || myArgs.length > 3 )
{
let myname = process.argv[1].replace(/^.*\//, '');
console.error( "Wrong number of arguments.");
console.error( "Usage: " + myname + " <account> <proof> [<privatekeypassword>]");
process.exit( 1 );
}
function findMatchingFile( needle, accountdir )
{
return new Promise(
function(resolve, reject)
{
// strip leading '0x' from the needle, if present
// also lowercase the name
needle = needle.replace(/^0x/, '').toLowerCase();
let haystack = fs.readdirSync( accountdir, 'utf8' );
haystack.forEach(
(filename) => {
if ( filename.toLowerCase().endsWith('--'+needle) )
resolve(accountdir + filename);
}
);
reject( 'Account not found. Try specifying the absolute path to your key file, or set one of $BFAHOME $BFANETWORKDIR $BFANODEDIR $KEYSTORE' );
}
);
}
function findaccountfile( accountfile )
{
return new Promise(
function( resolve, reject )
{
// find account file
// is it actually just a file?
if ( accountfile.includes('/') )
{
resolve( accountfile );
return;
}
let accountdir;
// if not a file, then look (only) at the most specific directory we have been told about.
if ( process.env.KEYSTORE !== undefined )
accountdir = process.env.KEYSTORE;
else
if ( process.env.BFANODEDIR !== undefined )
accountdir = process.env.BFANODEDIR + '/keystore/';
else
if ( process.env.BFANETWORKDIR !== undefined )
accountdir = process.env.BFANETWORKDIR + '/node/keystore/';
else
if ( process.env.BFAHOME !== undefined )
accountdir = process.env.BFAHOME + '/network/node/keystore/';
else
accountdir = process.env.HOME + '.geth/keystore';
findMatchingFile( myaccount, accountdir )
.then( resolve )
.catch( reject );
}
);
}
function loadaccount( path )
{
return new Promise(
function( resolve, reject )
{
let contents = fs.readFileSync( path, 'utf8' );
fs.readFile( path, 'utf8', (err,data) => {
if ( err )
{
reject( err );
return;
}
let keystore;
try {
keystore = JSON.parse( data );
}
catch(e)
{
reject( e );
return;
}
let w;
try {
w = web3.eth.accounts.wallet.decrypt( [keystore], password );
}
catch(e)
{
reject( e );
return;
}
// Make sure we have the address properly written (instead of a
// filename) if that was given as parameter when starting the program.
if ( w === undefined )
{
reject( "Unable to decrypt the key file." );
return;
}
myaccount = w[0]["address"];
resolve();
});
}
);
}
function gasestimate(method)
{
return new Promise(
function( resolve, reject )
{
method.estimateGas()
.then( g => { estimate = g } )
.then( resolve )
}
);
}
function getnetworkid()
{
return new Promise(
function( resolve, reject )
{
web3.eth.net.getId()
.then( id => {
networkid = id;
if ( networkid == 0xb10c4d39a )
{
contractaddr = "0xc3cF96aF51d3c41DfBE428dE75a8E5597d4D7A7B";
chainid = 0xbfa2018;
}
else
{
reject( "There is insufficient information to function on this chain." );
return;
}
resolve();
});
}
);
}
function getnonce( account )
{
return new Promise(
function( resolve, reject )
{
// Cuantas transacciones ya hemos hecho?
web3.eth.getTransactionCount( account )
.then( txcount => {
nonce = txcount;
resolve();
});
}
);
}
function prepareCall()
{
var abi = [
{
"name" : "hash",
"type" : "function",
"inputs" : [ { "name": "key", "type": "string" } ],
"outputs" : [ { "name": "", "type": "bytes32" } ],
"constant" : true,
"payable" : false,
"stateMutability" : "pure"
},
{
"name" : "proveControl",
"type" : "function",
"inputs" : [ { "name": "key", "type": "string" } ],
"outputs" : [ { "name": "", "type": "bool" } ],
"constant" : false,
"payable" : false,
"stateMutability" : "nonpayable"
}
];
return new Promise(
function(resolve, reject)
{
let p1 = getnetworkid();
let p2 = getnonce( myaccount );
Promise.all([ p1, p2 ])
.then( () => {
if ( contractaddr === undefined )
{
reject( "The contract address is not defined for this network." );
return;
}
// Creá una instancia del contrato
var contractInstance = new web3.eth.Contract(abi, contractaddr );
//Estimá el gas que vas a gastar
let method = contractInstance.methods.proveControl( proof );
// contractInstance.methods.hash(proof).call().then( console.log );
rawdata = method.encodeABI();
gasestimate( method )
.then( resolve );
});
}
);
}
function signrawtx()
{
//Generá una transacción
let promise = web3.eth.accounts.signTransaction(
{
"from" : myaccount,
"to" : contractaddr,
"nonce" : nonce,
"value" : 0,
"data" : rawdata,
"gas" : estimate,
"gasPrice" : 1000000000,
"common" :
{
"customChain" :
{
"name" : "BFA 2018",
"chainId" : chainid,
"networkId" : networkid
}
}
},
web3.eth.accounts.wallet[0].privateKey
);
return promise;
}
function printReceipt( r )
{
console.log( "blockNumber: " + r.blockNumber );
console.log( "transactionHash: " + r.transactionHash );
console.log( "Status: " + (r.status?'Transaction successful':'Transaction failed') );
}
findaccountfile( myaccount )
.then( (path)=> { return loadaccount(path) } )
// prepara la instancia del contrato
.then ( prepareCall )
// Firma "offline"
.then( () => { return signrawtx() })
// Manda la transaccion a la blockchain
.then( tx => {
console.log( "Sending this transaction: " + tx.rawTransaction );
console.log( "TX hash: " + tx.transactionHash );
//process.exit( 0 );
web3.eth.sendSignedTransaction( tx.rawTransaction )
//Obtené el recibo.
.once( 'receipt', printReceipt )
.catch( (e) => { console.error( "It would seem that the transaction has failed:\n" + e)} );
})
.catch( e => {
console.error( "Oh no. Something most definitely seems to have gone wrong. What I was told is:" );
console.error( e );
});
#!/bin/bash
# Robert Martin-Legene <robert@nic.ar>
# Proposes to promote a new sealer or to demote an existing sealer.
# Also sends this vote to the new Sealers contract, so we have a place
# a contract can look up which addresses are sealers.
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
operation="false"
if [ $( basename $0 ) == "sealeradd.sh" ]
then
operation="true"
fi
victim="$1"
(
echo "clique.propose(\"0x${victim#0x}\", ${operation});"
contractSendTx Sealers admin_promote \"0x${victim#0x}\" ${operation}
) | geth_attach
sealeradd.sh
\ No newline at end of file
#!/usr/bin/perl -w
use strict;
use warnings;
use Math::BigInt;
use Carp;
$Carp::Verbose = 1;
BEGIN {
die "\$BFAHOME not set. Did you source bfa/bin/env ?\n"
unless exists $ENV{BFAHOME};
}
use lib $ENV{'BFAHOME'}.'/bin';
use libbfa;
my $libbfa;
package ansi;
our $CSI = "\x1b[";
sub CUP { $CSI.(shift||1).';'.(shift||1).'H' }
sub EL { $CSI.(shift||0).'K' }
sub ED { $CSI.(shift||0).'J' }
sub normal { $CSI.'m' }
sub black { $CSI.'30m' }
sub red { $CSI.'41m' }
sub green { $CSI.'42m' }
sub brightwhite { $CSI.'97m' }
sub bgyellow { $CSI.'103m' }
package alias;
use IO::File;
our $initialised = 0;
our %list;
sub translate
{
my $victim = lc shift;
if ( ! $initialised )
{
$initialised = 1;
local $_ = undef;
my $fh = IO::File->new($libbfa->{'networkdir'}.'/aliases');
if ( defined $fh )
{
while ( $_ = $fh->getline )
{
s|^\s+||;
s|\s+$||;
my @e = split /\s+/, $_, 2;
if ( scalar @e == 2 )
{
my $addr = lc $e[0];
$list{$addr}= $e[1];
}
}
$fh->close;
}
}
return $list{$victim} if exists $list{$victim};
return;
}
package tools;
our $CSI = "\x1b[";
our $clearEOS = "${CSI}J";
our $up = "${CSI}A";
our $red = "${CSI}41m";
our $normal = "${CSI}m";
sub new
{
my ($class, $libbfa) = @_;
my $self = bless {}, ref $class || $class;
return $self;
}
sub gmt
{
my $ts = shift;
return unless defined $ts;
my @t = gmtime($ts);
$t[5] += 1900;
$t[4] ++;
return sprintf('%04d%02d%02d-%02d%02d%02d', (@t)[5,4,3,2,1,0]);
}
sub hex2string($)
{
my ($msg) = @_;
my $txt = '';
while ($msg ne '')
{
my $i = hex( substr($msg,0,2) );
$txt .= ( $i >= 32 and $i <= 127 ) ? chr($i) : '.';
$msg = substr $msg, 2;
}
return $txt;
}
sub max(@)
{
my $num = 0;
local $_ = undef;
foreach $_ (@_)
{
$num = $_
if $num < $_;
}
return $num;
}
package error;
use JSON;
sub new
{
my ($class, $json_in) = @_;
my $self = bless {
'_code' => undef,
'_message' => undef,
}, ref $class || $class;
my $json;
eval {
$json = decode_json( $json_in )
};
return unless defined $json;
return unless exists $json->{'error'};
$self->code( $json->{'error'}->{'code'} ) if exists $json->{'error'}->{'code'};
$self->message( $json->{'error'}->{'message'} ) if exists $json->{'error'}->{'message'};
return $self;
}
sub code
{
my ( $self, $val ) = @_;
$self->{'_code'} = $val if scalar @_ > 1;
return $self->{'_code'};
}
sub message
{
my ( $self, $val ) = @_;
$self->{'_message'} = $val if scalar @_ > 1;
return $self->{'_message'};
}
package block;
#use LWP;
use JSON;
sub new
{
my ( $class, $libbfa ) = @_;
my $self = bless {}, ref $class || $class;
$self->{'libbfa'} = $libbfa if defined $libbfa;
return $self;
}
sub parse
{
my ( $self, $json_raw ) = @_;
return unless defined $json_raw;
return if $json_raw eq '';
$self->{'json_raw'} = $json_raw;
eval { $self->{'json'} = decode_json( $json_raw ) };
return if $@;
$self->error( error->new($json_raw) );
return $self;
}
sub error
{
return if not exists $_[0]->{'error'};
return $_[0]->{'error'};
}
sub json
{
return unless exists $_[0]->{'json'};
return $_[0]->{'json'};
}
sub result
{
return unless exists $_[0]->{'json'}->{'result'};
return $_[0]->{'json'}->{'result'};
}
sub number
{
return if not exists $_[0]->result->{'number'};
return hex( $_[0]->result->{'number'} );
}
sub difficulty
{
return if not exists $_[0]->result->{'difficulty'};
return hex( $_[0]->result->{'difficulty'} );
}
sub td
{
return if not exists $_[0]->result->{'totalDifficulty'};
return hex( $_[0]->result->{'totalDifficulty'} );
}
sub timestamp
{
return if not exists $_[0]->result->{'timestamp'};
return hex( $_[0]->result->{'timestamp'} );
}
sub gasLimit
{
return if not exists $_[0]->result->{'gasLimit'};
return hex( $_[0]->result->{'gasLimit'} );
}
sub miner
{
return if not exists $_[0]->result->{'miner'};
return $_[0]->result->{'miner'};
}
sub nonce
{
return if not exists $_[0]->result->{'nonce'};
return lc $_[0]->result->{'nonce'};
}
sub hash
{
return if not exists $_[0]->result->{'hash'};
return lc $_[0]->result->{'hash'};
}
sub parentHash
{
return if not exists $_[0]->result->{'parentHash'};
return lc $_[0]->result->{'parentHash'};
}
sub extradata
{
return if not exists $_[0]->result->{'extraData'};
my $t = $_[0]->result->{'extraData'};
return substr($t,2) if substr($t,0,2) eq '0x';
return lc $t;
}
sub sealers
{
my $t = $_[0]->extradata;
return unless defined $t;
$t = substr $t,64;
$t = substr $t,0,-130;
my @a;
while ( length $t >= 40 )
{
push @a, substr($t, 0, 40);
$t = substr $t, 40;
}
return @a;
}
sub get
{
my ($self, $number) = @_;
my $libbfa = $self->{'libbfa'};
my $hexed = $number =~ /^\d+$/ ? sprintf("0x%x",$number) : $number;
my $content = $libbfa->rpcreq( 'eth_getBlockByNumber', qq("$hexed"), "false");
my $block = block->new( $libbfa );
$block->parse( $content );
return if not exists $block->{'json'};
die $block->error->message if $block->error;
return if not $block->result;
return $block;
}
sub print
{
print scalar($_[0]->sprint),"\n";
}
my $nonce_xlate = {
'0x0000000000000000' => 'SEALER_REM',
'0xffffffffffffffff' => 'SEALER_ADD',
};
sub sprint
{
my ( $self ) = @_;
my $txt = '';
my $lines = 1;
my @sealers = $self->sealers;
if ( @sealers )
{
$txt = sprintf "\r${tools::clearEOS}";
$txt = '';
for my $sealer ( @sealers )
{
$txt .= sprintf
"Confirming signer at epoch: 0x%s\n",
$sealer;
$lines++;
}
}
$txt .= sprintf
'%s block:%s %s',
tools::gmt($self->timestamp),
$self->number,
$self->sealer;
if ( $self->miner !~ /^0x0{40}$/o )
{
# we have auth or drop
my $nonce = $self->nonce;
$nonce = $nonce_xlate->{$nonce} if exists $nonce_xlate->{$nonce};
$txt .= sprintf " %s %s", $nonce, $self->miner;
}
return wantarray ? ($txt, $lines) : $txt;
}
package main;
use JSON;
$| = 1;
chdir "$ENV{BFAHOME}" or die $!;
my $number = shift || 'latest';
my $tools = tools->new;
my %cache;
my $latestvalidblock;
my %signers;
# If we started with 'latest' then subtract 100,
# so we get an updated list of recent signers faster.
my $subtract = $number eq 'latest' ? 100 : 0;
$libbfa = libbfa->new();
my $block = block->new( $libbfa )->get( $number );
die if not defined $block;
$number = $block->number;
my $run_to = $number;
$number -= $subtract;
print ansi::CUP().ansi::ED();
sub determine_colour
{
my $diff = shift;
return ansi::green() . ansi::brightwhite()
if $diff == 0;
return ansi::green()
if $diff < scalar ( keys %signers );
return ansi::bgyellow() . ansi::black()
if $diff < 720; # one hour
return ansi::red();
}
sub colour_split
{
my ($name, $diff, $col, $difficulty)
= @_;
return $name
if $diff == 0;
my $len = length $name;
if ( $diff <= $len )
{
my $part1 = substr $name, 0,$len-$diff;
my $part2 = substr $name, $len-$diff,1;
my $part3 = substr $name, $len-$diff+1;
if ( $difficulty == 2 )
{
$part3 = $part2 . $part3;
$part2 = '';
}
$part2 = ansi::bgyellow() . ansi::black() . $part2 . ansi::normal()
if $part2 ne '';
return
$part1 .
$part2 .
(( $col eq '' ) ? ansi::green() : $col) .
$part3 .
ansi::normal();
}
# diff > len
return
ansi::red() . substr( $name, 0, 1 ) . ansi::normal() .
$col . substr( $name, 1 ) . ansi::normal();
}
my $maxy = 2;
sub presentation_top
{
my $block = shift;
return if not defined $block;
#
my $warning = '';
if ( $block->timestamp + 1800 < time() )
{
$warning = ' ' . ansi::red() . " **NOT RECENT** " . ansi::normal()
}
elsif ( $block->timestamp + 30 < time() )
{
$warning = ' ' . ansi::bgyellow() . ansi::black() . " **NOT RECENT** " . ansi::normal()
}
print
ansi::CUP(),
ansi::normal(),
tools::gmt($block->timestamp),
$warning,
ansi::EL(0),
ansi::CUP($maxy, 1);
}
while ( defined $block || sleep 1 )
{
my $parent = undef;
my $prospect = block->new( $libbfa )->get( $number );
$block = $prospect
if defined $prospect;
presentation_top( $block );
next
if not defined $prospect;
$cache{$number}{'block'} = $block;
$number = $block->number;
if ( exists $cache{ $number - 1 }{'block'} )
{
$parent = $cache{ $number - 1 }{'block'};
# If we do have any information about previous blocks,
# see if the hash matches. If we were in a side branch
# we would eventually get wrong hashes, because we
# ask for blocknumbers (block height).
# This is a good way to determine if we're side tracked.
if ( $parent->hash ne $block->parentHash )
{
# First delete the signer of the to-be-forgotten block
# from the list of 'recent signs'. This will create a
# red 'n/a' to appear in the list. This is more desirable
# than finding the proper previous block of this signer,
# as it makes it more visual that a fork had happened.
my $prevsigner = $cache{ $number - 1 }{'signer'};
delete $signers{$prevsigner};
# If we are side tracked, we'll read backwards
# until we find a match (or until we have nothing cached)
delete $cache{$number};
$number --;
next;
}
}
my $hexed = $number =~ /^\d+$/ ? sprintf("0x%x",$number) : $number;
my $snapjson = $libbfa->rpcreq( 'clique_getSnapshot', qq("$hexed") );
die unless defined $snapjson;
die if $snapjson eq '';
my $snapparsed;
$snapparsed = decode_json( $snapjson );
my $result = $snapparsed->{'result'};
my $thissigner = $result->{'recents'}{$number};
#
# Make sure we only have current signers listed
my %newsigners = ();
for my $this (sort keys %{ $result->{'signers'} } )
{
$newsigners{$this} = $signers{$this}
if exists $signers{$this};
}
%signers = %newsigners;
$signers{$thissigner} = $number;
$cache{$number}{'signer'} = $thissigner;
#
# presentation
my $num_max_width = tools::max( length $number, 3 );
print ansi::CUP(2,1);
foreach my $this ( sort keys %{ $result->{'signers'}} )
{
my $lastnum = exists $signers{$this} ? $signers{$this} : -12345;
my $diff = $number - $lastnum;
my $col = determine_colour( $diff );
my $difficulty = ( exists $signers{$this} and exists $cache{$signers{$this}}{'block'} )
? $cache{$signers{$this}}{'block'}->difficulty
: 0;
my $id = colour_split( $this, $diff, $col, $difficulty );
my $flags = $diff == 0 ? '*' : '';
my $alias = alias::translate( $this );
my $numtxt = (not defined $lastnum or $lastnum < 0) ? 'n/a' : $lastnum;
printf "%s%-1s%s%-${num_max_width}s%s %s%s\n",
$id, $flags, $col, $numtxt, ansi::normal(),
defined $alias ? $alias : '',
ansi::EL(0);
}
$maxy = scalar( keys %{ $result->{'signers'} }) + 2;
print ansi::ED(0), ansi::CUP($maxy, 1);
#
$number = $block->number + 1;
select( undef, undef,undef, 0.1 )
if $number >= $run_to;
}
#!/usr/bin/node
// 20201019 Robert Martin-Legene
/*
Copyright 2020 de la Dirección General de Sistemas Informáticos – Secretaría Legal y Técnica - Nación - Argentina.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/
*/
const Web3 = require( 'web3' );
const fs = require( 'fs' );
var web3 = new Web3();
function usage(exitvalue)
{
console.log("Usage: "+process.argv[1]+" sign <keyfilename> [<password>]");
console.log(" or: "+process.argv[1]+" recover <signature>");
console.log(" or: "+process.argv[1]+" --help");
process.exit(exitvalue);
}
if ( process.argv.length < 3 )
usage( 1 );
if ( process.argv[2] == '--help' )
usage( 0 );
if ( process.argv[2] == 'recover' )
{
if ( process.argv.length != 4 )
usage( 1 );
recover( process.argv[3] );
}
else
if ( process.argv[2] == 'sign' )
{
if ( process.argv.length < 4 || process.argv.length > 5 )
usage( 1 );
sign( process.argv[3], process.argv[4] || '');
}
else
usage( 1 );
function sign( filename, password )
{
var filecontents;
try {
filecontents = fs.readFileSync( filename, 'utf8' );
} catch {
console.error( "Unable to read the file containing the key." );
usage(1);
}
var jsonacct;
try {
jsonacct = JSON.parse( filecontents );
} catch {
console.error( "Unable to parse the contents of the key file." );
usage(1);
}
var account;
try {
account = web3.eth.accounts.decrypt( jsonacct, password );
} catch {
console.error( "Unable to unlock the account with the password specified." );
usage(1);
}
var signed = web3.eth.accounts.sign( "", account.privateKey );
var signature = signed.signature;
console.log( "The signature of the message is\n" + signature );
}
function recover( signature )
{
var acctsigner = web3.eth.accounts.recover( "", signature );
console.log( "The message was signed by\n" + acctsigner );
}
#!/bin/bash
# 20190816 Robert Martin-Legene
# GPL2-only
# BFA tiene 3 bootnodes oficiales.
# Para levantar un bootnode hay que poner la clave en
# este archivo. Se genera la clave con: bootnode -genkey
bootnodekeyfile=${BFANETWORKDIR}/bootnode/key
# Bail out if anything fails.
trap "exit 1" ERR
# Detect children dying
trap "reaper" SIGCHLD
trap "killprocs" SIGTERM
trap "killallprocs" EXIT
unset LOGPIPE PIDIDX ALLPIDIDX TMPDIR SHUTTING_DOWN
declare -A PIDIDX ALLPIDIDX
SHUTTING_DOWN=0
function reaper()
{
local reaped_a_child_status=0
# Find out which child died
for pid in ${!PIDIDX[*]}
do
kill -0 $pid 2>/dev/null && continue
reaped_a_child_status=1
local rc=0
wait $pid || rc=$? || true
local name=${PIDIDX[$pid]}
echo "*** $name (pid $pid) has exited with value $rc"
if [ $name = 'geth' ]; then geth_rc_status=$rc; fi
unset PIDIDX[$pid]
done
# Return if we didn't find out which child died.
if [ $reaped_a_child_status = 0 ]; then return; fi
# Kill all other registered processes
for pid in ${!PIDIDX[*]}
do
kill -0 $pid 2>/dev/null || continue
echo "*** Killing ${PIDIDX[$pid]} (pid $pid)."
kill $pid || true
done
}
function killprocs()
{
SHUTTING_DOWN=1
if [ ${#PIDIDX[*]} -gt 0 ]
then
echo "*** Killing all remaining processes: ${PIDIDX[*]} (${!PIDIDX[*]})."
kill ${!PIDIDX[*]} 2>/dev/null || true
fi
}
function killallprocs()
{
# merge the 2 pid list, to make killing and echoing easier
for pid in ${!ALLPIDIDX[*]}
do
PIDIDX[$pid]=${ALLPIDIDX[$pid]}
unset ALLPIDIDX[$pid]
done
killprocs
if [ -n "$TMPDIR" -a -e "$TMPDIR" ]
then
rm -rf "$TMPDIR"
fi
}
function startgeth()
{
echo "***" Starting geth $*
# "NoPruning=true" means "--gcmode archive"
geth --config ${BFATOML} $* &
gethpid=$!
PIDIDX[${gethpid}]="geth"
}
# You can start as:
# BFAHOME=/home/bfa/bfa singlestart.sh
# singlestart.sh /home/bfa/bfa
if [ -z "${BFAHOME}" -a -n "$1" -a -f "$1" ]
then
export BFAHOME="$1"
fi
if [ -z "${BFAHOME}" ]; then echo "\$BFAHOME not set. Did you source `dirname $0`/env ?" >&2; exit 1; fi
#
if [ -r "${BFAHOME}/bin/env" ]
then
source ${BFAHOME}/bin/env
else
if [ "$VIRTUALIZATION" = "DOCKER" ]
then
# The idea is that the environment already is set by the Docker environment.
touch ${BFAHOME}/bin/env
else
echo "Can't do much without a \$BFAHOME/bin/env file. Maybe you can copy one of these?" >&2
ls -1 ${BFAHOME}/*/env >&2
exit 1
fi
fi
source ${BFAHOME}/bin/libbfa.sh
TMPDIR=$( mktemp -d )
mknod ${TMPDIR}/sleeppipe p
sleep 987654321 > ${TMPDIR}/sleeppipe &
ALLPIDIDX[$!]='sleep'
if [ "$VIRTUALIZATION" = "DOCKER" ]
then
echo
echo
echo
echo
date
echo $0 startup
echo
echo "See log info with \"docker logs\""
else
echo "Logging mostly everything to ${BFANODEDIR}/log"
echo "Consider running: tail -n 1000 -F ${BFANODEDIR}/log"
echo "or: bfalog.sh"
# Docker has it's own logging facility, so we will not use our own
# logging functionality if we're in docker.
echo "*** Setting up logging."
LOGPIPE=${TMPDIR}/logpipe
mknod ${LOGPIPE} p
${BFAHOME}/bin/log.sh ${BFANODEDIR}/log < ${LOGPIPE} &
# Separate pididx for processes we don't want to send signals to
# until in the very end.
ALLPIDIDX[$!]="log.sh"
exec > ${LOGPIPE} 2>&1
fi
function sleep()
{
read -t ${1:-1} < ${TMPDIR}/sleeppipe || true
}
# Start a sync
startgeth --exitwhensynced
echo "*** Starting monitor.js"
monitor.js &
PIDIDX[$!]="monitor.js"
# geth will exit when it has synced, then we kill it's monitor.
# Then wait here for their exit status to get reaped
while [ "${#PIDIDX[*]}" -gt 0 ]; do sleep 1; done
# if it went well, start a normal geth (to run "forever")
test $geth_rc_status = 0 || exit $geth_rc_status
test "$SHUTTING_DOWN" != 0 && exit 0
# regular geth
startgeth
# monitor
echo "*** Starting monitor.js"
monitor.js &
PIDIDX[$!]="monitor.js"
# bootnode
if [ -r "$bootnodekeyfile" ]
then
echo "*** Starting bootnode."
bootnode --nodekey $bootnodekeyfile &
PIDIDX[$!]="bootnode"
fi
while [ "${#PIDIDX[*]}" -gt 0 ]; do sleep 1; done
#!/bin/bash
# (c) 20190524 NIC Argentina, GPLv2
# Robert Martin-Legene <robert@nic.ar>
# See if PATH is set up incorrectly (look for another install of this binary).
printf -v newline '\n'
function find_evm_version()
{
# Remember to set the evm version to be compatible with
# the blockchain. BFA is still at Byzantium (as per 2021).
# The current version is listed in lib/versions with
# keyword chain_evm_version
unset evmvers
local keyword
keyword=chain_evm_version
while read -r
do
if [[ "$REPLY" =~ ^${keyword}= ]]
then
evmvers=${REPLY#${keyword}=}
break
fi
done < "${BFAHOME:-/home/bfa/bfa}/lib/versions"
if [ -z "$evmvers" ]
then
printf -v errortxt '%s\n%s' "$errortxt" "Can not find the keyword ${keyword} in lib/versions"
errortxt=${errortxt#${newline}}
fi
}
function find_first_solc
{
local mypath myname p
mypath=$( realpath "$0" )
myname=${mypath##*/}
unset first_solc
for p in ${PATH//:/ }
do
local checkfile=${p}/${myname}
if [ ! -x "${checkfile}" ]
then
continue
fi
if [ "$(realpath "${checkfile}")" = "$mypath" ]
then
continue
fi
# we found another one, yay!
first_solc=${checkfile}
break
done
if [ -z "$first_solc" ]
then
printf -v errortxt '%s\n%s' "$errortxt" "solc: command not found."
errortxt=${errortxt#${newline}}
fi
}
errortxt=
find_first_solc
find_evm_version
if [ -n "$errortxt" ]
then
echo "$errortxt" >&2
exit 1
fi
exec "${first_solc}" --evm-version="$evmvers" --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,generated-sources,generated-sources-runtime,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,storage-layout,userdoc "$@"
exit 1
......@@ -2,6 +2,7 @@
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
test -e ${BFAHOME}/bin/env || cp -p ${BFANETWORKDIR}/env bin/env
enodeARIU="7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358"
bootARIUv4="enode://${enodeARIU}@[170.210.45.179]:30301"
......@@ -10,31 +11,22 @@ bootARIUv6="enode://${enodeARIU}@[2800:110:44:6300::aad2:2db3]:30301"
enodeUNC="82b66b13d7addcf9ffe1e4e972a105f6ccf50557161c4a0978a5d9ce595ababde609ea8a49897ae89b1d41e90551cb2e9241363238593e950ca68bd5af7c24d6"
bootUNCv4="enode://${enodeUNC}@[200.16.28.28]:30301"
enodeDGSI="59ae768ecdee632e0daceccb6f71b215392eba89230d626573f2fb4e9c0786c9a661027ab7343820ca63d96fe48ffd81ed1bf6e4d512f0ba50ec072c9efd9e4e"
enodeDGSI="ddbf37799e8d33b0c42dddbda713037b17d14696b29ecf424ca3d57bb47db78a467505e22c5f2b709a98c3d55ac8ecbf4610765385317dd51049438f498881c6"
bootDGSIv4="enode://${enodeDGSI}@[200.108.146.100]:30301"
bootnodes="${bootARIUv6},${bootARIUv4},${bootUNCv4},${bootDGSIv4}"
# touch the "miner" file if you are authorized to mine
# If you don't want to restart after touching the file,
# you can use attach.sh and issue the command:
# miner.start()
function getsyncmode
{
local syncmode=$( cat "${BFANODEDIR}/syncmode" 2>/dev/null || true )
syncmode=${syncmode:-fast}
syncmode=${syncmode:-full}
echo "--syncmode ${syncmode}"
}
test -n "${BFAACCOUNT}" ||
fatal "No account defined."
function startbootnode
{
local ERRTEXT="bootnode section failed"
local keyfile=${BFANETWORKDIR}/bootnode/key
local pidfile=${BFANETWORKDIR}/bootnode/bootnode.pid
local pidfile=${BFANETWORKDIR}/bootnode.pid
which bootnode >/dev/null 2>&1 || return 0
test -r $keyfile || return 0
(
......@@ -46,81 +38,166 @@ function startbootnode
then
echo Starting bootnode.
fi
while :
do
echo
echo '***'
echo
bootnode --nodekey $keyfile &
echo $! > $pidfile
wait
sleep 60
done 2>&1 | ${BFAHOME}/bin/log.sh ${BFANETWORKDIR}/bootnode/log &
(
echo ${BASHPID} > ${BFANETWORKDIR}/start-bootnode-loop.pid
while :
do
echo
echo '***'
echo
bootnode --nodekey $keyfile &
echo $! > $pidfile
wait
sleep 60
done
) 2>&1 | ${BFAHOME}/bin/log.sh ${BFANETWORKDIR}/bootnode/log &
) 9>> $pidfile
}
function startmonitor
{
if [ -e "${BFANODEDIR}/opentx" ]
then
echo "Monitor uses functions which are disabled when running an opentx node, so monitor not started."
return
fi
local ERRTEXT="monitor section failed"
local pidfile=${BFANETWORKDIR}/monitor.pid
local pidfile=${BFANODEDIR}/monitor.pid
(
flock --nonblock --exclusive 9 || (
echo "A monitor is already running."
false
) || exit
(
monitor.js &
echo $! > $pidfile
wait
echo ${BASHPID} > ${BFANODEDIR}/start-monitor-loop.pid
while :
do
monitor.js &
echo $! > $pidfile
wait
sleep 10
done
) 2>&1 | ${BFAHOME}/bin/log.sh ${BFANODEDIR}/log &
) 9>> $pidfile
}
function geth_capab
{
geth_version=$( geth --help | sed -n '/^VERSION:/{n;s/^ *//;s/-.*$//;p}' )
# default to a dumb version
test -n "${geth_version}" || geth_version=0.0.0
v_major=${geth_version%%.*}
tmp=${geth_version%.*}
v_minor=${tmp#*.}
v_patch=${geth_version##*.}
unset tmp
#
# Determine capabilities
# 0 legacy
# 1 supports --allow-insecure-unlock
cap=0
if [ "${v_major}" -lt 1 ]
then
cap=0
elif [ "${v_major}" -eq 1 ]
then
if [ "${v_minor}" -eq 8 ]
then
if [ "${v_patch}" -gt 28 ]
then
cap=1
fi
elif [ "${v_minor}" -ge 9 ]
then
cap=1
fi
elif [ "${v_major}" -ge 2 ]
then
cap=1
fi
if [ ${cap} -ge 1 ]
then
flexargs="${flexargs} --allow-insecure-unlock"
fi
}
function geth_args
{
# (re)configure parameters (you never know if they changed)
flexargs="$( getsyncmode )"
geth_capab
#
# the basic modules
local rpcapis="eth,net,web3,clique"
if [ -e "${BFANODEDIR}/opentx" ]
then
local txhostnames dummy
# If you want other hostnames, put them in this file (comma separated)
read txhostnames dummy < ${BFANODEDIR}/opentx
if [ "${txhostnames}" = "" ]
then
# but if you don't put any hostnames, these are the defaults
txhostnames="localhost,opentx.bfa.ar"
fi
flexargs="${flexargs} --rpcvhosts ${txhostnames}"
# INADDR_ANY - listen on all addresses
flexargs="${flexargs} --rpcaddr 0.0.0.0"
# Oh, and don't put your keys in / because we use that as a dummy directory
flexargs="${flexargs} --keystore /"
else
# expose more modules, if we are a private node (localhost'ed)
rpcapis="${rpcapis},admin,miner,personal"
fi
flexargs="${flexargs} --rpcapi ${rpcapis}"
}
function startgeth
{
# Start the node.
local ERRTEXT="geth section failed"
local pidfile=${BFANODEDIR}/geth.pid
which geth >/dev/null 2>&1 || return 0
#
(
flock --nonblock --exclusive 9 || (
echo "A geth is already running."
false
) || exit 1
echo ${BASHPID} > ${BFANODEDIR}/start-loop.pid
if [ -t 1 ]
then
echo Starting geth
echo Logging everything to ${BFANODEDIR}/log
echo Consider running: tail -n 1000 -F ${BFANODEDIR}/log
if [ "$VIRTUALIZATION" = "DOCKER" ]
then
echo Consider running: docker logs --tail 100 -f '<containername>'
else
echo Consider running: bfa tail
fi
fi
while :
do
ERRTEXT="geth"
echo
echo '***'
echo
# (re)configure parameters (you never know if they changed)
flexargs="$( getsyncmode ) --extradata $( extradata )"
set -x
geth \
--datadir ${BFANODEDIR} \
--networkid ${BFANETWORKID} \
--rpc \
--rpcport $rpcport \
--rpcapi "eth,net,web3,admin,clique,miner,personal" \
--port $netport \
--nousb \
--gcmode archive \
--cache 512 \
--verbosity ${BFAVERBOSITY:-3} \
${flexargs} \
--bootnodes "${bootnodes}" &
set +x
echo $! > ${BFANODEDIR}/geth.pid
wait
sleep 60
done 2>&1 | ${BFAHOME}/bin/log.sh ${BFANODEDIR}/log &
) 9>> ${BFANODEDIR}/start-loop.pid
loop_counter=0
(
echo ${BASHPID} > ${BFANODEDIR}/start-geth-loop.pid
echo "*** Loop counter starting at $(( loop_counter + 1 ))"
while :
do
loop_counter=$(( ${loop_counter} + 1 ))
ERRTEXT="geth"
echo
echo '***'
echo "*** loop #${loop_counter}"
echo '***'
echo
geth_args
set -x
geth --config "${BFANETWORKDIR}/config.localhost+full+archive" &
set +x
echo $! > $pidfile
rv=0
wait -n || rv=$?
sleep 60
done
) 2>&1 | ${BFAHOME}/bin/log.sh ${BFANODEDIR}/log &
) 9>> $pidfile
}
startgeth
......
......@@ -59,10 +59,13 @@ function bye( exitcode, msg )
function unlockall()
{
var wallets = new Array();
web3.eth.personal.bfalistWallets()
.then(
function pushone(x)
web3.bfa.personal.listWallets(
function pushone(err,x)
{
if ( err )
bye( 1, err );
if ( x == undefined )
bye( 1, "wallets not defined" );
var failures = 0;
var promises = new Array();
var i = x.length;
......
......@@ -74,7 +74,7 @@ sub hex2string($)
sub rpcreq
{
my ( $opname, @params ) = @_;
my $req = HTTP::Request->new( POST => 'http://127.0.0.1:' . $libbfa->{'rpcport'} );
my $req = HTTP::Request->new( POST => 'http://127.0.0.1:8545' );
$req->content_type('application/json');
my $extra = scalar @params
? sprintf(qq(,\"params\":[%s]), join(',', @params))
......
geth_tag=v1.10.1
solidity_tag=v0.8.3
chain_evm_version=byzantium
0x91c055c6478bd0ad6d19bcb58f5e7ca7b04e67f1 DGSI / Dirección General de Sistemas de Información
0x46991ada2a2544468eb3673524641bf293f23ccc UNC / Universidad Nacional de Córdoba
0x342e1d075d820ed3f9d9a05967ec4055ab23fa1e CABASE BUE / CABASE CABA
0x02665f10cb7b93b4491ac9594d188ef2973c310a CABASE MZA / CABASE Mendoza
0x19fe7b9b3a1bebde77c5374c8e13c623e3d1b5b2 ARIU / Asociación Redes de Interconexión Universitaria
0x2feb6a8876bd9e2116b47834b977506a08ea77bd PNA / Prefectura Naval Argentina
0x9b3ac6719b02ec7bb4820ae178d31c0bbda3a4e0 Everis
0x609043eBDE4a06bD28a1DE238848E8f82cca9C23 UNSJ / Universidad Nacional de San Juan
0xc0310a7b3b25f49b11b901a667208a3eda8d7ceb SyT / Servicios y Telecomunicaciones S.A.
0x998c2651db6f76ca568c0071667d265bcc1b1e98 ASI / Agencia de Sistemas de Información, CABA
0x39170a1ce03729d141dfaf8077c08b72c9cfdd0c IXPBB / IXP Bahia Blanca
0x2388d2cdb2cd6e7722b4af39c3bb406dd31f560e UNR / Universidad Nacional de Rosario
0x401d7a8432caa1025d5f093276cc6ec957b87c00 ONTI / Oficina Nacional de Tecnologías de Información
0x97a47d718eab9d660b10de08ef42bd7fd915b783 UNP / Universidad Nacional de La Plata
0xa14152753515674ae47453bea5e155a20c4ebabc UP / Universidad de Palermo
0xe70fbc9d6be2fe509e4de7317637d8ee83d4f13c CABASE PMY / CABASE Puerto Madryn
0xd1f17aa41354d58940c300ffd79a200944dda2df Marandú
0xd1420aa9dd092f50f68913e9e53b378a68e76ed7 SMGP/OPTIC
0xf36475eb25ba0c825455f150b26e24ab9449a443 SRT / Superintendencia de Riesgos del Trabajo
0x850b30dc584b39275a7ddcaf74a5c0e211523a30 UM / Ultima Milla
0xe191ac3108cb2c5d70d0e978876c048d4ba41b03 ANSV / Agencia Nacional de Seguridad Vial
0x52f8a89484947cd29903b6f52ec6beda69965e38 CABASE PSS / CABASE Posadas
0xb43b53af0db2c3fac788195f4b4dcf2b3d72aa44 IPLAN
0x354779914a94ad428d2b53ae96cce3010bb0ce1e Red Link
0xabeff859aa6b0fb206d840dbf19de970065d4437 Belatrix
0xb3d1209aefbe00c78b2247656e2ddfa9e3897526 ColEscBA / Colegio de Escribanos de la Provincia de Buenos Aires
0x377ab0cd00744dbb07b369cd5c0872dcd362c8f0 UNER / Universidad Nacional de Entre Ríos
[Eth]
NetworkId = 47525974938
SyncMode = "full"
NoPruning = true
DatabaseCache = 4096
[Node.P2P]
BootstrapNodes = ["enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@[2800:110:44:6300::aad2:2db3]:30301", "enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@170.210.45.179:30301", "enode://82b66b13d7addcf9ffe1e4e972a105f6ccf50557161c4a0978a5d9ce595ababde609ea8a49897ae89b1d41e90551cb2e9241363238593e950ca68bd5af7c24d6@200.16.28.28:30301", "enode://ddbf37799e8d33b0c42dddbda713037b17d14696b29ecf424ca3d57bb47db78a467505e22c5f2b709a98c3d55ac8ecbf4610765385317dd51049438f498881c6@200.108.146.100:30301"]
BootstrapNodesV5 = ["enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@[2800:110:44:6300::aad2:2db3]:30301", "enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@170.210.45.179:30301", "enode://82b66b13d7addcf9ffe1e4e972a105f6ccf50557161c4a0978a5d9ce595ababde609ea8a49897ae89b1d41e90551cb2e9241363238593e950ca68bd5af7c24d6@200.16.28.28:30301", "enode://ddbf37799e8d33b0c42dddbda713037b17d14696b29ecf424ca3d57bb47db78a467505e22c5f2b709a98c3d55ac8ecbf4610765385317dd51049438f498881c6@200.108.146.100:30301"]
[Node]
DataDir = "/home/bfa/bfa/network/node"
HTTPHost = "0.0.0.0"
HTTPCors = ["*"]
omitempty = ""
IPCPath = "geth.ipc"
HTTPPort = 8545
HTTPVirtualHosts = ["*"]
HTTPModules = ["net", "web3", "eth", "clique"]
WSHost = "0.0.0.0"
WSPort = 8546
WSModules = ["eth", "net", "web3", "clique"]
[Eth]
NetworkId = 47525974938
SyncMode = "full"
NoPruning = true
DatabaseCache = 4096
[Node.P2P]
BootstrapNodes = ["enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@[2800:110:44:6300::aad2:2db3]:30301", "enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@170.210.45.179:30301", "enode://82b66b13d7addcf9ffe1e4e972a105f6ccf50557161c4a0978a5d9ce595ababde609ea8a49897ae89b1d41e90551cb2e9241363238593e950ca68bd5af7c24d6@200.16.28.28:30301", "enode://ddbf37799e8d33b0c42dddbda713037b17d14696b29ecf424ca3d57bb47db78a467505e22c5f2b709a98c3d55ac8ecbf4610765385317dd51049438f498881c6@200.108.146.100:30301"]
BootstrapNodesV5 = ["enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@[2800:110:44:6300::aad2:2db3]:30301", "enode://7ec4dd9d5e1a2b29d6b60aa9f95677c0c3a5f9306e73d65dd3bcbfda3737a8c509b02d1eab763ce39f18cfe96423df7ee544d6c36191ec17f59ade75bc99d358@170.210.45.179:30301", "enode://82b66b13d7addcf9ffe1e4e972a105f6ccf50557161c4a0978a5d9ce595ababde609ea8a49897ae89b1d41e90551cb2e9241363238593e950ca68bd5af7c24d6@200.16.28.28:30301", "enode://ddbf37799e8d33b0c42dddbda713037b17d14696b29ecf424ca3d57bb47db78a467505e22c5f2b709a98c3d55ac8ecbf4610765385317dd51049438f498881c6@200.108.146.100:30301"]
[Node]
DataDir = "/home/bfa/bfa/network/node"
[{"inputs":[],"stateMutability":"payable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"orderGiver","type":"address"},{"indexed":true,"internalType":"address","name":"victim","type":"address"},{"indexed":false,"internalType":"enum destileria2.UserType","name":"usertype","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"added","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"creator","type":"address"}],"name":"deployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"orderGiver","type":"address"},{"indexed":true,"internalType":"address","name":"victim","type":"address"}],"name":"kicked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"orderGiver","type":"address"},{"indexed":true,"internalType":"address","name":"victim","type":"address"},{"indexed":false,"internalType":"uint256","name":"topuplimit","type":"uint256"}],"name":"limitChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"orderGiver","type":"address"},{"indexed":true,"internalType":"address","name":"victim","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountGiven","type":"uint256"}],"name":"replenished","type":"event"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"topuplimit","type":"uint256"}],"name":"addBeneficiary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"topuplimit","type":"uint256"}],"name":"addDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToUserEntryIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allstopmarker","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"topuplimit","type":"uint256"}],"name":"changeLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"deletedEntries","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBeneficiariesCount","outputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getBeneficiaryLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getDistributorLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum destileria2.UserType","name":"victimtype","type":"uint8"},{"internalType":"address","name":"addr","type":"address"}],"name":"getTopUpLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUserIndexCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getUserType","outputs":[{"internalType":"enum destileria2.UserType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isBeneficiary","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isDistributor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"kickBeneficiary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"kickDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"victim","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"replenish","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"replenishAll","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"accs","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"replenishList","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"userIndex","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"enum destileria2.UserType","name":"userType","type":"uint8"},{"internalType":"uint256","name":"topUpLimit","type":"uint256"},{"internalType":"bool","name":"deleted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum destileria2.UserType","name":"","type":"uint8"}],"name":"userTypeNames","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]