Skip to content

Instantly share code, notes, and snippets.

@dwerbam
Created May 16, 2025 00:50
Show Gist options
  • Save dwerbam/3b42984eef8c5060b8051bd543e00b90 to your computer and use it in GitHub Desktop.
Save dwerbam/3b42984eef8c5060b8051bd543e00b90 to your computer and use it in GitHub Desktop.
SD2025
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