From 227f1320860575c1fb65cc83fe14979f0ce878e7 Mon Sep 17 00:00:00 2001
From: Robert Martin-Legene <robert@nic.ar>
Date: Mon, 23 Jul 2018 17:14:47 -0300
Subject: [PATCH] A neat (non-perfect) script for monitoring the blockchain

---
 bin/walker.pl | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 377 insertions(+)
 create mode 100755 bin/walker.pl

diff --git a/bin/walker.pl b/bin/walker.pl
new file mode 100755
index 0000000..95c6f85
--- /dev/null
+++ b/bin/walker.pl
@@ -0,0 +1,377 @@
+#!/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;
+}
-- 
GitLab