Created
May 16, 2025 00:50
-
-
Save dwerbam/3b42984eef8c5060b8051bd543e00b90 to your computer and use it in GitHub Desktop.
SD2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import csv | |
from decimal import Decimal | |
import math | |
class Block: | |
BLOCK_HASH =None | |
TS =None | |
PREV_BLOCK_HASH=None | |
NONCE ="1234" | |
FEE =None | |
FEE_RECIPIENT =None | |
miner = None | |
def __init__(self, miner, prev_block_hash, fee_recipient): | |
self.txs = [] | |
self.miner = miner | |
self.PREV_BLOCK_HASH = prev_block_hash | |
self.FEE_RECIPIENT = fee_recipient | |
def add(self, tx): | |
self.txs.append(tx) | |
def build(self): | |
fees = sum([tx.get_fee() for tx in self.txs]) | |
self.FEE = fees | |
# print(f"{fees:.2f}") | |
max_ts = max([tx.get_ts() for tx in self.txs]) | |
# print(max_ts) | |
feetx=Transaction(ts=max_ts,sender="[fees]", receiver=self.miner, ref="Confirmation fees", value=f"{fees:.2f}", signed_by=self.miner, crc=None, fee="0.0", index=max_ts) | |
self.add(feetx) | |
#build the block | |
self.TS = max_ts | |
import hashlib | |
m = hashlib.sha256() | |
hashcontent = f"{''.join([str(tx) for tx in self.txs])}{max_ts}{self.NONCE}{self.PREV_BLOCK_HASH}" | |
m.update(hashcontent.encode(encoding = 'UTF-8', errors = 'strict')) | |
# print(m.hexdigest()) | |
self.BLOCK_HASH = m.hexdigest() | |
#change block hash of each TX | |
for tx in self.txs: | |
tx.update_block_hash(self.BLOCK_HASH) | |
# concat(tx_hashes in block) + timestamp + nonce + prev_block_hash | |
def get_block_hash(self): | |
return self.BLOCK_HASH | |
def __str__(self): | |
if self.BLOCK_HASH is None: | |
self.build() | |
# print(feetx) | |
return '\n'.join([str(x) for x in self.txs]) + \ | |
f"\nB,{self.BLOCK_HASH},{self.TS},{self.PREV_BLOCK_HASH},{self.NONCE},,,,,,,{self.FEE:.2f},{self.FEE_RECIPIENT}," | |
class Transaction: | |
BLOCK_HASH =None | |
TS =None | |
SENDER =None | |
RECEIVER =None | |
REF =None | |
VALUE =None | |
SIGNED_BY =None | |
TX_CRC =None | |
FEE =None | |
TX_INDEX =None | |
def __init__(self, ts, sender, receiver, ref, value, signed_by, crc, fee, index): | |
self.TS = ts | |
self.SENDER = sender | |
self.RECEIVER = receiver | |
self.REF = ref | |
try: | |
self.VALUE = Decimal(value) | |
except ValueError: | |
print(f"Warning: Invalid value '{value}'") | |
# self.VALUE = value | |
self.SIGNED_BY = signed_by | |
self.TX_CRC = crc | |
try: | |
self.FEE = Decimal(fee) | |
except ValueError: | |
print(f"Warning: Invalid fee '{fee}'") | |
self.FEE_RECIPIENT = "XXX" | |
self.TX_INDEX = index | |
def calc_CRC(self): | |
import hashlib | |
m = hashlib.sha256() | |
hashcontent = f"{self.SENDER}{self.RECEIVER}{self.REF}{self.VALUE}{self.FEE:.2f}" | |
m.update(hashcontent.encode(encoding = 'UTF-8', errors = 'strict')) | |
# print(m.hexdigest()) | |
self.TX_CRC = m.hexdigest() | |
def update_block_hash(self, hash): | |
self.BLOCK_HASH = hash | |
def __str__(self): | |
# TYPE : (T) transaction, (B) block | |
# BLOCK_HASH : this links the tx with the block. Hash=sha256(concat(tx_hashes in block) + timestamp + nonce + prev_block_hash) | |
# TS : timestamp | |
# PREV_BLOCK_HASH: previous block, to create the chain (for BLOCK) | |
# NONCE : the proof-of-work value used to find the proper hash (for BLOCK) | |
# SENDER : the public key/address of the sender (for TX). | |
# RECEIVER : the public key/address of the receiver (for TX). | |
# REF : Description or checksum of a previous transaction being spent (for TX). | |
# VALUE : the amount transferred (for TX). | |
# SIGNED_BY : the public key used to sign the transaction (for TX). | |
# TX_CRC : the unique hash of the transaction (for TX). | |
# FEE : the fee amount the sender is willing to pay (for TX). | |
# FEE_RECIPIENT : the public key/address receiving the aggregated fees (BLOCK or the special fee TX). | |
# TX_INDEX : an index to order tx within a block (for TX). | |
if self.TX_CRC is None: | |
self.calc_CRC() | |
return f"T,{self.BLOCK_HASH},{self.TS},,,{self.SENDER}"+\ | |
f",{self.RECEIVER},{self.REF},{self.VALUE:.2f},{self.SIGNED_BY},"+\ | |
f"{self.TX_CRC},{self.FEE:.2f},"+ \ | |
f"{self.TX_INDEX}" | |
def get_fee(self): | |
return self.FEE | |
def get_ts(self): | |
return self.TS | |
def get_crc(self): | |
return self.TX_CRC | |
def process_transactions(filename="transactions_notification.csv"): | |
""" | |
Processes transaction notifications from a CSV file, emulates balances for A, B, C, and D, | |
and detects double spends. | |
Args: | |
filename (str): The name of the CSV file containing transaction data. Defaults to "transaction_notifications.csv". | |
Returns: | |
tuple: A tuple containing: | |
- dict: Balances for accounts A, B, C, and D. | |
- list: List of detected double spends (transactions). | |
""" | |
balances = {'A': 100, 'B': 100, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0, 'I': 0} | |
spent_outputs = {} # Track which outputs have been spent | |
double_spends = [] | |
block_fees = [] | |
bloque = None | |
prev_block_hash = "0000000000000000000000000000000000000000000000000000000000000000" #genesis | |
with open(filename, 'r') as csvfile: | |
reader = csv.reader(csvfile) | |
for row in reader: | |
txid, output_txid, sender, receiver, currency, ts, amount_str = row | |
try: | |
amount = Decimal(amount_str) | |
except ValueError: | |
print(f"Warning: Invalid amount '{amount_str}' in transaction {txid}. Skipping.") | |
continue | |
fee = amount*Decimal(0.002) #0,2% | |
# print(f"\nTID: {txid}:\n { sender} -> { receiver}\n {amount_str} {currency} fee:{round(fee,2)}") | |
if sender in balances and receiver in balances: | |
if balances[sender] >= amount+fee: | |
# agrega al bloque | |
if bloque is None: | |
bloque = Block(miner="Kent", prev_block_hash=prev_block_hash, fee_recipient="Kent") | |
tx = Transaction( | |
ts=ts, sender=sender, receiver=receiver, | |
ref='', value=amount, signed_by=sender, | |
crc=output_txid, fee=fee, index=len(block_fees) | |
) | |
bloque.add(tx) | |
# | |
balances[sender] -= (amount+fee) | |
balances[receiver] += amount | |
# balances['FEE'] += fee | |
block_fees.append(fee) | |
else: | |
# print(f"Double spend: Sender '{sender}' in transaction {txid}. Doesn't have enough money ({amount} {currency} + {fee}) to complete. Ignoring.") | |
continue | |
else: | |
print(f"Warning: Unknown sender '{sender} {receiver}' in transaction {txid}. Skipping.") | |
continue | |
# print_balances(balances) | |
if len(block_fees) >= 5: | |
print(bloque) | |
# print(f">>>>> BLOCK elems ({len(block_fees)}) FEES ({sum(block_fees)}): {block_fees}") | |
block_fees=[] | |
prev_block_hash = bloque.get_block_hash() | |
bloque=None | |
if len(bloque.txs)>0: | |
print(bloque) ## imprime bloque final | |
return balances, double_spends | |
def print_balances(balances): | |
print("Balances:") | |
for account, balance in balances.items(): | |
print(f" {account}: {round(balance,2)}") | |
if __name__ == "__main__": | |
balances, double_spends = process_transactions() | |
# print_balances(balances) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment