Skip to content

Commit

Permalink
Fix checksig
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchocholaty committed Nov 5, 2024
1 parent 4ebef60 commit 8c80537
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 25 deletions.
50 changes: 36 additions & 14 deletions src/script.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from dataclasses import dataclass
from typing import List, Any, Union
from Crypto.Hash import RIPEMD160
import hashlib
import ecdsa
from src.op_codes import OP_CODES
from Crypto.Hash import RIPEMD160
from src.verify import parse_der_signature_bytes
from src.serialize import serialize_transaction

class InvalidScriptException(Exception):
"""Custom exception for Script execution errors"""
Expand Down Expand Up @@ -42,10 +44,12 @@ def __init__(self, script: bytes, json_transaction: dict = None, input_index: in
self.input_index = input_index

def create_signature_hash(self, hash_type: int) -> bytes:
data_signed = serialize_transaction(self.transaction, self.input_index, int(hash_type))
return hashlib.sha256(data_signed).digest()
"""
Create the signature hash for the transaction based on the hash type.
This is what gets signed/verified in OP_CHECKSIG.
"""
if not self.transaction:
raise InvalidScriptException("No transaction context provided for signature verification")
Expand Down Expand Up @@ -97,7 +101,7 @@ def create_signature_hash(self, hash_type: int) -> bytes:
# Double SHA256
return hashlib.sha256(hashlib.sha256(serialized).digest()).digest()

"""
def serialize_transaction(self, tx: dict) -> bytes:
"""Serialize a transaction for signing/verification"""
result = bytearray()
Expand Down Expand Up @@ -177,6 +181,7 @@ def execute(self) -> bool:
# Script executed successfully if stack is not empty and top value is true
if self.stack.is_empty():
return False

return self.stack.pop() != b'\x00'

except Exception as e:
Expand All @@ -186,7 +191,7 @@ def _execute_opcode(self, op_name: str) -> int:
"""Execute a single opcode and return how many bytes to advance"""

# Constants
if op_name == 'OP_0':
if op_name == 'OP_0':
self.stack.push(b'\x00')
return 1
elif op_name == 'OP_1NEGATE':
Expand All @@ -202,7 +207,7 @@ def _execute_opcode(self, op_name: str) -> int:
if self.stack.is_empty():
self.if_stack.append(False)
else:
value = self.stack.pop()
value = self.stack.pop()
self.if_stack.append(value != b'\x00')
return 1
elif op_name == 'OP_NOTIF':
Expand Down Expand Up @@ -284,7 +289,7 @@ def _execute_opcode(self, op_name: str) -> int:
if self.stack.size() < 2:
raise InvalidScriptException("Stack too small for OP_EQUAL")
a = self.stack.pop()
b = self.stack.pop()
b = self.stack.pop()
self.stack.push(b'\x01' if a == b else b'\x00')
return 1

Expand Down Expand Up @@ -328,32 +333,49 @@ def op_checksig(self) -> int:

try:
# Extract DER signature and hash type
if len(signature) < 1:
if len(signature) < 1:
raise InvalidScriptException("Empty signature")

der_sig = signature[:-1] # Remove hash type byte
hash_type = signature[-1]
#der_sig = signature[:-1] # Remove hash type byte
#hash_type = signature[-1]

der_sig = signature[:-1]
r, s, hash_type = parse_der_signature_bytes(der_sig)

der_len = len(der_sig)
signature_len = len(r + s) + 6

if der_len != signature_len:
self.stack.push(b'\x00')
return 1

sig = r + s

#print(pubkey)

# Create verifying key from public key bytes
try:
vk = ecdsa.VerifyingKey.from_string(
pubkey,
curve=ecdsa.SECP256k1,
hashfunc=hashlib.sha256
)
except Exception as e:
except Exception as e:
raise InvalidScriptException(f"Invalid public key: {str(e)}")

# Create signature hash based on hash type
sig_hash = self.create_signature_hash(hash_type)

# Verify the signature
try:
verified = vk.verify(der_sig, sig_hash)
verified = vk.verify(sig, sig_hash)
except Exception:
verified = False

self.stack.push(b'\x01' if verified else b'\x00')

#print(verified)

return 1

except Exception as e:
Expand Down Expand Up @@ -412,7 +434,7 @@ def op_checkmultisig(self) -> int:
return 1

@staticmethod
def combine_scripts(*scripts: Union[bytes, 'Script']) -> 'Script':
def combine_scripts(*scripts: Union[bytes, 'Script'], json_transaction: dict) -> 'Script':
"""
Combine multiple scripts into a single script.
Accepts both bytes and Script objects.
Expand All @@ -425,5 +447,5 @@ def combine_scripts(*scripts: Union[bytes, 'Script']) -> 'Script':
combined.extend(script)
else:
raise InvalidScriptException(f"Invalid script type: {type(script)}")
return Script(bytes(combined))
return Script(bytes(combined), json_transaction)

24 changes: 13 additions & 11 deletions src/transaction.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import hashlib
import json

#from ecdsa import VerifyingKey, SECP256k1, BadSignatureError
from ecdsa import VerifyingKey, SECP256k1, BadSignatureError

from src.script import Script, InvalidScriptException
from src.serialize import serialize_transaction
Expand Down Expand Up @@ -128,20 +128,19 @@ def validate_p2pkh(self, vin_idx, vin):
# Pubkey script #
#################
scriptsig = decode_hex(vin.get("scriptsig", ""))

if not scriptsig:
return False

prevout = vin.get("prevout", {})

if not prevout:
return False

scriptpubkey = decode_hex(prevout.get("scriptpubkey", ""))

# Combine and verify
script = Script.combine_scripts(scriptsig, scriptpubkey)
script = Script.combine_scripts(scriptsig, scriptpubkey, json_transaction=self.json_transaction)
is_valid = script.execute()

#print(is_valid)

return is_valid
"""
#####################################################################
Expand Down Expand Up @@ -179,7 +178,8 @@ def validate_p2pkh(self, vin_idx, vin):
return False
signature = r + s
#print(signature)
######################
# Parse scriptPubKey #
######################
Expand All @@ -206,8 +206,8 @@ def validate_p2pkh(self, vin_idx, vin):
data_signed = serialize_transaction(self.json_transaction, vin_idx, int(hash_type))
data_hash = hashlib.sha256(data_signed).digest()
print(self.json_transaction)
print("********************************")
#print(self.json_transaction)
#print("********************************")
# Verify the signature
verifying_key = VerifyingKey.from_string(public_key, curve=SECP256k1)
Expand All @@ -216,8 +216,10 @@ def validate_p2pkh(self, vin_idx, vin):
except BadSignatureError:
return False
return True"""

print(public_key)
print("-------------------")
return True
"""
"""
def validate_p2sh_p2wpkh(self, vin_idx, vin):
# Extract scriptSig and witness
Expand Down

0 comments on commit 8c80537

Please sign in to comment.