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
|
|
|
|
}
|