firmware/testing/serialize.py
2024-05-27 13:14:31 -04:00

244 lines
6.0 KiB
Python

#!/usr/bin/env python3
# Copyright (c) 2010 ArtForz -- public domain half-a-node
# Copyright (c) 2012 Jeff Garzik
# Copyright (c) 2010-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""
Bitcoin Object Python Serializations
************************************
Modified from the test/test_framework/mininode.py file from the
Bitcoin repository
"""
import struct
from struct import error as struct_err
from typing import List
# Serialization/deserialization tools
def ser_compact_size(size: int) -> bytes:
"""
Serialize an integer using Bitcoin's compact size unsigned integer serialization.
:param size: The int to serialize
:returns: The int serialized as a compact size unsigned integer
"""
r = b""
if size < 253:
r = struct.pack("B", size)
elif size < 0x10000:
r = struct.pack("<BH", 253, size)
elif size < 0x100000000:
r = struct.pack("<BI", 254, size)
else:
r = struct.pack("<BQ", 255, size)
return r
def deser_compact_size(f):
"""
Deserialize a compact size unsigned integer from the beginning of the byte stream.
:param f: The byte stream
:returns: The integer that was serialized
"""
try:
nit: int = struct.unpack("<B", f.read(1))[0]
except struct_err:
return None
if nit == 253:
nit = struct.unpack("<H", f.read(2))[0]
elif nit == 254:
nit = struct.unpack("<I", f.read(4))[0]
elif nit == 255:
nit = struct.unpack("<Q", f.read(8))[0]
return nit
def deser_string(f) -> bytes:
"""
Deserialize a variable length byte string serialized with Bitcoin's variable length string serialization from a byte stream.
:param f: The byte stream
:returns: The byte string that was serialized
"""
nit = deser_compact_size(f)
return f.read(nit)
def ser_string(s: bytes) -> bytes:
"""
Serialize a byte string with Bitcoin's variable length string serialization.
:param s: The byte string to be serialized
:returns: The serialized byte string
"""
return ser_compact_size(len(s)) + s
def deser_uint256(f) -> int:
"""
Deserialize a 256 bit integer serialized with Bitcoin's 256 bit integer serialization from a byte stream.
:param f: The byte stream.
:returns: The integer that was serialized
"""
r = 0
for i in range(8):
t = struct.unpack("<I", f.read(4))[0]
r += t << (i * 32)
return r
def ser_uint256(u: int) -> bytes:
"""
Serialize a 256 bit integer with Bitcoin's 256 bit integer serialization.
:param u: The integer to serialize
:returns: The serialized 256 bit integer
"""
rs = b""
for _ in range(8):
rs += struct.pack("<I", u & 0xFFFFFFFF)
u >>= 32
return rs
def uint256_from_str(s: bytes) -> int:
"""
Deserialize a 256 bit integer serialized with Bitcoin's 256 bit integer serialization from a byte string.
:param s: The byte string
:returns: The integer that was serialized
"""
r = 0
t = struct.unpack("<IIIIIIII", s[:32])
for i in range(8):
r += t[i] << (i * 32)
return r
def deser_vector(f, c) -> List:
"""
Deserialize a vector of objects with Bitcoin's object vector serialization from a byte stream.
:param f: The byte stream
:param c: The class of object to deserialize for each object in the vector
:returns: A list of objects that were serialized
"""
nit = deser_compact_size(f)
r = []
for _ in range(nit):
t = c()
t.deserialize(f)
r.append(t)
return r
def ser_vector(v) -> bytes:
"""
Serialize a vector of objects with Bitcoin's object vector serialzation.
:param v: The list of objects to serialize
:returns: The serialized objects
"""
r = ser_compact_size(len(v))
for i in v:
r += i.serialize()
return r
def deser_string_vector(f) -> List[bytes]:
"""
Deserialize a vector of byte strings from a byte stream.
:param f: The byte stream
:returns: The list of byte strings that were serialized
"""
nit = deser_compact_size(f)
r = []
for _ in range(nit):
t = deser_string(f)
r.append(t)
return r
def ser_string_vector(v: List[bytes]) -> bytes:
"""
Serialize a list of byte strings as a vector of byte strings.
:param v: The list of byte strings to serialize
:returns: The serialized list of byte strings
"""
r = ser_compact_size(len(v))
for sv in v:
r += ser_string(sv)
return r
def ser_sig_der(r: bytes, s: bytes) -> bytes:
"""
Serialize the ``r`` and ``s`` values of an ECDSA signature using DER.
:param r: The ``r`` value bytes
:param s: The ``s`` value bytes
:returns: The DER encoded signature
"""
sig = b"\x30"
# Make r and s as short as possible
ri = 0
for b in r:
if b == 0:
ri += 1
else:
break
r = r[ri:]
si = 0
for b in s:
if b == 0:
si += 1
else:
break
s = s[si:]
# Make positive of neg
first = r[0]
if first & (1 << 7) != 0:
r = b"\x00" + r
first = s[0]
if first & (1 << 7) != 0:
s = b"\x00" + s
# Write total length
total_len = len(r) + len(s) + 4
sig += struct.pack("B", total_len)
# write r
sig += b"\x02"
sig += struct.pack("B", len(r))
sig += r
# write s
sig += b"\x02"
sig += struct.pack("B", len(s))
sig += s
sig += b"\x01"
return sig
def ser_sig_compact(r: bytes, s: bytes, recid: bytes) -> bytes:
"""
Serialize the ``r`` and ``s`` values of an ECDSA signature using the compact signature serialization scheme.
:param r: The ``r`` value bytes
:param s: The ``s`` value bytes
:returns: The compact signature
"""
rec = struct.unpack("B", recid)[0]
prefix = struct.pack("B", 27 + 4 + rec)
sig = b""
sig += prefix
sig += r + s
return sig