139 lines
4.0 KiB
Python
139 lines
4.0 KiB
Python
from pybtc.functions.tools import rh2s
|
|
from pybtc.functions.tools import var_int_to_int, var_int_len
|
|
from pybtc.functions.tools import read_var_int
|
|
from pybtc.functions.hash import double_sha256
|
|
from pybtc.transaction import Transaction
|
|
from pybtc.functions.block import bits_to_target, target_to_difficulty
|
|
from struct import unpack, pack
|
|
import io
|
|
import time
|
|
import sys
|
|
from collections import OrderedDict
|
|
|
|
|
|
def get_stream(stream):
|
|
if not isinstance(stream, io.BytesIO):
|
|
if isinstance(stream, str):
|
|
stream = bytes.fromhex(stream)
|
|
if isinstance(stream, bytes):
|
|
stream = io.BytesIO(stream)
|
|
else:
|
|
raise TypeError("object should be bytes or HEX encoded string")
|
|
return stream
|
|
|
|
|
|
def decode_block_tx(block):
|
|
s = get_stream(block)
|
|
b = dict()
|
|
b["amount"] = 0
|
|
b["size"] = len(block)
|
|
b["strippedSize"] = 80
|
|
b["version"] = unpack("<L", s.read(4))[0]
|
|
b["versionHex"] = pack(">L", b["version"]).hex()
|
|
b["previousBlockHash"] = rh2s(s.read(32))
|
|
b["merkleRoot"] = rh2s(s.read(32))
|
|
b["time"] = unpack("<L", s.read(4))[0]
|
|
b["bits"] = s.read(4)
|
|
b["target"] = bits_to_target(unpack("<L", b["bits"])[0])
|
|
b["targetDifficulty"] = target_to_difficulty(b["target"])
|
|
b["target"] = b["target"].to_bytes(32, byteorder="little")
|
|
b["nonce"] = unpack("<L", s.read(4))[0]
|
|
s.seek(-80, 1)
|
|
b["header"] = s.read(80).hex()
|
|
b["bits"] = rh2s(b["bits"])
|
|
b["target"] = rh2s(b["target"])
|
|
b["hash"] = double_sha256(b["header"], hex=0)
|
|
b["hash"] = rh2s(b["hash"])
|
|
b["rawTx"] = dict()
|
|
b["tx"] = list()
|
|
for i in range(var_int_to_int(read_var_int(s))):
|
|
b["rawTx"][i] = Transaction(s, format="raw")
|
|
b["tx"].append(rh2s(b["rawTx"][i]["txId"]))
|
|
b["amount"] += b["rawTx"][i]["amount"]
|
|
b["strippedSize"] += b["rawTx"][i]["bSize"]
|
|
b["strippedSize"] += var_int_len(len(b["tx"]))
|
|
b["weight"] = b["strippedSize"] * 3 + b["size"]
|
|
return b
|
|
|
|
|
|
class Cache():
|
|
def __init__(self, max_size=1000000, clear_tail=True):
|
|
self._store = OrderedDict()
|
|
self._store_size = 0
|
|
self._max_size = max_size
|
|
self.clear_tail = False
|
|
self.clear_tail_auto = clear_tail
|
|
self._requests = 0
|
|
self._hit = 0
|
|
|
|
def set(self, key, value):
|
|
self._check_limit()
|
|
self._store[key] = value
|
|
self._store_size += sys.getsizeof(value) + sys.getsizeof(key)
|
|
|
|
def _check_limit(self):
|
|
if self._store_size >= self._max_size:
|
|
self.clear_tail = True
|
|
if self.clear_tail and self.clear_tail_auto:
|
|
if self._store_size >= int(self._max_size * 0.75):
|
|
try:
|
|
[self.pop_last() for i in range(20)]
|
|
except:
|
|
pass
|
|
else:
|
|
self.clear_tail = False
|
|
|
|
def get(self, key):
|
|
self._requests += 1
|
|
try:
|
|
i = self._store[key]
|
|
self._hit += 1
|
|
return i
|
|
except:
|
|
return None
|
|
|
|
def pop(self, key):
|
|
self._requests += 1
|
|
try:
|
|
data = self._store.pop(key)
|
|
self._store_size -= sys.getsizeof(data) + sys.getsizeof(key)
|
|
self._hit += 1
|
|
return data
|
|
except:
|
|
return None
|
|
|
|
def remove(self, key):
|
|
try:
|
|
data = self._store.pop(key)
|
|
self._store_size -= sys.getsizeof(data) + sys.getsizeof(key)
|
|
except:
|
|
pass
|
|
|
|
def pop_last(self):
|
|
try:
|
|
i = next(reversed(self._store))
|
|
data = self._store[i]
|
|
del self._store[i]
|
|
self._store_size -= sys.getsizeof(data) + sys.getsizeof(i)
|
|
return data
|
|
except:
|
|
return None
|
|
|
|
def get_last_key(self):
|
|
try:
|
|
i = next(reversed(self._store))
|
|
return i
|
|
except:
|
|
return None
|
|
|
|
def len(self):
|
|
return len(self._store)
|
|
|
|
def hitrate(self):
|
|
if self._requests:
|
|
return self._hit / self._requests
|
|
else:
|
|
return 0
|
|
|
|
|