95 lines
3.3 KiB
Python
Executable file
95 lines
3.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# Copyright (C) 2024-2026 Whiterun LLC,
|
|
# This software is licensed under the GNU Affero General Public License (AGPL), version 3.0 or later.,
|
|
# A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/agpl-3.0.html
|
|
|
|
import os
|
|
import sys
|
|
import re
|
|
|
|
"""
|
|
Disallow SQL hex() function on Bitcoin hash columns.
|
|
|
|
Bitcoin hash types (TokenID, Txid, BlockHash, PoolID, OutPointHash) use reversed
|
|
byte display format. SQL hex() returns bytes in internal order, not display format.
|
|
|
|
Use blob_to_display_hex::<T>() in Rust instead of SQL hex() for these columns.
|
|
|
|
Exception: owner_pkh (PubkeyHash) does NOT reverse bytes, so hex() is allowed.
|
|
"""
|
|
|
|
OUR_PATH = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
# Hash columns that use byte reversal (hex() is WRONG for these)
|
|
REVERSED_HASH_COLUMNS = [
|
|
'token_id',
|
|
'txid',
|
|
'blockhash',
|
|
'pool',
|
|
'creation_utxo',
|
|
'utxo',
|
|
'outpoint_hash',
|
|
]
|
|
|
|
# Columns that do NOT reverse bytes (hex() is OK for these)
|
|
NON_REVERSED_COLUMNS = [
|
|
'owner_pkh',
|
|
]
|
|
|
|
def build_pattern():
|
|
"""Build regex pattern to match hex(column_name) for reversed hash columns."""
|
|
# Match hex(column) or hex(alias.column) for each reversed column
|
|
columns_pattern = '|'.join(REVERSED_HASH_COLUMNS)
|
|
# Pattern: hex( optional_whitespace optional_alias. column_name optional_whitespace )
|
|
return re.compile(
|
|
r'\bhex\s*\(\s*(?:\w+\.)?\s*(' + columns_pattern + r')\s*\)',
|
|
re.IGNORECASE
|
|
)
|
|
|
|
FORBIDDEN_PATTERN = build_pattern()
|
|
|
|
def check_file(file_path):
|
|
"""Check if the file contains forbidden hex() usage on hash columns."""
|
|
errors = []
|
|
with open(file_path, 'r', encoding='utf-8') as file:
|
|
for line_num, line in enumerate(file, 1):
|
|
matches = FORBIDDEN_PATTERN.findall(line)
|
|
if matches:
|
|
for match in matches:
|
|
errors.append((line_num, match, line.strip()))
|
|
return errors
|
|
|
|
def check_files(directories):
|
|
"""
|
|
Traverse the specified directories and check each .rs file.
|
|
"""
|
|
all_errors = []
|
|
|
|
for directory in directories:
|
|
dir_path = os.path.join(OUR_PATH, "..", directory)
|
|
for root, dirs, files in os.walk(dir_path):
|
|
for file in files:
|
|
if file.endswith('.rs'):
|
|
file_path = os.path.join(root, file)
|
|
errors = check_file(file_path)
|
|
if errors:
|
|
rel_path = os.path.relpath(file_path, os.path.join(OUR_PATH, ".."))
|
|
for line_num, column, line in errors:
|
|
all_errors.append((rel_path, line_num, column, line))
|
|
|
|
if all_errors:
|
|
print("ERROR: SQL hex() used on Bitcoin hash columns that require byte reversal.")
|
|
print("Use blob_to_display_hex::<T>() in Rust instead.\n")
|
|
for rel_path, line_num, column, line in all_errors:
|
|
print(f"{rel_path}:{line_num}: hex({column}) found")
|
|
print(f" {line}\n")
|
|
print(f"\nAffected columns: {', '.join(REVERSED_HASH_COLUMNS)}")
|
|
print("These columns store Bitcoin hashes that use reversed byte display format.")
|
|
print("SQL hex() returns internal byte order, which is incorrect for API output.")
|
|
sys.exit(1)
|
|
else:
|
|
print("OK")
|
|
sys.exit(0)
|
|
|
|
if __name__ == "__main__":
|
|
check_files(['src'])
|