Skip to content

Commit

Permalink
v3
Browse files Browse the repository at this point in the history
  • Loading branch information
raypan2022 committed May 1, 2024
1 parent f3c0dd5 commit 8a1ba9d
Showing 1 changed file with 60 additions and 41 deletions.
101 changes: 60 additions & 41 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,22 @@
# difficulty target: 0000ffff00000000000000000000000000000000000000000000000000000000

# output format:
# block header
# serialized coinbase transaction
# txids of the transactions mined in the block
# first line: block header
# second line: serialized coinbase transaction
# following lines: txids of the transactions mined in the block

# task 1: process the transactions
# task 2: validate the transactions
# task 3: put the transactions in a block
# task 4: mine the block

import json
import os
import hashlib

import time
from ecdsa import VerifyingKey, SECP256k1, BadSignatureError
from ecdsa.util import sigdecode_der

def serialize_transaction(transaction):
prevout = transaction['vin'][0]['prevout']
scriptpubkey_type = prevout['scriptpubkey_type']

if scriptpubkey_type == 'v0_p2wpkh':
serialized_tx = f"{prevout['value']}{prevout['scriptpubkey']}"
else:
serialized_tx = json.dumps(transaction, sort_keys=True)

return serialized_tx

def double_hash256(data):
return hashlib.sha256(hashlib.sha256(data.encode()).digest()).digest()

def load_transactions(mempool_dir):
transactions = []
for filename in os.listdir(mempool_dir):
Expand All @@ -44,33 +35,59 @@ def load_transactions(mempool_dir):
return transactions

def validate_transaction(transaction):
try:
vin = transaction['vin'][0]
if 'witness' not in vin or len(vin['witness']) < 2:
return False
try:
vin = transaction['vin'][0]
if 'witness' not in vin or len(vin['witness']) < 2:
return False

txid = vin['txid']
signature_hex = vin['witness'][0]
public_key_hex = vin['witness'][1]
signature_hex = vin['witness'][0]
public_key_hex = vin['witness'][1]

sighash_type = signature_hex[-2:]
signature_bytes = bytes.fromhex(signature_hex[:-2])

serialized_tx = serialize_transaction(transaction)
serialized_tx += sighash_type
signature_bytes = bytes.fromhex(signature_hex)
vk = VerifyingKey.from_string(bytes.fromhex(public_key_hex), curve=SECP256k1)
serialized_tx = serialize_transaction(transaction)
message_hash = double_hash256(serialized_tx)

return vk.verify(signature_bytes, message_hash, sigdecode=sigdecode_der)
except (KeyError, ValueError, BadSignatureError, IndexError):
return False

def serialize_transaction(transaction):
# Simplified serialization logic
serialized_tx = json.dumps(transaction, sort_keys=True)
return serialized_tx

def double_hash256(data):
return hashlib.sha256(hashlib.sha256(data.encode()).digest()).digest()

def calculate_merkle_root(transactions):
if not transactions:
return ''

message_hash = double_hash256(serialized_tx)
tx_hashes = [hashlib.sha256(json.dumps(tx).encode()).hexdigest() for tx in transactions]
while len(tx_hashes) > 1:
new_level = []
for i in range(0, len(tx_hashes), 2):
left = tx_hashes[i]
right = tx_hashes[i + 1] if (i + 1) < len(tx_hashes) else tx_hashes[i]
new_level.append(hashlib.sha256((left + right).encode()).hexdigest())
tx_hashes = new_level

vk = VerifyingKey.from_string(bytes.fromhex(public_key_hex), curve=SECP256k1)
return vk.verify(signature_bytes, message_hash, sigdecode=sigdecode_der)
except (KeyError, ValueError, BadSignatureError, IndexError):
return False
return tx_hashes[0]

def create_block_header(transactions, nonce):
merkle_root = calculate_merkle_root(transactions)
timestamp = int(time.time())
previous_block_hash = '0000000000000000000000000000000000000000000000000000000000000000'
version = 1
bits = '1d00ffff'
header = f"{version}{previous_block_hash}{merkle_root}{timestamp}{bits}{nonce}"
return header

def mine_block(transactions, difficulty_target):
block_data = "".join(tx['id'] for tx in transactions)
nonce = 0
while True:
block_header = f'{block_data}{nonce}'
block_header = create_block_header(transactions, nonce)
block_hash = hashlib.sha256(block_header.encode()).hexdigest()
if block_hash < difficulty_target:
break
Expand All @@ -82,15 +99,17 @@ def write_output(block_hash, transactions):
f.write(f"Block Hash: {block_hash}\n")
f.write("Serialized Coinbase Transaction\n")
for tx in transactions:
f.write(f"{tx['id']}\n")
f.write(f"{tx['txid']}\n")

def main():
mempool_dir = "mempool"
mempool_dir = "./mempool"
transactions = load_transactions(mempool_dir)
if not transactions:
print("No valid transactions found.")
return
difficulty_target = "0000ffff00000000000000000000000000000000000000000000000000000000"
_, block_hash = mine_block(transactions, difficulty_target)
nonce, block_hash = mine_block(transactions, difficulty_target)
write_output(block_hash, transactions)

if __name__ == "__main__":
main()

0 comments on commit 8a1ba9d

Please sign in to comment.