Repository: bitcoin/bitcoin
File: contrib/utxo-tools/utxo_to_sqlite.py (Python)

Code before:
    """Decompress pubkey by calculating y = sqrt(x^3 + 7) % p
       (see functions `secp256k1_eckey_pubkey_parse` and `secp256k1_ge_set_xo_var`).
    """
    P = 2**256 - 2**32 - 977  # secp256k1 field size
    assert len(compressed_pubkey) == 33 and compressed_pubkey[0] in (2, 3)
    x = int.from_bytes(compressed_pubkey[1:], 'big')
    rhs = (x**3 + 7) % P
    y = pow(rhs, (P + 1)//4, P)  # get sqrt using Tonelli-Shanks algorithm (for p % 4 = 3)
    assert pow(y, 2, P) == rhs, f"pubkey is not on curve ({compressed_pubkey.hex()})"
    tag_is_odd = compressed_pubkey[0] == 3
    y_is_odd = (y & 1) == 1
    if tag_is_odd != y_is_odd:  # fix parity (even/odd) if necessary
        y = P - y
    return bytes([4]) + x.to_bytes(32, 'big') + y.to_bytes(32, 'big')


def main():
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('infile', help='filename of compact-serialized UTXO set (input)')
    parser.add_argument('outfile', help='filename of created SQLite3 database (output)')
    parser.add_argument('-v', '--verbose', action='store_true', help='show details about each UTXO')
    args = parser.parse_args()

    if not os.path.exists(args.infile):
        print(f"Error: provided input file '{args.infile}' doesn't exist.")
        sys.exit(1)

    if os.path.exists(args.outfile):
        print(f"Error: provided output file '{args.outfile}' already exists.")
        sys.exit(1)

    # create database table
    con = sqlite3.connect(args.outfile)
    con.execute("CREATE TABLE utxos(txid TEXT, vout INT, value INT, coinbase INT, height INT, scriptpubkey TEXT)")

    # read metadata (magic bytes, version, network magic, block hash, UTXO count)
    f = open(args.infile, 'rb')
    magic_bytes = f.read(5)
    version = int.from_bytes(f.read(2), 'little')
    network_magic = f.read(4)
    block_hash = f.read(32)
    num_utxos = int.from_bytes(f.read(8), 'little')
    if magic_bytes != UTXO_DUMP_MAGIC:
        print(f"Error: provided input file '{args.infile}' is not an UTXO dump.")
        sys.exit(1)
    if version != UTXO_DUMP_VERSION:
        print(f"Error: provided input file '{args.infile}' has unknown UTXO dump version {version} "
              f"(only version {UTXO_DUMP_VERSION} supported)")
        sys.exit(1)
    network_string = NET_MAGIC_BYTES.get(network_magic, f"unknown network ({network_magic.hex()})")
    print(f"UTXO Snapshot for {network_string} at block hash "

Review comment (suggestion):
Does `rawle` mean `raw little-endian`? If so, it would be better to be explicit in the description.
```suggestion
    parser.add_argument('--txid', choices=['hex', 'raw', 'rawle'], default='hex', help='encode txid as hex, raw bytes (sha256 byteorder), or reversed raw bytes (litle-endian)')
```

Although `rev_raw` or something similar would also work fine.

Code after:


def decompress_pubkey(compressed_pubkey):
    """Decompress pubkey by calculating y = sqrt(x^3 + 7) % p
       (see functions `secp256k1_eckey_pubkey_parse` and `secp256k1_ge_set_xo_var`).
    """
    P = 2**256 - 2**32 - 977  # secp256k1 field size
    assert len(compressed_pubkey) == 33 and compressed_pubkey[0] in (2, 3)
    x = int.from_bytes(compressed_pubkey[1:], 'big')
    rhs = (x**3 + 7) % P
    y = pow(rhs, (P + 1)//4, P)  # get sqrt using Tonelli-Shanks algorithm (for p % 4 = 3)
    assert pow(y, 2, P) == rhs, f"pubkey is not on curve ({compressed_pubkey.hex()})"
    tag_is_odd = compressed_pubkey[0] == 3
    y_is_odd = (y & 1) == 1
    if tag_is_odd != y_is_odd:  # fix parity (even/odd) if necessary
        y = P - y
    return bytes([4]) + x.to_bytes(32, 'big') + y.to_bytes(32, 'big')


def main():
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('infile', help='filename of compact-serialized UTXO set (input)')
    parser.add_argument('outfile', help='filename of created SQLite3 database (output)')
    parser.add_argument('--verbose', action='store_true', help='show details about each UTXO')
    parser.add_argument('--spk', choices=['hex', 'raw'], default='hex', help='encode scriptPubKey as hex or raw bytes')
    parser.add_argument('--txid', choices=['hex', 'raw', 'rawle'], default='hex', help='encode txid as hex, raw bytes (sha256 byteorder), or reversed raw bytes (little endian)')
    args = parser.parse_args()

    if not os.path.exists(args.infile):
        print(f"Error: provided input file '{args.infile}' doesn't exist.")
        sys.exit(1)

    if os.path.exists(args.outfile):
        print(f"Error: provided output file '{args.outfile}' already exists.")
        sys.exit(1)

    spk_hex = (args.spk == 'hex')
    txid_hex = (args.txid == 'hex')
    txid_reverse = (args.txid != 'raw')

    # create database table
    txid_fmt = "TEXT" if txid_hex else "BLOB"
    spk_fmt = "TEXT" if spk_hex else "BLOB"
    con = sqlite3.connect(args.outfile)
    con.execute(f"CREATE TABLE utxos(txid {txid_fmt}, vout INT, value INT, coinbase INT, height INT, scriptpubkey {spk_fmt})")

    # read metadata (magic bytes, version, network magic, block hash, UTXO count)
    f = open(args.infile, 'rb')
    magic_bytes = f.read(5)
    version = int.from_bytes(f.read(2), 'little')
    network_magic = f.read(4)