fireexplorer-indexer/indexerClasses.py

417 lines
13 KiB
Python
Raw Normal View History

2025-02-05 22:38:20 +11:00
import json
import asyncio
import requests
class Block:
def __init__(self, data):
if isinstance(data, dict):
self.hash = data["hash"]
self.height = data["height"]
self.depth = data["depth"]
self.version = data["version"]
self.prevBlock = data["prevBlock"]
self.merkleRoot = data["merkleRoot"]
self.witnessRoot = data["witnessRoot"]
self.treeRoot = data["treeRoot"]
self.reservedRoot = data["reservedRoot"]
self.time = data["time"]
self.bits = data["bits"]
self.nonce = data["nonce"]
self.extraNonce = data["extraNonce"]
self.mask = data["mask"]
self.txs = data["txs"]
elif isinstance(data, list) or isinstance(data, tuple):
self.hash = data[0]
self.height = data[1]
self.depth = data[2]
self.version = data[3]
self.prevBlock = data[4]
self.merkleRoot = data[5]
self.witnessRoot = data[6]
self.treeRoot = data[7]
self.reservedRoot = data[8]
self.time = data[9]
self.bits = data[10]
self.nonce = data[11]
self.extraNonce = data[12]
self.mask = data[13]
self.txs = json.loads(data[14])
else:
raise ValueError("Invalid data type")
def __str__(self):
return f"Block {self.height}"
2025-02-08 14:04:25 +11:00
def toJSON(self) -> dict:
return {
"hash": self.hash,
"height": self.height,
"depth": self.depth,
"version": self.version,
"prevBlock": self.prevBlock,
"merkleRoot": self.merkleRoot,
"witnessRoot": self.witnessRoot,
"treeRoot": self.treeRoot,
"reservedRoot": self.reservedRoot,
"time": self.time,
"bits": self.bits,
"nonce": self.nonce,
"extraNonce": self.extraNonce,
"mask": self.mask,
"txs": self.txs
}
2025-02-05 22:38:20 +11:00
class Transaction:
def __init__(self, data):
if isinstance(data, dict):
self.hash = data["hash"]
self.witnessHash = data["witnessHash"]
self.fee = data["fee"]
self.rate = data["rate"]
self.mtime = data["mtime"]
self.block = data["block"]
self.index = data["index"]
self.version = data["version"]
self.inputs = data["inputs"]
self.outputs = data["outputs"]
self.locktime = data["locktime"]
self.hex = data["hex"]
elif isinstance(data, list) or isinstance(data, tuple):
self.hash = data[0]
self.witnessHash = data[1]
self.fee = data[2]
self.rate = data[3]
self.mtime = data[4]
self.block = data[5]
self.index = data[6]
self.version = data[7]
# Load inputs with Input class
2025-02-08 14:04:25 +11:00
self.inputs: list[Input] = []
2025-02-05 22:38:20 +11:00
for input in json.loads(data[8]):
self.inputs.append(Input(input))
2025-02-08 14:04:25 +11:00
self.outputs: list[Output] = []
2025-02-05 22:38:20 +11:00
for output in json.loads(data[9]):
self.outputs.append(Output(output))
self.locktime = data[10]
self.hex = data[11]
else:
raise ValueError("Invalid data type")
def __str__(self):
return f"Transaction {self.hash}"
2025-02-08 14:04:25 +11:00
def toJSON(self) -> dict:
return {
"hash": self.hash,
"witnessHash": self.witnessHash,
"fee": self.fee,
"rate": self.rate,
"mtime": self.mtime,
"block": self.block,
"index": self.index,
"version": self.version,
"inputs": [input.toJSON() for input in self.inputs],
"outputs": [output.toJSON() for output in self.outputs],
"locktime": self.locktime,
"hex": self.hex
}
2025-02-05 22:38:20 +11:00
class Input:
def __init__(self, data):
if isinstance(data, dict):
self.prevout = data["prevout"]
self.witness = data["witness"]
self.sequence = data["sequence"]
2025-02-08 14:04:25 +11:00
self.address = None
self.coin = None
if "address" in data:
self.address = data["address"]
if "coin" in data:
self.coin = Coin(data["coin"])
2025-02-05 22:38:20 +11:00
else:
raise ValueError("Invalid data type")
def __str__(self):
return f"Input {self.prevout['hash']} {self.coin}"
2025-02-08 14:04:25 +11:00
def toJSON(self) -> dict:
return {
"prevout": self.prevout,
"witness": self.witness,
"sequence": self.sequence,
"address": self.address,
"coin": self.coin.toJSON() if self.coin else None
}
2025-02-05 22:38:20 +11:00
class Output:
def __init__(self, data):
if isinstance(data, dict):
self.value = data["value"]
self.address = data["address"]
self.covenant = Covenant(data["covenant"])
else:
raise ValueError("Invalid data type")
def __str__(self):
return f"Output {self.value} {self.address} {self.covenant}"
2025-02-08 14:04:25 +11:00
def toJSON(self) -> dict:
return {
"value": self.value,
"address": self.address,
"covenant": self.covenant.toJSON()
}
def hex_to_ascii(hex_string):
# Convert the hex string to bytes
bytes_obj = bytes.fromhex(hex_string)
# Decode the bytes object to an ASCII string
ascii_string = bytes_obj.decode('ascii')
return ascii_string
2025-02-05 22:38:20 +11:00
class Covenant:
def __init__(self, data):
if isinstance(data, dict):
self.type = data["type"]
self.action = data["action"]
self.items = data["items"]
2025-02-08 14:04:25 +11:00
self.nameHash = None
self.height = None
self.name = None
self.flags = None
self.hash = None
self.nonce = None
self.recordData = None
self.blockHash = None
self.version = None
self.Address = None
self.claimHeight = None
self.renewalCount = None
if self.type > 0: # All but NONE
self.nameHash = self.items[0]
self.height = self.items[1]
if self.type == 1: # CLAIM
self.flags = self.items[3]
if self.type in [1,2,3]: # CLAIM, OPEN, BID
self.name = hex_to_ascii(self.items[2])
if self.type == 3: # BID
self.hash = self.items[3]
if self.type == 4: # REVEAL
self.nonce = self.items[2]
if self.type in [6,7]: # REGISTER, UPDATE
self.recordData = self.items[2]
if self.type == 6: # REGISTER
self.blockHash = self.items[3]
if self.type == 8: # RENEW
self.blockHash = self.items[2]
if self.type == 9: # TRANSFER
self.version = self.items[2]
self.Address = self.items[3]
if self.type == 10: # FINALIZE
self.name = hex_to_ascii(self.items[2])
self.flags = self.items[3]
self.claimHeight= self.items[4]
self.renewalCount = self.items[5]
self.blockHash = self.items[6]
2025-02-05 22:38:20 +11:00
else:
raise ValueError("Invalid data type")
def __str__(self):
2025-02-08 14:04:25 +11:00
return self.toString()
def toString(self):
return self.action
def toJSON(self) -> dict:
return {
"type": self.type,
"action": self.action,
"items": self.items
}
2025-02-05 22:38:20 +11:00
class Coin:
def __init__(self, data):
if isinstance(data, dict):
self.version = data["version"]
self.height = data["height"]
self.value = data["value"]
self.address = data["address"]
self.covenant = Covenant(data["covenant"])
self.coinbase = data["coinbase"]
else:
raise ValueError("Invalid data type")
def __str__(self):
2025-02-08 14:04:25 +11:00
return f"Coin {self.value} {self.address} {self.covenant}"
def toJSON(self) -> dict:
return {
"version": self.version,
"height": self.height,
"value": self.value,
"address": self.address,
"covenant": self.covenant.toJSON(),
"coinbase": self.coinbase
}
class Bid:
def __init__(self, covenant: Covenant, tx: Transaction):
self.name = covenant.name
self.nameHash = covenant.nameHash
self.height = tx.block
self.tx: Transaction = tx
self.bidHash = covenant.hash
self.bid = covenant
2025-02-09 21:33:20 +11:00
self.reveal = None
self.redeem = None
2025-02-08 14:04:25 +11:00
self.value = 0
self.blind = 0
2025-02-09 21:33:20 +11:00
self.txs = [tx.hash]
# TODO add blind calculation
def update(self, covenant: Covenant, tx: Transaction):
if covenant.type == 4: # REVEAL
self.reveal = covenant
self.txs.append(tx.hash)
# TODO add true bid calculation
# TODO add redeem/register covenants
2025-02-08 14:04:25 +11:00
class Name:
def __init__(self, data):
self.name = None
self.nameHash = None
self.state = "CLOSED"
self.height = 0
self.lastRenewal = 0
self.owner = None
self.value = 0
self.highest = 0
self.data = None
self.transfer = 0
self.revoked = 0
self.claimed = 0
self.renewals = 0
self.registered = False
self.expired = False
self.weak = False
self.stats = None
self.start = None
self.txs = []
self.bids = []
if isinstance(data, Covenant):
if not data.type in [1,2]:
print(data.type)
raise ValueError("Invalid covenant type")
self.name = data.name
self.nameHash = data.nameHash
self.height = data.height
if data.type == 2: # OPEN
self.state = "OPEN"
elif isinstance(data, dict):
for key, value in data.items():
setattr(self, key, value)
elif isinstance(data, list) or isinstance(data, tuple):
for key, value in zip(self.__dict__.keys(), data):
setattr(self, key, value)
else:
raise ValueError("Invalid data type")
def __str__(self):
return self.name
def update(self, covenant: Covenant, tx: Transaction):
2025-02-09 21:33:20 +11:00
self.txs.append(tx.hash)
2025-02-08 14:04:25 +11:00
if covenant.type == 0: # NONE
return
if covenant.type == 1: # CLAIM
self.state = "CLOSED"
self.claimed += 1
if covenant.type == 2: # OPEN
self.state = "OPEN"
2025-02-09 21:33:20 +11:00
self.height = covenant.height
2025-02-08 14:04:25 +11:00
if covenant.type == 3: # BID
bid: Bid = Bid(covenant, tx)
2025-02-09 21:33:20 +11:00
self.bids.append(bid)
if covenant.type == 4: # REVEAL
# Get the index of the REVEAL in the outputs
index = 0
for output in tx.outputs:
if output.covenant.hash == covenant.hash:
break
index += 1
# Get input from index
tx_input = tx.inputs[index]
# TODO get matching bid
print(tx_input)
print(covenant)
print(tx)
print(self.bids)
raise NotImplementedError
if covenant.type == 7: # UPDATE
# TODO
raise NotImplementedError
if covenant.type in [6,8]: # REGISTER, RENEW
self.lastRenewal = covenant.height
self.registered = True
if covenant.type == 6: # REGISTER
# TODO
raise NotImplementedError
if covenant.type == 9: # TRANSFER
# TODO
raise NotImplementedError
if covenant.type == 10: # FINALIZE
# TODO
raise NotImplementedError
2025-02-08 14:04:25 +11:00
def toJSON(self) -> dict:
return {
"name": self.name,
"nameHash": self.nameHash,
"state": self.state,
"height": self.height,
"lastRenewal": self.lastRenewal,
"owner": self.owner,
"value": self.value,
"highest": self.highest,
"data": self.data,
"transfer": self.transfer,
"revoked": self.revoked,
"claimed": self.claimed,
"renewals": self.renewals,
"registered": self.registered,
"expired": self.expired,
"weak": self.weak,
"stats": self.stats,
"start": self.start,
"txs": self.txs,
"bids": self.bids
}