Skip to content

Commit

Permalink
Replace DER Encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchocholaty committed Nov 8, 2024
1 parent fbd804f commit 40aa722
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 67 deletions.
53 changes: 14 additions & 39 deletions src/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,59 +328,34 @@ def op_checksig(self) -> int:
"""
if self.stack.size() < 2:
raise InvalidScriptException("Stack too small for CHECKSIG")

pubkey = self.stack.pop()
signature = self.stack.pop()

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]
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)
der_sig, hash_type = signature[:-1], signature[-1]

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

vk = ecdsa.VerifyingKey.from_string(
pubkey,
curve=ecdsa.SECP256k1,
hashfunc=hashlib.sha256
)

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

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

verified = vk.verify(der_sig, sig_hash, sigdecode=ecdsa.util.sigdecode_der)
self.stack.push(b'\x01' if verified else b'\x00')

#print(verified)

return 1

except ecdsa.BadSignatureError:
self.stack.push(b'\x00')
return 1
except Exception as e:
self.stack.push(b'\x00')
print(f"Unexpected exception: {e}")
return 1

def op_checkmultisig(self) -> int:
Expand Down
50 changes: 28 additions & 22 deletions src/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def valid_input(self, vin_idx, vin):

if scriptpubkey_type == "p2pkh":
return self.validate_p2pkh(vin_idx, vin)
#pass
elif scriptpubkey_type == "p2sh":
#return self.validate_p2sh(vin_idx, vin)
pass
Expand All @@ -121,9 +122,9 @@ def valid_input(self, vin_idx, vin):
elif scriptpubkey_type == "v1_p2tr":
pass
elif scriptpubkey_type == "v0_p2wpkh":
pass
#self.has_witness = True
#return self.validate_p2wpkh(vin_idx, vin)
#pass
self.has_witness = True
return self.validate_p2wpkh(vin_idx, vin)


# Unknown script type.
Expand Down Expand Up @@ -207,57 +208,62 @@ def validate_p2wpkh(self, vin_idx, vin):
Returns:
bool: True if the P2WPKH input is valid, False otherwise
"""
# Check for witness data
# Check for witness data (P2WPKH requires exactly 2 items: signature and pubkey)
witness = vin.get("witness", [])
if len(witness) != 2: # P2WPKH requires exactly 2 witness items (signature and pubkey)
if len(witness) != 2:
return False

# Extract the signature and public key from the witness data
try:
signature = bytes.fromhex(witness[0])
pubkey = bytes.fromhex(witness[1])
except ValueError:
# Handle invalid hex data
return False

# Get witness components
signature = bytes.fromhex(witness[0])
pubkey = bytes.fromhex(witness[1])

# Get previous output's scriptPubKey
# Check the length of the public key (33 bytes for compressed, 65 for uncompressed)
if len(pubkey) not in {33, 65}:
return False

# Extract the previous output's scriptPubKey
prevout = vin.get("prevout", {})
if not prevout:
return False

scriptpubkey = bytes.fromhex(prevout.get("scriptpubkey", ""))

# Verify the script is proper P2WPKH format (OP_0 <20-byte-key-hash>)
# Verify that the scriptPubKey matches the P2WPKH format (OP_0 <20-byte-key-hash>)
if len(scriptpubkey) != 22 or scriptpubkey[0] != 0x00 or scriptpubkey[1] != 0x14:
return False

# Create P2PKH-style script from witness data
# P2WPKH witness program is executed as if it were P2PKH scriptPubKey
# Create the equivalent P2PKH scriptPubKey from the witness data
p2pkh_script = (
bytes([len(signature)]) + # Push signature
bytes([len(signature)]) + # Push the signature
signature +
bytes([len(pubkey)]) + # Push pubkey
bytes([len(pubkey)]) + # Push the public key
pubkey +
# Standard P2PKH script operations
bytes([
0x76, # OP_DUP
0xa9, # OP_HASH160
0x14 # Push 20 bytes
0x14 # Push 20 bytes (20-byte hash follows)
]) +
scriptpubkey[2:22] + # 20-byte pubkey hash from witness program
scriptpubkey[2:22] + # Extract 20-byte public key hash from scriptPubKey
bytes([
0x88, # OP_EQUALVERIFY
0xac # OP_CHECKSIG
])
)

# Create script object with witness-specific transaction data
# Create a script object with the generated P2PKH script and transaction context
script = Script(
p2pkh_script,
json_transaction=self.json_transaction,
input_index=vin_idx,
segwit=True
)

# Execute the script
# Execute the script and catch any errors during the process
try:
#return script.execute()
return script.execute()
except Exception as e:
print(f"P2WPKH validation error: {str(e)}")
Expand Down
30 changes: 24 additions & 6 deletions src/verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,30 @@ def valid_transaction_syntax(json_transaction):


def parse_der_signature_bytes(der_signature):
# Parse the DER signature
# Parse the DER signature
if der_signature[0] != 0x30:
raise ValueError("Invalid DER signature format")

length = der_signature[1]
if length + 2 != len(der_signature):
raise ValueError("Invalid DER signature length")

if der_signature[2] != 0x02:
raise ValueError("Invalid DER signature format")

r_length = der_signature[3]
r = der_signature[4:4 + r_length]
s_length_index = 4 + r_length + 1
s_length = der_signature[s_length_index]
s = der_signature[s_length_index + 1:s_length_index + 1 + s_length]
hash_type = der_signature[-1]


if der_signature[4 + r_length] != 0x02:
raise ValueError("Invalid DER signature format")

s_length = der_signature[5 + r_length]
s = der_signature[6 + r_length:6 + r_length + s_length]

# Determine the hash type
if len(der_signature) > 6 + r_length + s_length:
hash_type = der_signature[-1]
else:
hash_type = 0x01 # Default to SIGHASH_ALL

return r, s, hash_type

0 comments on commit 40aa722

Please sign in to comment.