Implement gambling

This commit is contained in:
2025-12-13 23:41:07 +01:00
parent fd6b5019ac
commit 872e44e317
10 changed files with 954 additions and 171 deletions

145
wallet.py
View File

@@ -1,6 +1,6 @@
#! /usr/bin/env python3
import base64, socket, sys, time
import base64, hashlib, socket, sys, time
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
from cryptography.hazmat.primitives.serialization import Encoding, PrivateFormat, PublicFormat, NoEncryption
@@ -27,33 +27,75 @@ def write_transaction(timestamp, message, amount):
def show_balance(public_key):
public_key_raw = public_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
with open("blockchain", "rb") as f:
total_amount = 0
while True:
block = f.read(292)
if len(block) != 292:
break
miner = block[180:212]
timestamp = int.from_bytes(block[244:252], "big")
if block[0:148] != 148 * b"\0":
sender = block[4:36]
receiver = block[36:68]
amount = int.from_bytes(block[68:76], "big")
fee = int.from_bytes(block[76:84], "big")
if sender == public_key_raw:
write_transaction(timestamp, format_address(receiver), - amount - fee)
total_amount -= (amount + fee)
if receiver == public_key_raw:
write_transaction(timestamp, format_address(sender), amount)
total_amount += amount
else:
fee = 0
if miner == public_key_raw:
write_transaction(timestamp, "mining reward", 100 + fee)
total_amount += 100 + fee
print(81 * "\u2500")
amount_string = f"\U0001f955 \x1b[1;37m{format_amount(total_amount, 41, False)}\x1b[0m"
print(21 * " " + f"\x1b[1;37mYour balance:\x1b[0m{amount_string}")
try:
with open("blockchain", "rb") as f:
total_amount = 0
block_counter = 0
pending_bets = [[]]
while True:
block = f.read(293)
if len(block) != 293:
break
miner = block[181:213]
timestamp = int.from_bytes(block[245:253], "big")
if block[0] == 1:
# Payment
sender = block[5:37]
receiver = block[37:69]
amount = int.from_bytes(block[69:77], "big")
fee = int.from_bytes(block[77:85], "big")
if sender == public_key_raw:
write_transaction(timestamp, format_address(receiver), - amount)
if fee > 0:
write_transaction(timestamp, 39 * " " + "(fee)", - fee)
total_amount -= (amount + fee)
if receiver == public_key_raw:
write_transaction(timestamp, format_address(sender), amount)
total_amount += amount
elif block[0] == 2:
# Gambling
transaction_id = block[1:5]
player = block[5:37]
amount = int.from_bytes(block[37:45], "big")
fee = int.from_bytes(block[45:53], "big")
if player == public_key_raw:
write_transaction(timestamp, "- gambling -", -amount)
if fee > 0:
write_transaction(timestamp, 39 * " " + "(fee)", - fee)
total_amount -= (amount + fee)
pending_bets[-1].append((transaction_id, amount))
elif block[0] == 3:
# Reveal transaction
reveal_info = f.read(5376)
revealer = block[1:33]
R = reveal_info[0:256]
relevant_bets = pending_bets[0]
pending_bets = pending_bets[1:]
fee = 100
for (transaction_id, gambling_amount) in relevant_bets:
to_hash = transaction_id + public_key_raw + R
if hashlib.sha256(to_hash).digest()[0] >= 0x80:
write_transaction(timestamp, f"Won gambling with {format_amount(gambling_amount, 0, False)}", 2 * gambling_amount)
total_amount += 2 * gambling_amount
else:
write_transaction(timestamp, f"Lost gambling with {format_amount(gambling_amount, 0, False)}", 0)
if revealer == public_key_raw:
write_transaction(timestamp, "revealing reward", 1500)
total_amount += 1500
else:
fee = 0
if miner == public_key_raw:
write_transaction(timestamp, "mining reward", 100 + fee)
total_amount += 100 + fee
if block_counter % 256 == 255:
pending_bets.append([])
block_counter += 1
print(81 * "\u2500")
amount_string = f"\U0001f955 \x1b[1;37m{format_amount(total_amount, 41, False)}\x1b[0m"
print(21 * " " + f"\x1b[1;37mYour balance:\x1b[0m{amount_string}")
except FileNotFoundError:
print("File \"blockchain\" not found.\nThis wallet script requires a running node with at least 1 block in the current directory.", file=sys.stderr)
exit(1)
def parse_amount(amount):
amount = amount.replace(",", ".")
@@ -61,10 +103,12 @@ def parse_amount(amount):
if len(parts) == 1:
return int(parts[0]) * 100
elif len(parts) == 2:
if len(parts[1]) > 2:
if len(parts[1]) == 0 or len(parts[1]) > 2:
raise Exception(f"Invalid amount: {amount}")
coins = int(parts[0])
cents = int(parts[1])
coins = int(parts[0]) if parts[0] != "" else 0
cents = int(parts[1]) if parts[1] != "" else 0
if len(parts[1]) == 1:
cents *= 10
return coins * 100 + cents
raise Exception(f"Invalid amount: {amount}")
@@ -81,14 +125,17 @@ def find_free_id(public_key_raw):
with open("blockchain", "rb") as f:
used_ids = set()
while True:
block = f.read(292)
if len(block) != 292:
block = f.read(293)
if len(block) != 293:
break
if block[0:148] != 148 * b"\0":
transaction_id = int.from_bytes(block[0:4], "big")
sender = block[4:36]
transaction_type = block[0]
if transaction_type in (1, 2):
transaction_id = int.from_bytes(block[1:5], "big")
sender = block[5:37]
if sender == public_key_raw:
used_ids.add(transaction_id)
elif transaction_type == 3:
f.read(5376)
for possible_id in range(0, 2**32):
if possible_id not in used_ids:
return possible_id
@@ -106,15 +153,34 @@ def send_payment(private_key, target, amount, fee):
fee = parse_amount_checked(fee)
public_key_raw = private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
transaction_id = find_free_id(public_key_raw)
transaction_prefix = transaction_id.to_bytes(4, "big") + \
transaction_prefix = b"\x01" + \
transaction_id.to_bytes(4, "big") + \
public_key_raw + \
target_raw + \
amount.to_bytes(8, "big") + \
fee.to_bytes(8, "big")
signature = private_key.sign(transaction_prefix)
transaction = transaction_prefix + signature
request = b"\0\0\0\0\x09" + transaction
send_transaction(transaction)
def gamble(private_key, amount, fee):
amount = parse_amount_checked(amount)
if amount == 0:
raise Exception("Amount must not be zero")
fee = parse_amount_checked(fee)
public_key_raw = private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
transaction_id = find_free_id(public_key_raw)
transaction_prefix = b"\x02" + \
transaction_id.to_bytes(4, "big") + \
public_key_raw + \
amount.to_bytes(8, "big") + \
fee.to_bytes(8, "big")
signature = private_key.sign(transaction_prefix)
transaction = transaction_prefix + signature + 32 * b"\0"
send_transaction(transaction)
def send_transaction(transaction):
request = b"\0\0\0\0\x09" + transaction
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
try:
s.connect(("::1", 62039))
@@ -136,6 +202,7 @@ def usage_info():
print("Usage:", file=sys.stderr)
print(" ./wallet.py # see your past transactions and balance", file=sys.stderr)
print(" ./wallet.py pay <target> <amount> <fee> # send carrotcoins to someone else", file=sys.stderr)
print(" ./wallet.py gamble <amount> <fee> # set an arbitrary amount on a 50:50 bet", file=sys.stderr)
def main():
try:
@@ -156,6 +223,8 @@ def main():
show_balance(public_key)
elif len(sys.argv) == 5 and sys.argv[1] == "pay":
send_payment(private_key, *sys.argv[2:5])
elif len(sys.argv) == 4 and sys.argv[1] == "gamble":
gamble(private_key, *sys.argv[2:4])
else:
usage_info()
exit(1)