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

A neat (non-perfect) script for monitoring the blockchain

parent 12935569
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/perl -w
use strict;
use warnings;
use IO::File;
use Math::BigInt;
use Carp;
$Carp::Verbose = 1;
die "\$BFAHOME not set. Did you source bfa/bin/env ?\n"
unless exists $ENV{BFAHOME};
chdir "$ENV{BFAHOME}" or die $!;
package tools;
my $rpcport;
my $ua = LWP::UserAgent->new;
sub new
{
my $class = shift;
return bless {@_}, ref $class || $class;
}
sub wait
{
my $i = ++$_[0]->{'wait'};
printf "%s%c[D", substr('|/-\\', $i%4, 1), 27;
sleep 1;
}
sub cat($)
{
my ($filename) = @_;
my $fh = IO::File->new($filename) or return;
return join('', $fh->getlines);
}
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) );
my $c='.';
$c=chr($i) if $i >= 32 and $i <= 127;
$txt .= $c;
$msg=substr $msg, 2;
}
return $txt;
}
sub rpcreq
{
my ( $opname, @params ) = @_;
if ( not defined $rpcport )
{
$rpcport = tools::cat "$ENV{BFAHOME}/network5445/node1/rpcport" or die;
}
my $req = HTTP::Request->new( POST => "http://127.0.0.1:$rpcport" );
$req->content_type('application/json');
my $extra = scalar @params
? sprintf(qq(,\"params\":[%s]), join(',', @params))
: '';
$req->content( qq({"jsonrpc":"2.0","method":"${opname}"${extra},"id":1}));
my $res = $ua->request($req);
die $res->status_line
unless $res->is_success;
return $res->content;
}
package balance;
use JSON;
sub new
{
my ($class, $acct, $at) = @_;
my $self = bless {}, ref $class || $class;
$self->get( $acct, $at ) if defined $acct;
return $self;
}
sub acct
{
my ($self, $acct) = @_;
if ( defined $acct )
{
$acct = '0x'.$acct if $acct !~ /^0x/;
$self->{'_acct'} = $acct;
}
return unless exists $self->{'_acct'};
return $self->{'_acct'};
}
sub at
{
my ($self, $at) = @_;
$self->{'_at'} = $at if defined $at;
return sprintf('0x%x', $self->{'_at'}) if exists $self->{'_at'};
return 'latest';
}
sub get
{
my ($self, $acct, $at) = @_;
$self->acct($acct) if defined $acct;
$self->at($at) if defined $at;
my @params = ( sprintf(qq("%s","%s"),$self->acct,$self->at) );
my $content = tools::rpcreq( 'eth_getBalance', @params );
my $json;
eval { $json = decode_json( $content ) };
my $error = error->new( $content );
if ( $error )
{
my $msg = '';
return 'NOTFOUND' if $error->message =~ /^missing trie node /;
die join(' * ', @params, $content);
return;
}
die if not exists $json->{'result'};
die if not defined $json->{'result'};
return Math::BigInt->from_hex( $json->{'result'} );
}
package error;
use JSON;
sub new
{
my ($class, $json_in) = @_;
my $json;
eval { $json = decode_json( $json_in ) };
return unless defined $json;
return unless exists $json->{'error'};
my $self = bless {
'_code' => undef,
'_message' => undef,
}, ref $class || $class;
$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, $json_raw ) = @_;
return unless defined $json_raw;
return if $json_raw eq '';
my $self = bless {}, ref $class || $class;
$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 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 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 vanity
{
my $t = $_[0]->extradata;
return unless defined $t;
return substr($t,0,64);
}
sub clear
{
my $t = shift;
return unless defined $t;
return substr($t,2) if substr($t,0,2) eq '0x';
return $t;
}
sub signature
{
my ($self) = @_;
my $t = $self->extradata;
return unless defined $t;
return substr($t, -130);
my $res = $self->result;
die unless defined $res;
use Data::Dumper;
die Dumper($res).
clear($res->{'parentHash'}).
clear($res->{'sha3Uncles'}).
clear($res->{'miner'}).
clear($res->{'stateRoot'}).
clear($res->{'transactionsRoot'}).
clear($res->{'receiptsRoot'}).
clear($res->{'logsBloom'}).
clear($res->{'difficulty'}).
clear($res->{'number'}).
clear($res->{'gasLimit'}).
clear($res->{'gasUsed'}).
clear($res->{'timestamp'}).
substr( clear($res->{'extraData'}), 0, -130 ). ('0' x 130).
clear($res->{'mixHash'}).
clear($self->{'nonce'});
}
sub get($;$)
{
my ($number) = @_;
$number = sprintf('0x%x', $number) if $number ne 'earliest';
my $cachefile = "cache/block.$number";
my $block;
if ( -r $cachefile )
{
$block = block->new( tools::cat $cachefile );
return $block
if defined $block and not $block->error;
# We delete the cache file if we couldn't use the data in it.
unlink $cachefile;
# and then we continue to fetch it
}
my $content = tools::rpcreq( 'eth_getBlockByNumber', qq("$number"), "false");
$block = block->new( $content );
return if not defined $block;
return if not exists $block->{'json'};
die $block->error->message
if $block->error;
return if not $block->result;
my $fh = IO::File->new( $cachefile, 'w' )
or die $!;
$fh->print( $block->{'json_raw'} );
$fh->close;
return $block;
}
package main;
my $nonce_xlate = {
'0x0000000000000000' => 'SEALER_REM',
'0xffffffffffffffff' => 'SEALER_ADD',
};
$| = 1;
mkdir 'cache';
my $number = shift || tools::cat 'walker.block.last' || 'earliest';
my $tools = tools->new;
while ( 1 )
{
my $block = block::get($number);
if ( not defined $block )
{
$tools->wait();
next;
}
my $txt = sprintf
'%s block:%s gaslimit:%s Vanity: %s',
tools::gmt($block->timestamp),
$block->number,
$block->gasLimit,
tools::hex2string($block->vanity);
if ( $block->miner !~ /^0x0{40}$/ )
{
# we have auth or drop
my $nonce = $block->nonce;
$nonce = $nonce_xlate->{$nonce} if exists $nonce_xlate->{$nonce};
$txt .= sprintf " %s %s\n", $nonce, $block->miner;
}
else
{
my @sealers = $block->sealers;
if ( @sealers )
{
printf "\r%s%c[J\n", $txt, 27;
$txt = '';
for my $sealer ( @sealers )
{
printf "Confirming signer at epoch: 0x%s with an ETH balance of %s\n", $sealer, balance->new->get($sealer, $block->number);
}
}
}
printf "\r%s%c[J", $txt, 27 if $txt ne '';
#$block->signature;
$number = $block->number + 1;
}
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