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):
|
def __init__(self, src):
|
||||||
self.hash = src['hash']
|
self.hash = src['hash']
|
||||||
|
try:
|
||||||
self.number = int(strip_0x(src['number']), 16)
|
self.number = int(strip_0x(src['number']), 16)
|
||||||
|
except TypeError:
|
||||||
|
self.number = int(src['number'])
|
||||||
self.txs = src['transactions']
|
self.txs = src['transactions']
|
||||||
self.block_src = src
|
self.block_src = src
|
||||||
|
try:
|
||||||
self.timestamp = int(strip_0x(src['timestamp']), 16)
|
self.timestamp = int(strip_0x(src['timestamp']), 16)
|
||||||
|
except TypeError:
|
||||||
|
self.timestamp = int(src['timestamp'])
|
||||||
|
|
||||||
|
|
||||||
def src(self):
|
def src(self):
|
||||||
|
@ -36,6 +36,7 @@ from chainlib.eth.gas import (
|
|||||||
OverrideGasOracle,
|
OverrideGasOracle,
|
||||||
balance,
|
balance,
|
||||||
)
|
)
|
||||||
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
logging.basicConfig(level=logging.WARNING)
|
logging.basicConfig(level=logging.WARNING)
|
||||||
logg = logging.getLogger()
|
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('-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('-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('-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('-v', action='store_true', help='Be verbose')
|
||||||
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
|
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
|
||||||
argparser.add_argument('address', type=str, help='Account address')
|
argparser.add_argument('address', type=str, help='Account address')
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
# local imports
|
# 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.address import to_checksum
|
||||||
from chainlib.eth.connection import EthHTTPConnection
|
from chainlib.eth.connection import EthHTTPConnection
|
||||||
from chainlib.eth.tx import count
|
from chainlib.eth.tx import count
|
||||||
|
from chainlib.chain import ChainSpec
|
||||||
from crypto_dev_signer.keystore.dict import DictKeystore
|
from crypto_dev_signer.keystore.dict import DictKeystore
|
||||||
from crypto_dev_signer.eth.signer import ReferenceSigner as EIP155Signer
|
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.connection import EthHTTPConnection
|
||||||
from chainlib.eth.tx import Tx
|
from chainlib.eth.tx import Tx
|
||||||
from chainlib.eth.block import Block
|
from chainlib.eth.block import Block
|
||||||
|
from chainlib.chain import ChainSpec
|
||||||
|
|
||||||
logging.basicConfig(level=logging.WARNING)
|
logging.basicConfig(level=logging.WARNING)
|
||||||
logg = logging.getLogger()
|
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('-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('-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('-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('-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('--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('-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
|
# standard imports
|
||||||
import logging
|
import logging
|
||||||
import enum
|
import enum
|
||||||
|
import re
|
||||||
|
|
||||||
# external imports
|
# external imports
|
||||||
import coincurve
|
import coincurve
|
||||||
@ -270,20 +271,56 @@ class TxFactory:
|
|||||||
|
|
||||||
class Tx:
|
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):
|
def __init__(self, src, block=None, rcpt=None):
|
||||||
|
logg.debug('src {}'.format(src))
|
||||||
|
self.src = self.src_normalize(src)
|
||||||
self.index = -1
|
self.index = -1
|
||||||
|
tx_hash = add_0x(src['hash'])
|
||||||
if block != None:
|
if block != None:
|
||||||
self.index = int(strip_0x(src['transactionIndex']), 16)
|
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)
|
self.value = int(strip_0x(src['value']), 16)
|
||||||
|
except TypeError:
|
||||||
|
self.value = int(src['value'])
|
||||||
|
try:
|
||||||
self.nonce = int(strip_0x(src['nonce']), 16)
|
self.nonce = int(strip_0x(src['nonce']), 16)
|
||||||
self.hash = strip_0x(src['hash'])
|
except TypeError:
|
||||||
|
self.nonce = int(src['nonce'])
|
||||||
address_from = strip_0x(src['from'])
|
address_from = strip_0x(src['from'])
|
||||||
self.gasPrice = int(strip_0x(src['gasPrice']), 16)
|
try:
|
||||||
self.gasLimit = int(strip_0x(src['gas']), 16)
|
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.outputs = [to_checksum(address_from)]
|
||||||
self.contract = None
|
self.contract = None
|
||||||
|
|
||||||
|
try:
|
||||||
inpt = src['input']
|
inpt = src['input']
|
||||||
|
except KeyError:
|
||||||
|
inpt = src['data']
|
||||||
|
|
||||||
if inpt != '0x':
|
if inpt != '0x':
|
||||||
inpt = strip_0x(inpt)
|
inpt = strip_0x(inpt)
|
||||||
else:
|
else:
|
||||||
@ -310,8 +347,31 @@ class Tx:
|
|||||||
self.apply_receipt(rcpt)
|
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):
|
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:
|
if status_number == 1:
|
||||||
self.status = Status.SUCCESS
|
self.status = Status.SUCCESS
|
||||||
elif status_number == 0:
|
elif status_number == 0:
|
||||||
@ -323,6 +383,7 @@ class Tx:
|
|||||||
if contract_address != None:
|
if contract_address != None:
|
||||||
self.contract = contract_address
|
self.contract = contract_address
|
||||||
self.logs = rcpt['logs']
|
self.logs = rcpt['logs']
|
||||||
|
self.gas_used = int(rcpt['gasUsed'], 16)
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -338,19 +399,25 @@ nonce {}
|
|||||||
gasPrice {}
|
gasPrice {}
|
||||||
gasLimit {}
|
gasLimit {}
|
||||||
input {}
|
input {}
|
||||||
status {}
|
|
||||||
""".format(
|
""".format(
|
||||||
self.hash,
|
self.hash,
|
||||||
self.outputs[0],
|
self.outputs[0],
|
||||||
self.inputs[0],
|
self.inputs[0],
|
||||||
self.value,
|
self.value,
|
||||||
self.nonce,
|
self.nonce,
|
||||||
self.gasPrice,
|
self.gas_price,
|
||||||
self.gasLimit,
|
self.gas_limit,
|
||||||
self.payload,
|
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:
|
if self.contract != None:
|
||||||
s += """contract {}
|
s += """contract {}
|
||||||
""".format(
|
""".format(
|
||||||
|
@ -80,6 +80,11 @@ class TestRPCConnection(RPCConnection):
|
|||||||
return jsonrpc_result(r, error_parser)
|
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):
|
def eth_getBlockByNumber(self, p):
|
||||||
b = bytes.fromhex(strip_0x(p[0]))
|
b = bytes.fromhex(strip_0x(p[0]))
|
||||||
n = int.from_bytes(b, 'big')
|
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]
|
[metadata]
|
||||||
name = chainlib
|
name = chainlib
|
||||||
version = 0.0.2a1
|
version = 0.0.2a6
|
||||||
description = Generic blockchain access library and tooling
|
description = Generic blockchain access library and tooling
|
||||||
author = Louis Holbrook
|
author = Louis Holbrook
|
||||||
author_email = dev@holbrook.no
|
author_email = dev@holbrook.no
|
||||||
@ -41,3 +41,5 @@ console_scripts =
|
|||||||
eth-transfer = chainlib.eth.runnable.transfer:main
|
eth-transfer = chainlib.eth.runnable.transfer:main
|
||||||
eth-get = chainlib.eth.runnable.get:main
|
eth-get = chainlib.eth.runnable.get:main
|
||||||
eth-decode = chainlib.eth.runnable.decode: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