def __init__(self, inputs, outputs, version=b'\x01\x00\x00\x00', lock_time=b'\x00\x00\x00\x00'): self.inputs = inputs self.outputs = outputs assert len(version) == 4, 'Invalid Version' assert len(lock_time) == 4, 'Invalid lock time' self._version = version[::-1] self.version = bytes_to_int(self._version) self._lock_time = lock_time[::-1] self.lock_time = bytes_to_int(self._lock_time)
def from_seed(cls, seed: Union[bytes, str], addresstype='P2PKH') -> 'Xprv': if isinstance(seed, str): seed = hex_to_bytes(seed) assert 16 <= len(seed) <= 64, 'Seed should be between 128 and 512 bits' I = hmac.new(key=b"Bitcoin seed", msg=seed, digestmod=hashlib.sha512).digest() I_L, I_R = I[:32], I[32:] if bytes_to_int(I_L) == 0 or bytes_to_int(I_L) > CURVE.N: raise KeyDerivationError key, code = PrivateKey(I_L), I_R return cls(key, code, addresstype=addresstype)
def child(self, i: int) -> 'Xprv': hardened = i >= 1 << 31 if hardened: I = hmac.new(key=self.code, msg=self.keydata() + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() else: I = hmac.new(key=self.code, msg=self.key.to_public().encode(compressed=True) + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() I_L, I_R = bytes_to_int(I[:32]), I[32:] key = (I_L + self.key.int()) % CURVE.N if I_L >= CURVE.N or key == 0: return self.child(i + 1) ret_code = I_R if hardened: path = self.path + f'/{i - 2**31}h' else: path = self.path + f'/{i}' return Xprv(PrivateKey.from_int(key), ret_code, depth=self.depth + 1, i=i, parent=self.fingerprint(), path=path, addresstype=self.type.value)
def decode(cls, key: bytes) -> 'PublicKey': if key.startswith(b'\x04'): # uncompressed key assert len(key) == 65, 'An uncompressed public key must be 65 bytes long' x, y = bytes_to_int(key[1:33]), bytes_to_int(key[33:]) else: # compressed key assert len(key) == 33, 'A compressed public key must be 33 bytes long' x = bytes_to_int(key[1:]) root = modsqrt(CURVE.f(x), P) if key.startswith(b'\x03'): # odd root y = root if root % 2 == 1 else -root % P elif key.startswith(b'\x02'): # even root y = root if root % 2 == 0 else -root % P else: assert False, 'Wrong key format' return cls(Point(x, y))
def encode(bts: bytes) -> str: n = bytes_to_int(bts) leading_zero_bytes = len(bts) - len(bts.lstrip(b'\x00')) int_digits = [] while n: int_digits.append(int(n % BASE)) n //= BASE for _ in range(leading_zero_bytes): int_digits.append(0) return ''.join(ALPHABET[i] for i in reversed(int_digits))
def read_var_int(): """https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer""" byte = pop(1) if byte == b'\xfd': result = pop(2) elif byte == b'\xfe': result = pop(4) elif byte == b'\xff': result = pop(8) else: result = byte return bytes_to_int(result[::-1])
def sign_hash(self, hash): e = hex_to_int(hash) if isinstance(hash, str) else bytes_to_int(hash) r, s = 0, 0 while r == 0 or s == 0: k = secrets.randbelow(N) point = CURVE.G * k r = point.x % N inv_k = mulinv(k, N) s = (inv_k * (e + r * self.int())) % N return message.Signature(r=r, s=s)
def deserialize(cls, bts: bytes) -> 'ExtendedKey': def read(n): nonlocal bts data, bts = bts[:n], bts[n:] return data net = read(4) is_private = net in network('extended_prv').values() is_public = net in network('extended_pub').values() assert is_public ^ is_private, f'Invalid network bytes : {bytes_to_hex(net)}' address_lookup = { val: key for key, val in (network('extended_prv') if is_private else network('extended_pub')).items() } constructor = Xprv if is_private else Xpub depth = bytes_to_int(read(1)) assert depth in range(256), f'Invalid depth : {depth}' fingerprint = read(4) i = bytes_to_int(read(4)) if depth == 0: i = None path = None else: ih = f'{i}' if i < 2**31 else f"{i - 2**31}h" path = '/'.join([constructor.root_path] + ['x' for _ in range(depth - 1)] + [ih]) code = read(32) key = read(33) key = PrivateKey(key) if is_private else PublicKey.decode(key) assert not bts, 'Leftover bytes' return constructor(key, code, depth=depth, i=i, parent=fingerprint, path=path, addresstype=address_lookup[net])
def __init__(self, output, index, script, sequence=b'\xff\xff\xff\xff', witness=None, referenced_tx=None): # Parameters should be bytes as transmitted i.e reversed assert isinstance(output, bytes) and len(output) == 32 self.output = output[::-1] # referenced tx hash self.index = index if isinstance(index, int) else bytes_to_int(index[::-1]) assert self.index <= 0xffffffff self.script = script self.sequence = sequence[::-1] self._referenced_tx = referenced_tx self._referenced_output = None self.witness = witness self._parent = None self.tx_index = None # index of this input in it's parent tx self.parent_id = None
def child(self, i: int) -> 'Xprv': hardened = i >= 1 << 31 if hardened: I = hmac.new(key=self.code, msg=self.keydata() + int_to_bytes(i).rjust(32, b'\x00'), digestmod=hashlib.sha512).digest() else: I = hmac.new(key=self.code, msg=self.key.to_public().encode(compressed=True) + int_to_bytes(i).rjust(32, b'\x00'), digestmod=hashlib.sha512).digest() I_L, I_R = bytes_to_int(I[:32]), I[32:] key = I_L + self.key.int() % CURVE.N if I_L >= CURVE.N or key == 0: return self.child(i + 1) ret_code = I_R return Xprv(key, ret_code, depth=self.depth + 1, i=i)
def _receive(self, value: int): """Creates an output that sends to this address""" addr_type = self.type() output = Output(value=value, script=b'') if addr_type == ADDRESS.P2PKH: address = base58.decode(self.address).rjust(25, b'\x00') keyhash = address[1:-4] output.script = OP.DUP.byte + OP.HASH160.byte + push( keyhash) + OP.EQUALVERIFY.byte + OP.CHECKSIG.byte elif addr_type == ADDRESS.P2SH: address = base58.decode(self.address).rjust(25, b'\x00') scripthash = address[1:-4] output.script = OP.HASH160.byte + push(scripthash) + OP.EQUAL.byte elif addr_type in (ADDRESS.P2WPKH, ADDRESS.P2WSH): witness_version, witness_program = bech32.decode( network('hrp'), self.address) output.script = OP(bytes_to_int( witness_byte(witness_version))).byte + push( bytes(witness_program)) else: raise ValidationError(f"Cannot create output of type {addr_type}") return output
def asm(script): """Turns a script into a symbolic representation""" if isinstance(script, str): script = hex_to_bytes(script) else: script = copy(script) def read(n): nonlocal script data = script[:n] assert data or n == 0, 'EOF' script = script[n:] return data results = [] while script: byte = bytes_to_int(read(1)) op = OP(byte) if byte in range(1, 76): results.append(bytes_to_hex(read(byte))) else: results.append(str(op)) return ' '.join(results)
def step(self): """Executes one script operation""" byte = bytes_to_int(self.read(1)) opcode = OP(byte) self.op(opcode)
def __init__(self, bts): assert bytes_to_int(bts) < N, 'Key larger than Curve Order' super().__init__(bts)
def index(self): return bytes_to_int(self._index)
def sequence(self): return bytes_to_int(self._sequence)
def value(self): return bytes_to_int(self._value)