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}" 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 } 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 self.inputs: list[Input] = [] for input in json.loads(data[8]): self.inputs.append(Input(input)) self.outputs: list[Output] = [] 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}" 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 } class Input: def __init__(self, data): if isinstance(data, dict): self.prevout = data["prevout"] self.witness = data["witness"] self.sequence = data["sequence"] self.address = None self.coin = None if "address" in data: self.address = data["address"] if "coin" in data: self.coin = Coin(data["coin"]) else: raise ValueError("Invalid data type") def __str__(self): return f"Input {self.prevout['hash']} {self.coin}" 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 } 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}" 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 class Covenant: def __init__(self, data): if isinstance(data, dict): self.type = data["type"] self.action = data["action"] self.items = data["items"] 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] # TYPE 11 - REVOKE (Only has namehash and height) else: raise ValueError("Invalid data type") def __str__(self): return self.toString() def toString(self): return self.action def toJSON(self) -> dict: return { "type": self.type, "action": self.action, "items": self.items } 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): 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 self.reveal = None self.redeem = None self.value = 0 self.blind = 0 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 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): self.txs.append(tx.hash) 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" self.height = covenant.height if covenant.type == 3: # BID bid: Bid = Bid(covenant, tx) 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 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 }