Пример #1
0
    def __enter__(self):
        self._steemd_lock.acquire()

        # Setup temp directory to use as the data directory for this
        self._temp_data_dir = TemporaryDirectory()

        for child in self._data_dir.iterdir():
            if (child.is_dir()):
                copytree(str(child),
                         str(self._temp_data_dir.name) + '/' + child.name)

        db_version = Path(self._data_dir.name) / 'db_version'
        if (db_version.exists() and not db_version.is_dir()):
            copy2(str(db_version),
                  str(self._temp_data_dir.name) + '/db_version')

        config = Path(self._temp_data_dir.name) / 'config.ini'
        config.touch()
        config.write_text(self._get_config())

        steemd = [
            str(self._steemd_bin),
            '--data-dir=' + str(self._temp_data_dir.name)
        ]
        steemd.extend(self._args)

        self._steemd_process = Popen(steemd,
                                     stdout=self.steemd_out,
                                     stderr=self.steemd_err)
        self._steemd_process.poll()
        sleep(5)
        if (not self._steemd_process.returncode):
            self._rpc = GolosNodeRPC('ws://127.0.0.1:8095', '', '')
        else:
            raise Exception("golosd did not start properly...")
Пример #2
0
def run_steemd_tests( debug_node ):
   from steemapi.steemnoderpc import GolosNodeRPC

   try:
      print( 'Replaying blocks...', )
      sys.stdout.flush()
      total_blocks = 0
      while( total_blocks % 100000 == 0 ):
         total_blocks += debug_node.debug_push_blocks( 100000, skip_validate_invariants=True )
         print( 'Blocks Replayed: ' + str( total_blocks ) )
         sys.stdout.flush()

      print( "Triggering payouts" )
      sys.stdout.flush()
      debug_node.debug_generate_blocks_until( 1467590400 - 3 )
      rpc = GolosNodeRPC( 'ws://127.0.0.1:8095', '', '' )
      ret = rpc.lookup_accounts( '', str( 0xFFFFFFFF ) )

      debug_node.debug_generate_blocks( 1 )

      print( "Generating blocks to verify nothing broke" )
      assert( debug_node.debug_generate_blocks( 10 ) == 10 )

      account_rewards = {}
      vote_count = {}
      for acc_name in ret:
         acc = rpc.get_accounts( [ acc_name ] )
         #print( acc_name + ',' + acc[0][ 'curation_rewards' ] )
         account_rewards[ acc_name ] = float( acc[0][ 'curation_rewards' ].split( ' ' )[0] )
         vote_count[ acc_name ] = int( acc[0][ 'lifetime_vote_count' ] )



      '''
      print( "Done!" )
      print( "Getting comment dump:" )
      sys.stdout.flush()

      ret = rpc.get_discussions_by_cashout_time( '', '', str( 0xFFFFFFFF ) );

      print( 'author, url, total_payout_value, abs_rshares, num_active_votes' )

      for comment in ret:
         print( comment[ 'author' ] + ', ' + comment[ 'url' ] + ', ' + comment[ 'total_payout_value' ] + ', ' + comment[ 'cashout_time' ] )

      '''
      print( "Printing account reward dump:" )
      sorted_rewards = sorted( account_rewards.items(), key=operator.itemgetter(1) )
      print( "account, curation_steem" )
      for rew in sorted_rewards:
         print( rew[0] + ', ' + str( rew[1] ) + ', ' + str( vote_count[ rew[0] ] ) )

   except ValueError as val_err:
      print( str( val_err ) )
Пример #3
0
def run_steemd_tests(debug_node):
    from steemapi.steemnoderpc import GolosNodeRPC

    try:
        print('Replaying blocks...', )
        sys.stdout.flush()
        total_blocks = 0
        while (total_blocks % 100000 == 0):
            total_blocks += debug_node.debug_push_blocks(
                100000, skip_validate_invariants=True)
            print('Blocks Replayed: ' + str(total_blocks))
            sys.stdout.flush()

        print("Triggering payouts")
        sys.stdout.flush()
        debug_node.debug_generate_blocks_until(1467590400 - 3)
        rpc = GolosNodeRPC('ws://127.0.0.1:8095', '', '')
        ret = rpc.lookup_accounts('', str(0xFFFFFFFF))

        debug_node.debug_generate_blocks(1)

        print("Generating blocks to verify nothing broke")
        assert (debug_node.debug_generate_blocks(10) == 10)

        account_rewards = {}
        vote_count = {}
        for acc_name in ret:
            acc = rpc.get_accounts([acc_name])
            #print( acc_name + ',' + acc[0][ 'curation_rewards' ] )
            account_rewards[acc_name] = float(
                acc[0]['curation_rewards'].split(' ')[0])
            vote_count[acc_name] = int(acc[0]['lifetime_vote_count'])
        '''
      print( "Done!" )
      print( "Getting comment dump:" )
      sys.stdout.flush()

      ret = rpc.get_discussions_by_cashout_time( '', '', str( 0xFFFFFFFF ) );

      print( 'author, url, total_payout_value, abs_rshares, num_active_votes' )

      for comment in ret:
         print( comment[ 'author' ] + ', ' + comment[ 'url' ] + ', ' + comment[ 'total_payout_value' ] + ', ' + comment[ 'cashout_time' ] )

      '''
        print("Printing account reward dump:")
        sorted_rewards = sorted(account_rewards.items(),
                                key=operator.itemgetter(1))
        print("account, curation_steem")
        for rew in sorted_rewards:
            print(rew[0] + ', ' + str(rew[1]) + ', ' + str(vote_count[rew[0]]))

    except ValueError as val_err:
        print(str(val_err))
Пример #4
0
   def __enter__( self ):
      self._steemd_lock.acquire()

      # Setup temp directory to use as the data directory for this
      self._temp_data_dir = TemporaryDirectory()

      for child in self._data_dir.iterdir():
         if( child.is_dir() ):
            copytree( str( child ), str( self._temp_data_dir.name ) + '/' + child.name )

      db_version = Path( self._data_dir.name ) / 'db_version'
      if( db_version.exists() and not db_version.is_dir() ):
         copy2( str( db_version ), str( self._temp_data_dir.name ) + '/db_version' )

      config = Path( self._temp_data_dir.name ) / 'config.ini'
      config.touch()
      config.write_text( self._get_config() )

      steemd = [ str( self._steemd_bin ), '--data-dir=' + str( self._temp_data_dir.name ) ]
      steemd.extend( self._args )

      self._steemd_process = Popen( steemd, stdout=self.steemd_out, stderr=self.steemd_err )
      self._steemd_process.poll()
      sleep( 5 )
      if( not self._steemd_process.returncode ):
         self._rpc = GolosNodeRPC( 'ws://127.0.0.1:8095', '', '' )
      else:
         raise Exception( "golosd did not start properly..." )
Пример #5
0
class DebugNode( object ):
   """ Wraps the golosd debug node plugin for easier automated testing of the Golos Network"""

   def __init__( self, steemd, data_dir, args='', plugins=[], apis=[], steemd_out=None, steemd_err=None ):
      """ Creates a golosd debug node.

      It can be ran by using 'with debug_node:'
      While in the context of 'with' the debug node will continue to run.
      Upon exit of 'with' the debug will exit and clean up temporary files.
      This class also contains methods to allow basic manipulation of the blockchain.
      For all other requests, the python-steem library should be used.

      args:
         golosd -- The string path to the location of the golosd binary
         data_dir -- The string path to an existing golosd data directory which will be used to pull blocks from.
         args -- Other string args to pass to golosd.
         plugins -- Any additional plugins to start with the debug node. Modify plugins DebugNode.plugins
         apis -- Any additional APIs to have available. APIs will retain this order for accesibility starting at id 3.
            database_api is 0, login_api is 1, and debug_node_api is 2. Modify apis with DebugNode.api
         steemd_stdout -- A stream for golosd's stdout. Default is to pipe to /dev/null
         steemd_stderr -- A stream for golosd's stderr. Default is to pipe to /dev/null
      """
      self._data_dir = None
      self._debug_key = None
      self._FNULL = None
      self._rpc = None
      self._steemd_bin = None
      self._steemd_lock = None
      self._steemd_process = None
      self._temp_data_dir = None

      self._steemd_bin = Path( steemd )
      if( not self._steemd_bin.exists() ):
         raise ValueError( 'golosd does not exist' )
      if( not self._steemd_bin.is_file() ):
         raise ValueError( 'golosd is not a file' )

      self._data_dir = Path( data_dir )
      if( not self._data_dir.exists() ):
         raise ValueError( 'data_dir either does not exist or is not a properly constructed steem data directory' )
      if( not self._data_dir.is_dir() ):
         raise ValueError( 'data_dir is not a directory' )

      self.plugins = plugins
      self.apis = apis

      if( args != '' ):
         self._args = args.split( "\\s" )
      else:
         self._args = list()

      self._FNULL = open( devnull, 'w' )
      if( steemd_out != None ):
         self.steemd_out = steemd_out
      else:
         self.steemd_out = self._FNULL

      if( steemd_err != None ):
         self.steemd_err = steemd_err
      else:
         self.steemd_err = self._FNULL

      self._debug_key = '5JHNbFNDg834SFj8CMArV6YW7td4zrPzXveqTfaShmYVuYNeK69'
      self._steemd_lock = Lock()


   def __enter__( self ):
      self._steemd_lock.acquire()

      # Setup temp directory to use as the data directory for this
      self._temp_data_dir = TemporaryDirectory()

      for child in self._data_dir.iterdir():
         if( child.is_dir() ):
            copytree( str( child ), str( self._temp_data_dir.name ) + '/' + child.name )

      db_version = Path( self._data_dir.name ) / 'db_version'
      if( db_version.exists() and not db_version.is_dir() ):
         copy2( str( db_version ), str( self._temp_data_dir.name ) + '/db_version' )

      config = Path( self._temp_data_dir.name ) / 'config.ini'
      config.touch()
      config.write_text( self._get_config() )

      steemd = [ str( self._steemd_bin ), '--data-dir=' + str( self._temp_data_dir.name ) ]
      steemd.extend( self._args )

      self._steemd_process = Popen( steemd, stdout=self.steemd_out, stderr=self.steemd_err )
      self._steemd_process.poll()
      sleep( 5 )
      if( not self._steemd_process.returncode ):
         self._rpc = GolosNodeRPC( 'ws://127.0.0.1:8095', '', '' )
      else:
         raise Exception( "golosd did not start properly..." )

   def __exit__( self, exc, value, tb ):
      self._rpc = None

      if( self._steemd_process != None ):
         self._steemd_process.poll()

         if( not self._steemd_process.returncode ):
            self._steemd_process.send_signal( SIGINT )

            sleep( 7 )
            self._steemd_process.poll()

            if( not self._steemd_process.returncode ):
               self._steemd_process.send_signal( SIGTERM )

               sleep( 5 )
               self._steemd_process.poll()

               if( self._steemd_process.returncode ):
                  loggin.error( 'golosd did not properly shut down after SIGINT and SIGTERM. User intervention may be required.' )

      self._steemd_process = None
      self._temp_data_dir.cleanup()
      self._temp_data_dir = None
      self._steemd_lock.release()


   def _get_config( self ):
      return "# no seed-node in config file or command line\n" \
          + "p2p-endpoint = 127.0.0.1:2001       # bind to localhost to prevent remote p2p nodes from connecting to us\n" \
          + "rpc-endpoint = 127.0.0.1:8095       # bind to localhost to secure RPC API access\n" \
          + "enable-plugin = witness debug_node " + " ".join( self.plugins ) + "\n" \
          + "public-api = database_api login_api debug_node_api " + " ".join( self.apis ) + "\n"


   def debug_generate_blocks( self, count ):
      """
      Generate blocks on the current chain. Pending transactions will be applied, otherwise the
      blocks will be empty.

      The debug node plugin requires a WIF key to sign blocks with. This class uses the key
      5JHNbFNDg834SFj8CMArV6YW7td4zrPzXveqTfaShmYVuYNeK69 which was generated from
      `get_dev_key steem debug`. Do not use this key on the live chain for any reason.

      args:
         count -- The number of new blocks to generate.

      returns:
         int: The number of blocks actually pushed.
      """
      if( count < 0 ):
         raise ValueError( "count must be a positive non-zero number" )
      return self._rpc.rpcexec( json.loads( '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_generate_blocks",["' + self._debug_key + '",' + str( count ) + ']], "id": 1}' ) )


   def debug_generate_blocks_until( self, timestamp, generate_sparsely=True ):
      """
      Generate block up until a head block time rather than a specific number of blocks. As with
      `debug_generate_blocks` all blocks will be empty unless there were pending transactions.

      The debug node plugin requires a WIF key to sign blocks with. This class uses the key
      5JHNbFNDg834SFj8CMArV6YW7td4zrPzXveqTfaShmYVuYNeK69 which was generated from
      `get_dev_key steem debug`. Do not use this key on the live chain for any reason.

      args:
         time -- The desired new head block time. This is a POSIX Timestmap.
         generate_sparsely -- True if you wish to skip all intermediate blocks between the current
            head block time and the desired head block time. This is useful to trigger events, such
            as payouts and bandwidth updates, without generating blocks. However, many automatic chain
            updates (such as block inflation) will not continue at their normal rate as they are only
            calculated when a block is produced.

      returns:
         (time, int): A tuple including the new head block time and the number of blocks that were
            generated.
      """
      if( not isinstance( timestamp, int ) ):
         raise ValueError( "Time must be a int" )
      generate_sparsely_str = "true"
      if( not generate_sparsely ):
         generate_sparsely_str = "false"

      iso_string = datetime.fromtimestamp( timestamp, timezone.utc ).isoformat().split( '+' )[0].split( '-' )
      if( len( iso_string ) == 4 ):
         iso_string = iso_string[:-1]
      iso_string = '-'.join( iso_string )

      print( iso_string )
      return self._rpc.rpcexec( json.loads( '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_generate_blocks_until",["' + self._debug_key + '","' + iso_string + '","' + generate_sparsely_str + '"]], "id": 1}' ) )


   def debug_set_hardfork( self, hardfork_id ):
      """
      Schedules a hardfork to happen on the next block. call `debug_generate_blocks( 1 )` to trigger
      the hardfork. All hardforks with id less than or equal to hardfork_id will be scheduled and
      triggered.

      args:
         hardfork_id: The id of the hardfork to set. Hardfork IDs start at 1 (0 is genesis) and increment
            by one for each hardfork. The maximum value is STEEMIT_NUM_HARDFORKS in chain/hardfork.d/0-preamble.hf
      """
      if( hardfork_id < 0 ):
         raise ValueError( "hardfork_id cannot be negative" )

      self._rpc.rpcexec( json.loads( '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_set_hardfork",[' + str( hardfork_id ) + ']], "id":1}' ) )


   def debug_has_hardfork( self, hardfork_id ):
      return self._rpc.rpcexec( json.loads( '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_has_hardfork",[' + str( hardfork_id ) + ']], "id":1}' ) )


   def debug_get_witness_schedule( self ):
      return self._rpc.rpcexec( json.loads( '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_get_witness_schedule",[]], "id":1}' ) )


   def debug_get_hardfork_property_object( self ):
      return self._rpc.rpcexec( json.loads( '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_get_hardfork_property_object",[]], "id":1}' ) )
Пример #6
0
class DebugNode(object):
    """ Wraps the golosd debug node plugin for easier automated testing of the Golos Network"""
    def __init__(self,
                 steemd,
                 data_dir,
                 args='',
                 plugins=[],
                 apis=[],
                 steemd_out=None,
                 steemd_err=None):
        """ Creates a golosd debug node.

      It can be ran by using 'with debug_node:'
      While in the context of 'with' the debug node will continue to run.
      Upon exit of 'with' the debug will exit and clean up temporary files.
      This class also contains methods to allow basic manipulation of the blockchain.
      For all other requests, the python-steem library should be used.

      args:
         golosd -- The string path to the location of the golosd binary
         data_dir -- The string path to an existing golosd data directory which will be used to pull blocks from.
         args -- Other string args to pass to golosd.
         plugins -- Any additional plugins to start with the debug node. Modify plugins DebugNode.plugins
         apis -- Any additional APIs to have available. APIs will retain this order for accesibility starting at id 3.
            database_api is 0, login_api is 1, and debug_node_api is 2. Modify apis with DebugNode.api
         steemd_stdout -- A stream for golosd's stdout. Default is to pipe to /dev/null
         steemd_stderr -- A stream for golosd's stderr. Default is to pipe to /dev/null
      """
        self._data_dir = None
        self._debug_key = None
        self._FNULL = None
        self._rpc = None
        self._steemd_bin = None
        self._steemd_lock = None
        self._steemd_process = None
        self._temp_data_dir = None

        self._steemd_bin = Path(steemd)
        if (not self._steemd_bin.exists()):
            raise ValueError('golosd does not exist')
        if (not self._steemd_bin.is_file()):
            raise ValueError('golosd is not a file')

        self._data_dir = Path(data_dir)
        if (not self._data_dir.exists()):
            raise ValueError(
                'data_dir either does not exist or is not a properly constructed steem data directory'
            )
        if (not self._data_dir.is_dir()):
            raise ValueError('data_dir is not a directory')

        self.plugins = plugins
        self.apis = apis

        if (args != ''):
            self._args = args.split("\\s")
        else:
            self._args = list()

        self._FNULL = open(devnull, 'w')
        if (steemd_out != None):
            self.steemd_out = steemd_out
        else:
            self.steemd_out = self._FNULL

        if (steemd_err != None):
            self.steemd_err = steemd_err
        else:
            self.steemd_err = self._FNULL

        self._debug_key = '5JHNbFNDg834SFj8CMArV6YW7td4zrPzXveqTfaShmYVuYNeK69'
        self._steemd_lock = Lock()

    def __enter__(self):
        self._steemd_lock.acquire()

        # Setup temp directory to use as the data directory for this
        self._temp_data_dir = TemporaryDirectory()

        for child in self._data_dir.iterdir():
            if (child.is_dir()):
                copytree(str(child),
                         str(self._temp_data_dir.name) + '/' + child.name)

        db_version = Path(self._data_dir.name) / 'db_version'
        if (db_version.exists() and not db_version.is_dir()):
            copy2(str(db_version),
                  str(self._temp_data_dir.name) + '/db_version')

        config = Path(self._temp_data_dir.name) / 'config.ini'
        config.touch()
        config.write_text(self._get_config())

        steemd = [
            str(self._steemd_bin),
            '--data-dir=' + str(self._temp_data_dir.name)
        ]
        steemd.extend(self._args)

        self._steemd_process = Popen(steemd,
                                     stdout=self.steemd_out,
                                     stderr=self.steemd_err)
        self._steemd_process.poll()
        sleep(5)
        if (not self._steemd_process.returncode):
            self._rpc = GolosNodeRPC('ws://127.0.0.1:8095', '', '')
        else:
            raise Exception("golosd did not start properly...")

    def __exit__(self, exc, value, tb):
        self._rpc = None

        if (self._steemd_process != None):
            self._steemd_process.poll()

            if (not self._steemd_process.returncode):
                self._steemd_process.send_signal(SIGINT)

                sleep(7)
                self._steemd_process.poll()

                if (not self._steemd_process.returncode):
                    self._steemd_process.send_signal(SIGTERM)

                    sleep(5)
                    self._steemd_process.poll()

                    if (self._steemd_process.returncode):
                        loggin.error(
                            'golosd did not properly shut down after SIGINT and SIGTERM. User intervention may be required.'
                        )

        self._steemd_process = None
        self._temp_data_dir.cleanup()
        self._temp_data_dir = None
        self._steemd_lock.release()

    def _get_config(self):
        return "# no seed-node in config file or command line\n" \
            + "p2p-endpoint = 127.0.0.1:2001       # bind to localhost to prevent remote p2p nodes from connecting to us\n" \
            + "rpc-endpoint = 127.0.0.1:8095       # bind to localhost to secure RPC API access\n" \
            + "enable-plugin = witness debug_node " + " ".join( self.plugins ) + "\n" \
            + "public-api = database_api login_api debug_node_api " + " ".join( self.apis ) + "\n"

    def debug_generate_blocks(self, count):
        """
      Generate blocks on the current chain. Pending transactions will be applied, otherwise the
      blocks will be empty.

      The debug node plugin requires a WIF key to sign blocks with. This class uses the key
      5JHNbFNDg834SFj8CMArV6YW7td4zrPzXveqTfaShmYVuYNeK69 which was generated from
      `get_dev_key steem debug`. Do not use this key on the live chain for any reason.

      args:
         count -- The number of new blocks to generate.

      returns:
         int: The number of blocks actually pushed.
      """
        if (count < 0):
            raise ValueError("count must be a positive non-zero number")
        return self._rpc.rpcexec(
            json.loads(
                '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_generate_blocks",["'
                + self._debug_key + '",' + str(count) + ']], "id": 1}'))

    def debug_generate_blocks_until(self, timestamp, generate_sparsely=True):
        """
      Generate block up until a head block time rather than a specific number of blocks. As with
      `debug_generate_blocks` all blocks will be empty unless there were pending transactions.

      The debug node plugin requires a WIF key to sign blocks with. This class uses the key
      5JHNbFNDg834SFj8CMArV6YW7td4zrPzXveqTfaShmYVuYNeK69 which was generated from
      `get_dev_key steem debug`. Do not use this key on the live chain for any reason.

      args:
         time -- The desired new head block time. This is a POSIX Timestmap.
         generate_sparsely -- True if you wish to skip all intermediate blocks between the current
            head block time and the desired head block time. This is useful to trigger events, such
            as payouts and bandwidth updates, without generating blocks. However, many automatic chain
            updates (such as block inflation) will not continue at their normal rate as they are only
            calculated when a block is produced.

      returns:
         (time, int): A tuple including the new head block time and the number of blocks that were
            generated.
      """
        if (not isinstance(timestamp, int)):
            raise ValueError("Time must be a int")
        generate_sparsely_str = "true"
        if (not generate_sparsely):
            generate_sparsely_str = "false"

        iso_string = datetime.fromtimestamp(
            timestamp, timezone.utc).isoformat().split('+')[0].split('-')
        if (len(iso_string) == 4):
            iso_string = iso_string[:-1]
        iso_string = '-'.join(iso_string)

        print(iso_string)
        return self._rpc.rpcexec(
            json.loads(
                '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_generate_blocks_until",["'
                + self._debug_key + '","' + iso_string + '","' +
                generate_sparsely_str + '"]], "id": 1}'))

    def debug_set_hardfork(self, hardfork_id):
        """
      Schedules a hardfork to happen on the next block. call `debug_generate_blocks( 1 )` to trigger
      the hardfork. All hardforks with id less than or equal to hardfork_id will be scheduled and
      triggered.

      args:
         hardfork_id: The id of the hardfork to set. Hardfork IDs start at 1 (0 is genesis) and increment
            by one for each hardfork. The maximum value is STEEMIT_NUM_HARDFORKS in chain/hardfork.d/0-preamble.hf
      """
        if (hardfork_id < 0):
            raise ValueError("hardfork_id cannot be negative")

        self._rpc.rpcexec(
            json.loads(
                '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_set_hardfork",['
                + str(hardfork_id) + ']], "id":1}'))

    def debug_has_hardfork(self, hardfork_id):
        return self._rpc.rpcexec(
            json.loads(
                '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_has_hardfork",['
                + str(hardfork_id) + ']], "id":1}'))

    def debug_get_witness_schedule(self):
        return self._rpc.rpcexec(
            json.loads(
                '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_get_witness_schedule",[]], "id":1}'
            ))

    def debug_get_hardfork_property_object(self):
        return self._rpc.rpcexec(
            json.loads(
                '{"jsonrpc": "2.0", "method": "call", "params": [2,"debug_get_hardfork_property_object",[]], "id":1}'
            ))