1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
# Original version: https://raw.githubusercontent.com/bitcoin-core/HWI/3fe369d0379212fae1c72729a179d133b0adc872/hwilib/_serialize.py
#
#!/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 typing import (
List,
Sequence,
TypeVar,
Callable,
)
from typing_extensions import Protocol
class Readable(Protocol):
def read(self, n: int = -1) -> bytes:
...
class Deserializable(Protocol):
def deserialize(self, f: Readable) -> None:
...
class Serializable(Protocol):
def serialize(self) -> bytes:
...
# 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: Readable) -> int:
"""
Deserialize a compact size unsigned integer from the beginning of the byte stream.
:param f: The byte stream
:returns: The integer that was serialized
"""
nit: int = struct.unpack("<B", f.read(1))[0]
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: Readable) -> 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: Readable) -> 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
D = TypeVar("D", bound=Deserializable)
def deser_vector(f: Readable, c: Callable[[], D]) -> List[D]:
"""
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: Sequence[Serializable]) -> 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: Readable) -> 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
|