Merge branch 'lash/chain-stat' into 'master'
Add chain stat, info cli See merge request nolash/chainlib!2
This commit is contained in:
		
						commit
						72f49841f8
					
				@ -42,10 +42,16 @@ class Block:
 | 
			
		||||
    
 | 
			
		||||
    def __init__(self, src):
 | 
			
		||||
        self.hash = src['hash']
 | 
			
		||||
        self.number = int(strip_0x(src['number']), 16)
 | 
			
		||||
        try:
 | 
			
		||||
            self.number = int(strip_0x(src['number']), 16)
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            self.number = int(src['number'])
 | 
			
		||||
        self.txs = src['transactions']
 | 
			
		||||
        self.block_src = src
 | 
			
		||||
        self.timestamp = int(strip_0x(src['timestamp']), 16)
 | 
			
		||||
        try:
 | 
			
		||||
            self.timestamp = int(strip_0x(src['timestamp']), 16)
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            self.timestamp = int(src['timestamp'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def src(self):
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ from chainlib.eth.gas import (
 | 
			
		||||
        OverrideGasOracle,
 | 
			
		||||
        balance,
 | 
			
		||||
        )
 | 
			
		||||
from chainlib.chain import ChainSpec
 | 
			
		||||
 | 
			
		||||
logging.basicConfig(level=logging.WARNING)
 | 
			
		||||
logg = logging.getLogger()
 | 
			
		||||
@ -48,7 +49,6 @@ argparser.add_argument('-p', '--provider', dest='p', default=default_eth_provide
 | 
			
		||||
argparser.add_argument('-a', '--token-address', dest='a', type=str, help='Token address. If not set, will return gas balance')
 | 
			
		||||
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string')
 | 
			
		||||
argparser.add_argument('-u', '--unsafe', dest='u', action='store_true', help='Auto-convert address to checksum adddress')
 | 
			
		||||
argparser.add_argument('--abi-dir', dest='abi_dir', type=str, default=default_abi_dir, help='Directory containing bytecode and abi (default {})'.format(default_abi_dir))
 | 
			
		||||
argparser.add_argument('-v', action='store_true', help='Be verbose')
 | 
			
		||||
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
 | 
			
		||||
argparser.add_argument('address', type=str, help='Account address')
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
# local imports
 | 
			
		||||
from chainlib.eth.address import to_checksum
 | 
			
		||||
from chainlib.eth.address import to_checksum_address
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
print(to_checksum(sys.argv[1]))
 | 
			
		||||
print(to_checksum_address(sys.argv[1]))
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ import logging
 | 
			
		||||
from chainlib.eth.address import to_checksum
 | 
			
		||||
from chainlib.eth.connection import EthHTTPConnection
 | 
			
		||||
from chainlib.eth.tx import count
 | 
			
		||||
from chainlib.chain import ChainSpec
 | 
			
		||||
from crypto_dev_signer.keystore.dict import DictKeystore
 | 
			
		||||
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -34,6 +34,7 @@ from chainlib.jsonrpc import (
 | 
			
		||||
from chainlib.eth.connection import EthHTTPConnection
 | 
			
		||||
from chainlib.eth.tx import Tx
 | 
			
		||||
from chainlib.eth.block import Block
 | 
			
		||||
from chainlib.chain import ChainSpec
 | 
			
		||||
 | 
			
		||||
logging.basicConfig(level=logging.WARNING)
 | 
			
		||||
logg = logging.getLogger()
 | 
			
		||||
@ -45,7 +46,6 @@ argparser = argparse.ArgumentParser()
 | 
			
		||||
argparser.add_argument('-p', '--provider', dest='p', default=default_eth_provider, type=str, help='Web3 provider url (http only)')
 | 
			
		||||
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string')
 | 
			
		||||
argparser.add_argument('-t', '--token-address', dest='t', type=str, help='Token address. If not set, will return gas balance')
 | 
			
		||||
argparser.add_argument('-t', '--token-address', dest='t', type=str, help='Token address. If not set, will return gas balance')
 | 
			
		||||
argparser.add_argument('-u', '--unsafe', dest='u', action='store_true', help='Auto-convert address to checksum adddress')
 | 
			
		||||
argparser.add_argument('--abi-dir', dest='abi_dir', type=str, default=default_abi_dir, help='Directory containing bytecode and abi (default {})'.format(default_abi_dir))
 | 
			
		||||
argparser.add_argument('-v', action='store_true', help='Be verbose')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										136
									
								
								chainlib/eth/runnable/info.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								chainlib/eth/runnable/info.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,136 @@
 | 
			
		||||
#!python3
 | 
			
		||||
 | 
			
		||||
"""Token balance query script
 | 
			
		||||
 | 
			
		||||
.. moduleauthor:: Louis Holbrook <dev@holbrook.no>
 | 
			
		||||
.. pgp:: 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
# standard imports
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import json
 | 
			
		||||
import argparse
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
# third-party imports
 | 
			
		||||
from hexathon import (
 | 
			
		||||
        add_0x,
 | 
			
		||||
        strip_0x,
 | 
			
		||||
        even,
 | 
			
		||||
        )
 | 
			
		||||
import sha3
 | 
			
		||||
from eth_abi import encode_single
 | 
			
		||||
 | 
			
		||||
# local imports
 | 
			
		||||
from chainlib.eth.address import (
 | 
			
		||||
        to_checksum_address,
 | 
			
		||||
        is_checksum_address,
 | 
			
		||||
        )
 | 
			
		||||
from chainlib.jsonrpc import (
 | 
			
		||||
        jsonrpc_template,
 | 
			
		||||
        jsonrpc_result,
 | 
			
		||||
        )
 | 
			
		||||
from chainlib.eth.block import block_latest
 | 
			
		||||
from chainlib.eth.tx import count
 | 
			
		||||
from chainlib.eth.erc20 import ERC20
 | 
			
		||||
from chainlib.eth.connection import EthHTTPConnection
 | 
			
		||||
from chainlib.eth.gas import (
 | 
			
		||||
        OverrideGasOracle,
 | 
			
		||||
        balance,
 | 
			
		||||
        price,
 | 
			
		||||
        )
 | 
			
		||||
from chainlib.chain import ChainSpec
 | 
			
		||||
 | 
			
		||||
logging.basicConfig(level=logging.WARNING)
 | 
			
		||||
logg = logging.getLogger()
 | 
			
		||||
 | 
			
		||||
default_abi_dir = os.environ.get('ETH_ABI_DIR', '/usr/share/local/cic/solidity/abi')
 | 
			
		||||
default_eth_provider = os.environ.get('ETH_PROVIDER', 'http://localhost:8545')
 | 
			
		||||
 | 
			
		||||
argparser = argparse.ArgumentParser()
 | 
			
		||||
argparser.add_argument('-p', '--provider', dest='p', default=default_eth_provider, type=str, help='Web3 provider url (http only)')
 | 
			
		||||
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, default='evm:ethereum:1', help='Chain specification string')
 | 
			
		||||
argparser.add_argument('-H', '--human', dest='human', action='store_true', help='Use human-friendly formatting')
 | 
			
		||||
argparser.add_argument('-u', '--unsafe', dest='u', action='store_true', help='Auto-convert address to checksum adddress')
 | 
			
		||||
argparser.add_argument('-v', action='store_true', help='Be verbose')
 | 
			
		||||
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
 | 
			
		||||
argparser.add_argument('-y', '--key-file', dest='y', type=str, help='Include summary for keyfile')
 | 
			
		||||
argparser.add_argument('address', nargs='?', type=str, help='Include summary for address (conflicts with -y)')
 | 
			
		||||
args = argparser.parse_args()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if args.vv:
 | 
			
		||||
    logg.setLevel(logging.DEBUG)
 | 
			
		||||
elif args.v:
 | 
			
		||||
    logg.setLevel(logging.INFO)
 | 
			
		||||
 | 
			
		||||
signer = None
 | 
			
		||||
holder_address = None
 | 
			
		||||
if args.address != None:
 | 
			
		||||
    if not args.u and is_checksum_address(args.address):
 | 
			
		||||
        raise ValueError('invalid checksum address {}'.format(args.address))
 | 
			
		||||
        holder_address = add_0x(args.address)
 | 
			
		||||
elif args.y != None:
 | 
			
		||||
    f = open(args.y, 'r')
 | 
			
		||||
    o = json.load(f)
 | 
			
		||||
    f.close()
 | 
			
		||||
    holder_address = add_0x(to_checksum_address(o['address']))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#if holder_address != None:
 | 
			
		||||
#    passphrase_env = 'ETH_PASSPHRASE'
 | 
			
		||||
#    if args.env_prefix != None:
 | 
			
		||||
#        passphrase_env = args.env_prefix + '_' + passphrase_env
 | 
			
		||||
#    passphrase = os.environ.get(passphrase_env)
 | 
			
		||||
#    logg.error('pass {}'.format(passphrase_env))
 | 
			
		||||
#    if passphrase == None:
 | 
			
		||||
#        logg.warning('no passphrase given')
 | 
			
		||||
#        passphrase=''
 | 
			
		||||
#
 | 
			
		||||
#    holder_address = None
 | 
			
		||||
#    keystore = DictKeystore()
 | 
			
		||||
#    if args.y != None:
 | 
			
		||||
#        logg.debug('loading keystore file {}'.format(args.y))
 | 
			
		||||
#        signer_address = keystore.import_keystore_file(args.y, password=passphrase)
 | 
			
		||||
#        logg.debug('now have key for signer address {}'.format(signer_address))
 | 
			
		||||
#    signer = EIP155Signer(keystore)
 | 
			
		||||
 | 
			
		||||
conn = EthHTTPConnection(args.p)
 | 
			
		||||
gas_oracle = OverrideGasOracle(conn)
 | 
			
		||||
 | 
			
		||||
token_symbol = 'eth'
 | 
			
		||||
 | 
			
		||||
chain_spec = ChainSpec.from_chain_str(args.i)
 | 
			
		||||
 | 
			
		||||
human = args.human
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    o = block_latest()
 | 
			
		||||
    r = conn.do(o)
 | 
			
		||||
    n = int(r, 16)
 | 
			
		||||
    if human:
 | 
			
		||||
        n = format(n, ',')
 | 
			
		||||
    sys.stdout.write('Block: {}\n'.format(n))
 | 
			
		||||
 | 
			
		||||
    o = price()
 | 
			
		||||
    r = conn.do(o)
 | 
			
		||||
    n = int(r, 16)
 | 
			
		||||
    if human:
 | 
			
		||||
        n = format(n, ',')
 | 
			
		||||
    sys.stdout.write('Gasprice: {}\n'.format(n))
 | 
			
		||||
 | 
			
		||||
    if holder_address != None:
 | 
			
		||||
        o = count(holder_address)
 | 
			
		||||
        r = conn.do(o)
 | 
			
		||||
        n = int(r, 16)
 | 
			
		||||
        sys.stdout.write('Address: {}\n'.format(holder_address))
 | 
			
		||||
        sys.stdout.write('Nonce: {}\n'.format(n))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main()
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
# standard imports
 | 
			
		||||
import logging
 | 
			
		||||
import enum
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
# external imports
 | 
			
		||||
import coincurve
 | 
			
		||||
@ -270,20 +271,56 @@ class TxFactory:
 | 
			
		||||
 | 
			
		||||
class Tx:
 | 
			
		||||
 | 
			
		||||
    re_camel_snake = re.compile(r'([a-z0-9]+)([A-Z])')
 | 
			
		||||
 | 
			
		||||
    # TODO: force tx type schema parser (whether expect hex or int etc)
 | 
			
		||||
    def __init__(self, src, block=None, rcpt=None):
 | 
			
		||||
        logg.debug('src {}'.format(src))
 | 
			
		||||
        self.src = self.src_normalize(src)
 | 
			
		||||
        self.index = -1
 | 
			
		||||
        tx_hash = add_0x(src['hash'])
 | 
			
		||||
        if block != None:
 | 
			
		||||
            self.index = int(strip_0x(src['transactionIndex']), 16)
 | 
			
		||||
        self.value = int(strip_0x(src['value']), 16)
 | 
			
		||||
        self.nonce = int(strip_0x(src['nonce']), 16)
 | 
			
		||||
        self.hash = strip_0x(src['hash'])
 | 
			
		||||
            i = 0
 | 
			
		||||
            for tx in block.txs:
 | 
			
		||||
                tx_hash_block = None
 | 
			
		||||
                try:
 | 
			
		||||
                    tx_hash_block = tx['hash']
 | 
			
		||||
                except TypeError:
 | 
			
		||||
                    tx_hash_block = add_0x(tx)
 | 
			
		||||
                logg.debug('tx {} cmp {}'.format(tx, tx_hash))
 | 
			
		||||
                if tx_hash_block == tx_hash:
 | 
			
		||||
                    self.index = i
 | 
			
		||||
                    break
 | 
			
		||||
                i += 1
 | 
			
		||||
            if self.index == -1:
 | 
			
		||||
                raise AttributeError('tx {} not found in block {}'.format(tx_hash, block.hash))
 | 
			
		||||
        self.block = block
 | 
			
		||||
        self.hash = strip_0x(tx_hash)
 | 
			
		||||
        try:
 | 
			
		||||
            self.value = int(strip_0x(src['value']), 16)
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            self.value = int(src['value'])
 | 
			
		||||
        try:
 | 
			
		||||
            self.nonce = int(strip_0x(src['nonce']), 16)
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            self.nonce = int(src['nonce'])
 | 
			
		||||
        address_from = strip_0x(src['from'])
 | 
			
		||||
        self.gasPrice = int(strip_0x(src['gasPrice']), 16)
 | 
			
		||||
        self.gasLimit = int(strip_0x(src['gas']), 16)
 | 
			
		||||
        try:
 | 
			
		||||
            self.gas_price = int(strip_0x(src['gasPrice']), 16)
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            self.gas_price = int(src['gasPrice'])
 | 
			
		||||
        try:
 | 
			
		||||
            self.gas_limit = int(strip_0x(src['gas']), 16)
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            self.gas_limit = int(src['gas'])
 | 
			
		||||
        self.outputs = [to_checksum(address_from)]
 | 
			
		||||
        self.contract = None
 | 
			
		||||
 | 
			
		||||
        inpt = src['input']
 | 
			
		||||
        try:
 | 
			
		||||
            inpt = src['input']
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            inpt = src['data']
 | 
			
		||||
 | 
			
		||||
        if inpt != '0x':
 | 
			
		||||
            inpt = strip_0x(inpt)
 | 
			
		||||
        else:
 | 
			
		||||
@ -308,10 +345,33 @@ class Tx:
 | 
			
		||||
 | 
			
		||||
        if rcpt != None:
 | 
			
		||||
            self.apply_receipt(rcpt)
 | 
			
		||||
   
 | 
			
		||||
  
 | 
			
		||||
    
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def src_normalize(self, src):
 | 
			
		||||
        src_normal = {}
 | 
			
		||||
        for k in src.keys():
 | 
			
		||||
            s = ''
 | 
			
		||||
            right_pos = 0
 | 
			
		||||
            for m in self.re_camel_snake.finditer(k):
 | 
			
		||||
                g = m.group(0)
 | 
			
		||||
                s += g[:len(g)-1]
 | 
			
		||||
                s += '_' + g[len(g)-1].lower()
 | 
			
		||||
                right_pos = m.span()[1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            s += k[right_pos:]
 | 
			
		||||
            src_normal[k] = src[k]
 | 
			
		||||
            if s != k:
 | 
			
		||||
                logg.debug('adding snake {} for camel {}'.format(s, k))
 | 
			
		||||
                src_normal[s] = src[k]
 | 
			
		||||
 | 
			
		||||
        return src_normal
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def apply_receipt(self, rcpt):
 | 
			
		||||
        status_number = int(strip_0x(rcpt['status']))
 | 
			
		||||
        logg.debug('rcpt {}'.format(rcpt))
 | 
			
		||||
        status_number = int(rcpt['status'], 16)
 | 
			
		||||
        if status_number == 1:
 | 
			
		||||
            self.status = Status.SUCCESS
 | 
			
		||||
        elif status_number == 0:
 | 
			
		||||
@ -323,6 +383,7 @@ class Tx:
 | 
			
		||||
        if contract_address != None:
 | 
			
		||||
            self.contract = contract_address
 | 
			
		||||
        self.logs = rcpt['logs']
 | 
			
		||||
        self.gas_used = int(rcpt['gasUsed'], 16)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
@ -338,19 +399,25 @@ nonce {}
 | 
			
		||||
gasPrice {}
 | 
			
		||||
gasLimit {}
 | 
			
		||||
input {}
 | 
			
		||||
status {}
 | 
			
		||||
""".format(
 | 
			
		||||
        self.hash,
 | 
			
		||||
        self.outputs[0],
 | 
			
		||||
        self.inputs[0],
 | 
			
		||||
        self.value,
 | 
			
		||||
        self.nonce,
 | 
			
		||||
        self.gasPrice,
 | 
			
		||||
        self.gasLimit,
 | 
			
		||||
        self.gas_price,
 | 
			
		||||
        self.gas_limit,
 | 
			
		||||
        self.payload,
 | 
			
		||||
        self.status.name,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if self.status != Status.PENDING:
 | 
			
		||||
            s += """gasUsed {}
 | 
			
		||||
""".format(
 | 
			
		||||
        self.gas_used,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        s += 'status ' + self.status.name
 | 
			
		||||
 | 
			
		||||
        if self.contract != None:
 | 
			
		||||
            s += """contract {}
 | 
			
		||||
""".format(
 | 
			
		||||
 | 
			
		||||
@ -80,6 +80,11 @@ class TestRPCConnection(RPCConnection):
 | 
			
		||||
        return jsonrpc_result(r, error_parser)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def eth_blockNumber(self, p):
 | 
			
		||||
        block = self.backend.get_block_by_number('latest')
 | 
			
		||||
        return block['number']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def eth_getBlockByNumber(self, p):
 | 
			
		||||
        b = bytes.fromhex(strip_0x(p[0]))
 | 
			
		||||
        n = int.from_bytes(b, 'big')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								chainlib/stat.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								chainlib/stat.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
class ChainStat:
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.block_timestamp_last = None
 | 
			
		||||
        self.block_avg_aggregate = None
 | 
			
		||||
        self.block_avg_count = -1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def block_apply(self, block):
 | 
			
		||||
        if self.block_timestamp_last == None:
 | 
			
		||||
            self.block_timestamp_last = block.timestamp
 | 
			
		||||
        
 | 
			
		||||
        aggregate = block.timestamp - self.block_timestamp_last
 | 
			
		||||
 | 
			
		||||
        if self.block_avg_aggregate == None:
 | 
			
		||||
            self.block_avg_aggregate = float(aggregate)
 | 
			
		||||
        else:
 | 
			
		||||
            self.block_avg_aggregate *= self.block_avg_count
 | 
			
		||||
            self.block_avg_aggregate += block.timestamp - self.block_timestamp_last
 | 
			
		||||
            self.block_avg_aggregate /= (self.block_avg_count + 1)
 | 
			
		||||
 | 
			
		||||
        print('aggr {}'.format(type(self.block_avg_aggregate)))
 | 
			
		||||
        self.block_avg_count += 1
 | 
			
		||||
 | 
			
		||||
        self.block_timestamp_last = block.timestamp
 | 
			
		||||
 | 
			
		||||
    def block_average(self):
 | 
			
		||||
        return self.block_avg_aggregate
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
[metadata]
 | 
			
		||||
name = chainlib
 | 
			
		||||
version = 0.0.2a1
 | 
			
		||||
version = 0.0.2a6
 | 
			
		||||
description = Generic blockchain access library and tooling
 | 
			
		||||
author = Louis Holbrook
 | 
			
		||||
author_email = dev@holbrook.no
 | 
			
		||||
@ -41,3 +41,5 @@ console_scripts =
 | 
			
		||||
	eth-transfer = chainlib.eth.runnable.transfer:main
 | 
			
		||||
	eth-get = chainlib.eth.runnable.get:main
 | 
			
		||||
	eth-decode = chainlib.eth.runnable.decode:main
 | 
			
		||||
	eth-info = chainlib.eth.runnable.info:main
 | 
			
		||||
	eth = chainlib.eth.runnable.info:main
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								tests/test_stat.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tests/test_stat.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
			
		||||
# standard imports
 | 
			
		||||
import unittest
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
# external imports
 | 
			
		||||
from chainlib.stat import Stat
 | 
			
		||||
from chainlib.eth.block import Block
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestStat(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_block(self):
 | 
			
		||||
 | 
			
		||||
        s = ChainStat()
 | 
			
		||||
 
 | 
			
		||||
        d = datetime.datetime.utcnow() - datetime.timedelta(seconds=30)
 | 
			
		||||
        block_a = Block({
 | 
			
		||||
            'timestamp': d.timestamp(),
 | 
			
		||||
            'hash': None,
 | 
			
		||||
            'transactions': [],
 | 
			
		||||
            'number': 41,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        d = datetime.datetime.utcnow()
 | 
			
		||||
        block_b = Block({
 | 
			
		||||
            'timestamp': d.timestamp(),
 | 
			
		||||
            'hash': None,
 | 
			
		||||
            'transactions': [],
 | 
			
		||||
            'number': 42,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        s.block_apply(block_a)
 | 
			
		||||
        s.block_apply(block_b)
 | 
			
		||||
        self.assertEqual(s.block_average(), 30.0)
 | 
			
		||||
 | 
			
		||||
        d = datetime.datetime.utcnow() + datetime.timedelta(seconds=10)
 | 
			
		||||
        block_c = Block({
 | 
			
		||||
            'timestamp': d.timestamp(),
 | 
			
		||||
            'hash': None,
 | 
			
		||||
            'transactions': [],
 | 
			
		||||
            'number': 43,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        s.block_apply(block_c)
 | 
			
		||||
        self.assertEqual(s.block_average(), 20.0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user