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

Rewrote for better database layout

parent d1780003
No related branches found
No related tags found
No related merge requests found
FROM debian
RUN useradd --create-home --shell /bin/bash bfa && mkdir /home/bfa/collector
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y libjson-perl libnet-dns-perl libdbd-pg-perl libwww-perl vim
COPY collector.pl /home/bfa/collector
RUN chown -R bfa /home/bfa
USER bfa
#CMD /home/bfa/collector/collector.sh
CMD sleep 14d
FROM debian
RUN useradd --create-home --shell /bin/bash bfa && mkdir /home/bfa/collector
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y libjson-perl libnet-dns-perl libdbd-pg-perl libwww-perl vim
COPY collector.pl /home/bfa/collector
RUN chown -R bfa /home/bfa
USER bfa
ENV BFANODE public.47525974938.bfa.martin-legene.dk.
#CMD /home/bfa/collector/collector.sh
CMD sleep 14d
\ No newline at end of file
......@@ -11,8 +11,8 @@ use LWP;
use JSON;
use Net::DNS;
use Net::DNS::Resolver;
use DBI;
use "sql"
use lib '.';
use sql;
# 47525974938;
my $netversion = Math::BigInt->new( "0xb10c4d39a" );
......@@ -20,12 +20,9 @@ my $netversion = Math::BigInt->new( "0xb10c4d39a" );
my $chainid = Math::BigInt->new( "0xbfa2018" );
my $idcounter = 0;
my $ua;
my @endpoints;
my @blockqueue;
my %sealers;
# Set maxruns to 1 if you want to just do a single run to keep
# your database up-to-date.
my $maxruns = 0;
my $sql;
sub info(@)
{
......@@ -54,8 +51,9 @@ sub shorthash($)
sub rpcreq
{
my ( $endpoint,
$opname, @params ) = @_;
my ( $opname, @params )= @_;
$ua = LWP::UserAgent->new( keep_alive => 10 )
if not defined $ua;
# $ua->ssl_opts( 'verify_hostname' => 0 );
my %args = (
jsonrpc => '2.0',
......@@ -83,8 +81,12 @@ sub rpcreq
push @args, $p;
}
my $args = '{' . join(',',@args) . '}';
die 'The environment variable $BFANODE must contain a URL for a ' .
'server to connect to (e.g. https://10.20.30.40/), but it it ' .
'not set. Stopped'
unless defined $ENV{'BFANODE'};
my $res = $ua->post(
$endpoint,
$ENV{BFANODE},
'Content-Type' => 'application/json',
'Content' => $args,
);
......@@ -94,88 +96,17 @@ sub rpcreq
return $json->{'result'};
}
sub endpoint_find
{
my $networknumber = $netversion->bstr;
my $res = Net::DNS::Resolver->new();
my $origin = 'bfa.martin-legene.dk';
my $fqdn = sprintf
'_rpc._tcp.public.%s.%s.',
$networknumber, $origin;
my $reply = $res->query( $fqdn, 'SRV' );
die "No SRV RR found for public endpoints. Stopped"
if not $reply;
my @answer;
@answer = rrsort( 'SRV', 'priority', $reply->answer );
die "DNS SRV query returned no SRV records. Stopped"
if not @answer;
my %protolookup = (
'_rpc' => 'http',
'_rpcs' => 'https',
);
@endpoints = ();
foreach my $answer ( @answer )
{
my $targetname = $answer->target;
info "Publicly open endpoint found at %s.\n", $targetname;
my $r_a = $res->query( $targetname, 'A' );
my $r_aaaa = $res->query( $targetname, 'AAAA' );
my @addresses = ();
push @addresses, rrsort( 'A', $r_a->answer )
if $r_a;
push @addresses, rrsort( 'AAAA', $r_aaaa->answer )
if $r_aaaa;
warn "No address found for $targetname,"
if not @addresses;
my $label1 = (split( /\./, $fqdn, 2 ))[0];
foreach my $address_rr ( @addresses )
{
next
if not exists $protolookup{$label1};
my $proto = $protolookup{$label1};
push @endpoints, sprintf(
'%s://%s:%s',
$proto,
$address_rr->address,
$answer->port
);
}
}
info "Preferring endpoint at %s\n", $endpoints[0];
}
sub getblock
{
my $blockid = $_[0];
my $json;
$blockid = Math::BigInt->new( $blockid )
if ref($blockid) eq ''
and $blockid =~ /^\d+$/;
if ( ref $blockid eq 'Math::BigInt' or $blockid =~ /^(earliest|latest|pending)$/ )
{
my $specific = ref $blockid eq 'Math::BigInt'
? $blockid->as_hex
: $blockid;
$json = rpcreq(
$endpoints[0],
'eth_getBlockByNumber',
$specific, 'false' );
}
else
{
$sth_selectBlockByHash->execute( $blockid );
return if $sth_selectBlockByHash->fetch;
$json = rpcreq(
$endpoints[0],
'eth_getBlockByHash',
$blockid, 'false'
);
}
my $block = shift;
my $rpccmd = $block =~ /^(earliest|latest|pending)$/
? 'eth_getBlockByNumber'
: 'eth_getBlockByHash';
my $json = rpcreq( $rpccmd, $block, 'false' );
die "We should have received some kind of JSON from our RPC call, " .
"but apparently not. Stopped "
if not defined $json;
$sth_selectBlockByHash->execute( $json->{'hash'} );
return if $sth_selectBlockByHash->fetch;
return if $sql->selectBlockByHash( $json->{'hash'} );
foreach my $key (qw(
timestamp gasUsed gasLimit difficulty number totalDifficulty size
)) {
......@@ -190,7 +121,7 @@ sub getblock
$json->{'size'},
$json->{'gasUsed'},
$json->{'gasLimit'};
$sth_insertBlock->execute(
$sql->sth_insertBlock->execute(
lc $json->{'hash'},
lc $json->{'parentHash'},
$json->{'number'},
......@@ -213,11 +144,11 @@ sub getblock
sub getTransByHash
{
my $hash = $_[0];
$sth_selectTransactionByHash->execute( $hash );
return if $sth_selectTransactionByHash->fetch;
my $hash = shift;
# Don't get it via RPC if we already have it in the database
return
if $sql->selectTransactionByHash( $hash );
my $json = rpcreq(
$endpoints[0],
'eth_getTransactionByHash',
$hash
);
......@@ -225,7 +156,6 @@ sub getTransByHash
my $rcpt = undef;
do {
$rcpt = rpcreq(
$endpoints[0],
'eth_getTransactionReceipt',
$hash
);
......@@ -234,7 +164,7 @@ sub getTransByHash
sleep 1;
warn "Couldn't get the transaction receipt for $hash\n";
}
} while not $rcpt and $max++ < 60;
} while not $rcpt and $max++ < 10;
die "Couldn't get the transaction receipt for $hash\n" if not $rcpt;
$json->{'status'} = $rcpt->{'status'};
$json->{'gasUsed'} = $rcpt->{'gasUsed'};
......@@ -251,8 +181,11 @@ sub getTransByHash
shorthash $json->{'from'},
shorthash $json->{'to'};
my $input = $json->{'input'};
$input =~ s/^0x//;
my $inputlen = length($input) / 2;
my $inputlen = 0;
if ( $input =~ /^0x/ )
{
$inputlen = (length($input)-2) / 2;
}
my @args = (
lc $json->{'hash'},
lc $json->{'blockHash'},
......@@ -262,31 +195,26 @@ sub getTransByHash
$json->{'value'},
lc $json->{'from'},
lc $json->{'to'},
$inputlen,
$json->{'gasUsed'},
$json->{'status'},
$json->{'gasUsed'},
$input,
$inputlen,
);
my $function = $sth_insertTransaction;
if ( $json->{'contractAddress'} )
{
push @args, $json->{'contractAddress'};
$function = $sth_insertTransactionWithContractAddress;
$sql->insertTransactionWithContractAddress( @args, lc $json->{'contractAddress'} );
}
else
{
$sql->insertTransaction( @args );
}
$function->execute( @args );
}
sub getTransRcptByHash
{
my $hash = $_[0];
}
sub versioncheck
{
my $n;
my $ok = 1;
$n = Math::BigInt->new(
rpcreq( $endpoints[0], "net_version" )
);
$n = Math::BigInt->new( rpcreq( "net_version" ) );
if ( $n->bcmp( $netversion ) != 0 )
{
warn "Network says it has net.version "
......@@ -298,9 +226,7 @@ sub versioncheck
. ").\n";
$ok = 0;
}
$n = Math::BigInt->new(
rpcreq( $endpoints[0], "eth_chainId" )
);
$n = Math::BigInt->new( rpcreq( "eth_chainId" ) );
if ( $n->bcmp( $chainid ) != 0 )
{
warn sprintf("Network says it has eth.chainId %s (%s). Expected %s (%s).\n", $n->bstr, $n->as_hex, $chainid->bstr, $chainid->as_hex);
......@@ -310,76 +236,56 @@ sub versioncheck
return $ok;
}
sub sealer
{
my $hash = $_[0];
my $row;
die unless defined $hash;
return $sealers{$hash} if exists $sealers{$hash};
$sth_selectsealer->execute( $hash );
return $sealers{$hash} = $row->[0] if $row = $sth_selectsealer->fetch;
$sth_insertsealer->execute( $hash );
$sth_selectsealer->execute( $hash );
die "Apparently we failed to get find/create a new sealer. Stopped"
if not $row = $sth_selectsealer->fetch;
return $sealers{$hash} = $row->[0];
}
sub getsnap
{
my $number = $_[0];
my @blocks;
$number = Math::BigInt->new( $number )
if ref($number) ne 'Math::BigInt';
my $blockhash = $_[0];
my $json = rpcreq(
$endpoints[0],
'clique_getSnapshot',
$number->as_hex
'clique_getSnapshotAtHash',
$blockhash
);
# Sanity check 1 - does the lookup return the block?
return if not defined $json;
my $recents = $json->{'recents'};
return if not defined $recents;
my $hash = $json->{'hash'};
my $count = 0;
while ( exists $recents->{$number->bstr} )
# Blockhash of the requested block - we must use this for the insert
$blockhash = $json->{'hash'};
my $blocknumber = Math::BigInt->new( $json->{'number'} );
my @infonumbers;
while ( not $blocknumber->is_zero )
{
push @blocks, $number->bstr;
$sth_selectBlockByHash->execute( $hash );
my $row = $sth_selectBlockByHash->fetchrow_hashref;
return if not defined $row;
my $newnumber = Math::BigInt->new( $row->{'number'} );
return if $newnumber->bcmp($number) != 0;
my $sealerhash = $recents->{$blocknumber->bstr};
last if not defined $sealerhash;
push @infonumbers, $blocknumber->bstr;
my $ref = $sql->selectBlockByHash( $blockhash );
# Stop if we get to blocks which we still haven't stored in the db
return ( undef, $blocknumber )
if not defined $ref;
# Stop if we got to a block with sealers.
return if defined $row->{'sealer'};
my $sealerhash = $recents->{$number->bstr};
return if not defined $sealerhash;
my $internalid = sealer( $sealerhash );
return if not defined $internalid;
my $rows_affected = $sth_updatewhosealed->execute(
$internalid,
$hash
);
return ( undef, $blocknumber )
if exists $ref->{'sealerAccountId'} and defined $ref->{'sealerAccountId'};
$sql->updateWhoSealed( $sealerhash, $blockhash );
my $parentblockid = $ref->{'parentBlockhashId'};
# For next block...
$hash = $row->{'parentHash'};
$number->bsub(1);
$blockhash = $sql->selectBlockhashById( $parentblockid );
$blocknumber->bsub(1);
}
info "S Snapshot at block # @blocks.\n";
return $number;
info "S Snapshot at block # @infonumbers.\n";
return ($blockhash, $blocknumber);
}
# Find strands of blocks in the database which have their sealer set to NULL
sub allsnaps
{
$sth_selectmaxunknownsigned->execute();
my $unkn = $sth_selectmaxunknownsigned->fetchrow_hashref;
my $number = Math::BigInt->new( $unkn->{'number'} );
my $blockhash = $sql->selectMaxUnknownSigned;
my $committer = 0;
while ( defined $number and not $number->is_zero )
while ( defined $blockhash )
{
$number = getsnap( $number );
$dbh->commit
my $blocknumber;
( $blockhash, $blocknumber )
= getsnap( $blockhash );
$sql->commit
if ( $committer++ % 75 ) == 0;
last if $blocknumber->is_zero;
}
$dbh->commit;
}
sub main
......@@ -387,20 +293,15 @@ sub main
$| = 1;
info "Connecting to database and setting up "
. "prepared statements.\n";
sql::setup;
#
info "Looking for public RPC endpoints of "
. "the BFA.\n";
$ua = LWP::UserAgent->new( keep_alive => 10 );
endpoint_find();
$sql = sql->new();
versioncheck();
info "Looking for orphaned blocks in the "
. "database.\n";
push @blockqueue, sql::listOrphans();
push @blockqueue, sql->listOrphans();
while ( --$maxruns != 0 )
while ( 1 )
{
unshift @blockqueue, "latest";
unshift @blockqueue, 'latest';
while ( @blockqueue )
{
my $maxinarow = 2500;
......@@ -408,7 +309,7 @@ sub main
while @blockqueue and --$maxinarow;
# Find out who signed all blocks
allsnaps();
$dbh->commit;
$sql->commit;
}
sleep 5;
}
......
......@@ -4,105 +4,322 @@
package sql;
use DBI;
use base qw( Class::Accessor );
my @blockqueue;
my $dbh;
my $sth_insertBlock;
my $sth_selectBlockByHash;
my $sth_selectTransactionByHash;
my $sth_insertTransaction;
my $sth_insertTransactionWithContractAddress;
my $sth_insertsealer;
my $sth_selectsealer;
my $sth_updatewhosealed;
my $sth_selectmaxunknownsigned;
sub setup
__PACKAGE__->mk_accessors( 'dbh' );
sub new
{
$dbh = DBI->connect(
my ( $class ) = @_;
my $self = bless {}, ref $class || $class;
$self->dbh( DBI->connect(
'dbi:Pg:dbname=postgres;host=postgres','postgres','onlythelonely',
{AutoCommit=>0,RaiseError=>1}
)
or die DBI::errstr;
$sth_insertBlock = $dbh->prepare(q(
INSERT INTO blocks
( hash, parentHash, number, timestamp,
difficulty, gasUsed, gasLimit, size )
VALUES (?,?,?,?,?,?,?,?)
)) or die $DBI::errstr;
$sth_selectBlockByHash = $dbh->prepare(q(
SELECT parenthash,number,sealer
FROM blocks
WHERE hash=?
)) or die $DBI::errstr;
$sth_selectTransactionByHash
= $dbh->prepare(q(
SELECT "exists"
FROM transactions
WHERE hash=?
)) or die $DBI::errstr;
$sth_insertTransactionWithContractAddress
= $dbh->prepare(q(
INSERT INTO transactions
( hash, blockHash, nonce, gas,
gasPrice, value, _from, _to,
inputlen, gasUsed, status, contractAddress )
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
)) or die $DBI::errstr;
$sth_insertTransaction = $dbh->prepare(q(
INSERT INTO transactions
( hash, blockHash, nonce, gas,
gasPrice, value, _from, _to,
inputlen, gasUsed, status )
VALUES (?,?,?,?,?,?,?,?,?,?,?)
)) or die $DBI::errstr;
$sth_selectsealer = $dbh->prepare(q(
SELECT internalid
FROM sealers
WHERE hash=?
)) or die $DBI::errstr;
$sth_insertsealer = $dbh->prepare(q(
INSERT INTO sealers
( hash )
VALUES (?)
)) or die $DBI::errstr;
$sth_updatewhosealed = $dbh->prepare(q(
UPDATE blocks
SET sealer=?
WHERE hash=?
AND sealer IS NULL
)) or die $DBI::errstr;
$sth_selectmaxunknownsigned
= $dbh->prepare(q(
SELECT number
FROM blocks
WHERE number = (
SELECT MAX(number)
FROM blocks
WHERE sealer IS NULL
)
)) or die $DBI::errstr;
));
return $self;
}
sub listOrphans
sub commit
{
my @results;
my $sth_selectOrphans
= $dbh->prepare(q(
SELECT parentHash,number
my ( $self ) = @_;
$self->dbh->commit;
}
sub listOrphans
{
my ( $self ) = @_;
my $sth = $self->dbh->prepare(q(
SELECT parentBlockhashId,id
FROM blocks
WHERE parentHash NOT IN (
SELECT hash
WHERE parentBlockhashId NOT IN (
SELECT id
FROM blocks
)
)) or die $DBI::errstr;
$sth_selectOrphans->execute();
while ( my $row = $sth_selectOrphans->fetch )
));
my @results;
$sth->execute();
while ( my $row = $sth->fetchrow_arrayref )
{
# Block 0 has it's parent listed as 0x0{64}
next if $row->[0] =~ /^0x0{64}$/;
push @results, $row->[0]
next if $row->[0] =~ /^0x0{64}$/;
push @results, $self->selectBlockhashById( $row->[0] );
}
$sth->finish;
return @results;
}
__PACKAGE__->mk_accessors( 'sth_insertTransactionWithContractAddress' );
sub insertTransactionWithContractAddress
{
my $self = shift;
if ( not defined $self->sth_insertTransactionWithContractAddress )
{
$self->sth_insertTransactionWithContractAddress(
$self->dbh->prepare(q(
INSERT INTO transactions
( hash, blockId, nonce, gas,
gasPrice, value, fromAccountId, toAccountId,
status, gasUsed, input, inputlen,
contractaddressAccountId )
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
))
);
}
$_[1] = $self->selectBlockhashByHash( $_[1] );
$self->sth_insertTransactionWithContractAddress->execute( @_ );
}
__PACKAGE__->mk_accessors( 'sth_insertTransaction' );
sub insertTransaction
{
my $self = shift;
$self->sth_insertTransaction(
$self->dbh->prepare(q(
INSERT INTO transactions
( hash, blockId, nonce, gas,
gasPrice, value, fromAccountId, toAccountId,
status, gasUsed, input, inputlen )
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
))
);
$_[1] = $self->selectBlockhashByHash( $_[1] );
$self->sth_insertTransaction->execute( @_ );
}
__PACKAGE__->mk_accessors( 'sth_selectBlockhashById' );
sub selectBlockhashById
{
my ( $self, $id ) = @_;
if ( not defined $self->sth_selectBlockhashById )
{
$self->sth_selectBlockhashById(
$self->dbh->prepare(q(
SELECT hash
FROM blockhash
WHERE id=?
))
);
}
$self->sth_selectBlockhashById->execute( $id );
my $ref = $self->sth_selectBlockhashById->fetchrow_arrayref;
$self->sth_selectBlockhashById->finish;
return
if not defined $ref;
return $ref->[0];
}
__PACKAGE__->mk_accessors( 'sth_selectBlockhashByHash' );
sub selectBlockhashByHash
{
my ( $self, $hash )= @_;
if ( not defined $self->sth_selectBlockhashByHash )
{
$self->sth_selectBlockhashByHash(
$self->dbh->prepare(q(
SELECT id
FROM blockhash
WHERE hash=?
))
);
}
my $inserts = 0;
while ( 1 )
{
# Try to look up the hash
$self->sth_selectBlockhashByHash->execute( $hash );
my $ref = $self->sth_selectBlockhashByHash->fetchrow_arrayref;
$self->sth_selectBlockhashByHash->finish;
# If found, return it immediately
return $ref->[0] if defined $ref;
# Do maximum 1 attempt to insert a hash
return if $inserts > 0;
# Insert a hash, because it is not already in the database.
$self->insertBlockhash( $hash );
# Avoid loops.
$inserts++;
}
}
__PACKAGE__->mk_accessors( 'sth_insertBlockhash' );
sub insertBlockhash
{
my ( $self, $hash )= @_;
if ( not defined $self->sth_insertBlockhash )
{
$self->sth_insertBlockhash(
$self->dbh->prepare(q(
INSERT INTO blockhash
( hash )
VALUES (?)
))
)
}
$self->sth_insertBlockhash->execute( $hash );
}
__PACKAGE__->mk_accessors( 'sth_selectAccountIdByAddress' );
my %account;
sub selectAccountIdByAddress
{
my ( $self, $address )= @_;
if ( not defined $self->sth_selectAccountIdByAddress )
{
$self->sth_selectAccountIdByAddress(
$self->dbh->prepare(q(
SELECT id
FROM account
WHERE address=?
))
);
}
while ( not exists $account{$address} )
{
# Try to look up the hash
$self->sth_selectAccountIdByAddress->execute( $address );
my $ref = $self->sth_selectAccountIdByAddress->fetchrow_arrayref;
$self->sth_selectAccountIdByAddress->finish;
# If found, return it immediately
if ( defined $ref )
{
$account{$address} = $row->[0];
last;
}
# Do maximum 1 attempt to insert a hash
# (this is actually a failure and should not happen)
return if $inserts > 0;
# Insert a hash, because it is not already in the database.
$self->insertAccount( $hash );
# Avoid loops.
$inserts++;
}
return $account{$address};
}
__PACKAGE__->mk_accessors( 'sth_insertAccount' );
sub insertAccount
{
my ( $self, $address )= @_;
if ( not defined $self->sth_insertAccount )
{
$self->sth_insertAccount(
$self->dbh->prepare(q(
INSERT INTO account
( address )
VALUES (?)
))
);
}
$self->sth_insertAccount->execute( $hash );
}
__PACKAGE__->mk_accessors( 'sth_insertBlock' );
sub insertBlock
{
my ( $self, $hash, $parentHash, $number, $timestamp, $difficulty, $gasUsed, $gasLimit, $size )
= @_;
if ( not defined $self->sth_insertBlock )
{
$self->sth_insertBlock(
$self->dbh->prepare(q(
INSERT INTO blocks
( id, parentBlockhashId, number, timestamp,
difficulty, gasUsed, gasLimit, size )
VALUES (?,?,?,?,?,?,?,?)
))
);
}
my $id = $self->selectBlockhashByHash( $hash );
my $parentid = $self->selectBlockhashByHash( $parentHash );
$self->sth_insertBlock->execute( $id, $parentid, $number, $timestamp, $difficulty, $gasUsed, $gasLimit, $size );
}
__PACKAGE__->mk_accessors( 'sth_selectBlockByHash' );
sub selectBlockByHash
{
my ( $self, $hash) = @_;
if ( not defined $self->sth_selectBlockByHash )
{
$self->sth_selectBlockByHash(
$self->dbh->prepare(q(
SELECT parenthash,number,sealer
FROM blocks
WHERE hash=?
))
);
}
$sth->sth_selectBlockByHash( $hash );
my $row = $sth->selectBlockByHash->fetchrow_hashref;
$sth->selectBlockByHash->finish;
return
if not defined $row;
return $row;
}
__PACKAGE__->mk_accessors( 'sth_selectTransactionByHash' );
sub selectTransactionByHash
{
my ( $self, $hash) = @_;
if ( not defined $self->sth_selectTransactionByHash )
{
$self->sth_selectTransactionByHash(
$self->dbh->prepare(q(
SELECT 1
FROM transactions
WHERE hash=?
))
);
}
$sth->sth_selectTransactionByHash( $hash );
my $row = $sth->selectTransactionByHash->fetchrow_hashref;
$sth->selectTransactionByHash->finish;
return
if not defined $row;
return $row;
}
__PACKAGE__->mk_accessors( 'sth_selectAccountIdByAddress' );
sub updateWhoSealed
{
my ( $self, $sealerhash, $blockhash ) = @_;
if ( not defined $self->sth_updatewhosealed )
{
$self->sth_updatewhosealed(
$self->dbh->prepare(q(
UPDATE blocks
SET sealerAccountId=?
WHERE id=?
AND sealerAccountId IS NULL
))
);
}
my $blockid = $self->selectBlockhashByHash( $blockhash );
my $sealerid = $self->selectSealerByHash( $sealerhash );
$self->sth_updatewhosealed->execute( $sealerid, $blockid );
}
__PACKAGE__->mk_accessors( 'sth_selectmaxunknownsigned' );
sub selectMaxUnknownSigned
{
my ( $self ) = @_;
if ( not defined $self->sth_selectmaxunknownsigned )
{
$self->sth_selectmaxunknownsigned(
$self->dbh->prepare(q(
SELECT id
FROM blocks
WHERE number = (
SELECT MAX(number)
FROM blocks
WHERE sealer IS NULL
)
))
);
}
$self->sth_selectmaxunknownsigned->execute();
my $ref = $self->sth_selectmaxunknownsigned->fetchrow_hashref;
$self->sth_selectmaxunknownsigned->finish();
my $blockid = $ref->{'number'};
my $blockhash = $self->selectBlockhashById( $blockid );
return $blockhash;
}
1;
\ No newline at end of file
1;
......@@ -2,8 +2,7 @@
## POSTGRES SUPERUSER user name is postgres
## POSTGRES SUPERUSER password is onlythelonely
## POSTGRES USER dgsi has password bfa
# defined in postgres/90-add-db-user.sh
## POSTGRES USER leer has password bfa defined in postgres/90-add-db-user.sh
version: '3.4'
......
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