def parsear(cls, s): '''Toma un stream de bytes y parsea el bloque merkle. Devuelve un objeto Bloque Merkle''' # s.read(n) leerá n bytes desde el stream # versión - 4 bytes, little endian, interpretalo como int versión = little_endian_a_int(s.read(4)) # bloque_previo - 32 bytes, little endian (usa [::-1] para darle la vuelta) bloque_previo = s.read(32)[::-1] # raíz_merkle - 32 bytes, little endian (usa [::-1] para darle la vuelta) raíz_merkle = s.read(32)[::-1] # timestamp - 4 bytes, little endian, interpretalo como int timestamp = little_endian_a_int(s.read(4)) # bits - 4 bytes bits = s.read(4) # nonce - 4 bytes nonce = s.read(4) # número total de transacciones (4 bytes, little endian) total = little_endian_a_int(s.read(4)) # el número de hashes es un varint num_txs = read_varint(s) # inicializa el array de hashes hashes = [] # haz un bucle tantas veces como el número de hashes for _ in range(num_txs): # cada hash es 32 bytes, little endian hashes.append(s.read(32)[::-1]) # obtén la longitud del campo flags como un varint longitud_flags = read_varint(s) # lee el campo flags flags = s.read(longitud_flags) # inicializa la clase return cls(versión, bloque_previo, raíz_merkle, timestamp, bits, nonce, total, hashes, flags)
def prueba_parsear(self): hex_bloque_merkle = '00000020df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4dc7c835b67d8001ac157e670bf0d00000aba412a0d1480e370173072c9562becffe87aa661c1e4a6dbc305d38ec5dc088a7cf92e6458aca7b32edae818f9c2c98c37e06bf72ae0ce80649a38655ee1e27d34d9421d940b16732f24b94023e9d572a7f9ab8023434a4feb532d2adfc8c2c2158785d1bd04eb99df2e86c54bc13e139862897217400def5d72c280222c4cbaee7261831e1550dbb8fa82853e9fe506fc5fda3f7b919d8fe74b6282f92763cef8e625f977af7c8619c32a369b832bc2d051ecd9c73c51e76370ceabd4f25097c256597fa898d404ed53425de608ac6bfe426f6e2bb457f1c554866eb69dcb8d6bf6f880e9a59b3cd053e6c7060eeacaacf4dac6697dac20e4bd3f38a2ea2543d1ab7953e3430790a9f81e1c67f5b58c825acf46bd02848384eebe9af917274cdfbb1a28a5d58a23a17977def0de10d644258d9c54f886d47d293a411cb6226103b55635' mb = BloqueMerkle.parsear(BytesIO(bytes.fromhex(hex_bloque_merkle))) versión = 0x20000000 self.assertEqual(mb.versión, versión) hex_raíz_merkle = 'ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4' raíz_merkle = bytes.fromhex(hex_raíz_merkle)[::-1] self.assertEqual(mb.raíz_merkle, raíz_merkle) bloque_previo_hex = 'df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000' bloque_previo = bytes.fromhex(bloque_previo_hex)[::-1] self.assertEqual(mb.bloque_previo, bloque_previo) timestamp = little_endian_a_int(bytes.fromhex('dc7c835b')) self.assertEqual(mb.timestamp, timestamp) bits = bytes.fromhex('67d8001a') self.assertEqual(mb.bits, bits) nonce = bytes.fromhex('c157e670') self.assertEqual(mb.nonce, nonce) total = little_endian_a_int(bytes.fromhex('bf0d0000')) self.assertEqual(mb.total, total) hex_hashes = [ 'ba412a0d1480e370173072c9562becffe87aa661c1e4a6dbc305d38ec5dc088a', '7cf92e6458aca7b32edae818f9c2c98c37e06bf72ae0ce80649a38655ee1e27d', '34d9421d940b16732f24b94023e9d572a7f9ab8023434a4feb532d2adfc8c2c2', '158785d1bd04eb99df2e86c54bc13e139862897217400def5d72c280222c4cba', 'ee7261831e1550dbb8fa82853e9fe506fc5fda3f7b919d8fe74b6282f92763ce', 'f8e625f977af7c8619c32a369b832bc2d051ecd9c73c51e76370ceabd4f25097', 'c256597fa898d404ed53425de608ac6bfe426f6e2bb457f1c554866eb69dcb8d', '6bf6f880e9a59b3cd053e6c7060eeacaacf4dac6697dac20e4bd3f38a2ea2543', 'd1ab7953e3430790a9f81e1c67f5b58c825acf46bd02848384eebe9af917274c', 'dfbb1a28a5d58a23a17977def0de10d644258d9c54f886d47d293a411cb62261', ] hashes = [bytes.fromhex(h)[::-1] for h in hex_hashes] self.assertEqual(mb.hashes, hashes) flags = bytes.fromhex('b55635') self.assertEqual(mb.flags, flags)
def parsear(cls, s, testnet=False): '''Toma un stream y crea un NetworkEnvelope''' # comprueba la magia de red magia = s.read(4) if magia == b'': raise RuntimeError('Conexión reseteada!') if testnet: magia_esperada = MAGIA_RED_TESTNET else: magia_esperada = MAGIA_RED if magia != magia_esperada: raise RuntimeError('magia no es correcta {} vs {}'.format( magia.hex(), magia_esperada.hex())) # comando 12 bytes comando = s.read(12) # quita los 0's arrastrados comando = comando.strip(b'\x00') # longitud de carga 4 bytes, little endian longitud_carga = little_endian_a_int(s.read(4)) # checksum es 4 bytes, los primeros cuatro del doble_sha256 de carga checksum = s.read(4) # carga es de longitud longitud_carga carga = s.read(longitud_carga) # verificar checksum checksum_calculada = doble_sha256(carga)[:4] if checksum_calculada != checksum: raise RuntimeError('checksum no es correcta') return cls(comando, carga, testnet=testnet)
def parsear(cls, s): '''Toma un stream de bytes y parsea el tx_input al principio devuelve un objeto TxIn ''' # s.read(n) devolverá n bytes # tx_previa es 32 bytes, little endian tx_previa = s.read(32)[::-1] # indice_previo es 4 bytes, little endian, interpretalo como int indice_previo = little_endian_a_int(s.read(4)) # script_sig es un campo variable (longitud seguida de los datos) # puedes usar Script.parsear para obtener el script real script_sig = Script.parsear(s) # sequence es 4 bytes, little-endian, interpretalo como entero sequence = little_endian_a_int(s.read(4)) # devuelve una instancia de la clase (cls(...)) return cls(tx_previa, indice_previo, script_sig, sequence)
def parsear(cls, s): '''Toma un stream de bytes y parsea un bloque. Devuelve un objeto Bloque''' # s.read(n) leerá n bytes desde el stream # versión - 4 bytes, little endian, interpretalo como int versión = little_endian_a_int(s.read(4)) # bloque_previo - 32 bytes, little endian (usa [::-1] para darle la vuelta) bloque_previo = s.read(32)[::-1] # raíz_merkle - 32 bytes, little endian (usa [::-1] para darle la vuelta) raíz_merkle = s.read(32)[::-1] # timestamp - 4 bytes, little endian, interpret as int timestamp = little_endian_a_int(s.read(4)) # bits - 4 bytes bits = s.read(4) # nonce - 4 bytes nonce = s.read(4) # inicializa la clase return cls(versión, bloque_previo, raíz_merkle, timestamp, bits, nonce)
def comprobar_pow(self): '''Devuelve si este bloque satisface la prueba de trabajo''' # obtén el doble_sha256 de la serialización de este bloque sha = doble_sha256(self.serializar()) # interpreta este hash como un número little-endian prueba = little_endian_a_int(sha) # devuelve si este entero es menos que el target return prueba < self.target()
def parsear(cls, s): '''Toma un byte stream y parsea el tx_input al comienzo devuelve un objeto TxIn ''' # s.read(n) devolverá n bytes # tx_previa es 32 bytes, little endian tx_previa = s.read(32)[::-1] # indice_previo es 4 bytes, little endian, interpreta como int indice_previo = little_endian_a_int(s.read(4)) # script_sig es un campo variable (longitud seguida de los datos) # obtén la longitud usando read_varint(s) script_sig_longitud = read_varint(s) script_sig = s.read(script_sig_longitud) # sequence es 4 bytes, little-endian, interprétalo como int sequence = little_endian_a_int(s.read(4)) # devuelve una instancia de la clase (cls(...)) return cls(tx_previa, indice_previo, script_sig, sequence)
def parsear(cls, s): '''Toma un stream de bytes y parsea el tx_input al comienzo devuelve un objeto TxIn ''' # s.read(n) devolverá n bytes # prev_tx es 32 bytes, little endian prev_tx = s.read(32)[::-1] # prev_index es 4 bytes, little endian, interprétalo como int prev_index = little_endian_a_int(s.read(4)) # script_sig es un campo variable (longitud seguida de los datos) # obtén la longitud usando read_varint(s) script_sig_longitud = read_varint(s) script_sig = s.read(script_sig_longitud) # sequence tiene 4 bytes, little-endian, interprétalo como int sequence = little_endian_a_int(s.read(4)) # devuelve una instancia de la clase (cls(...)) return cls(prev_tx, prev_index, script_sig, sequence)
def comprobar_pow(self): '''Devuelve si este Bloque satisface la prueba de trabajo''' # obtén el doble_sha256 de la serialización de este Bloque sha = doble_sha256(self.serializar()) # interpreta este hash como un número little-endian prueba = little_endian_a_int(sha) # return whether this integer is less than the target return prueba < self.target()
def target(self): '''Devuelve el target de la prueba-de-trabajo basado en los bits''' # el últiom byte es el exponentee exponente = self.bits[-1] # los primeros 3 bytes son el coeficiente en little endian coeficiente = little_endian_a_int(self.bits[:-1]) # la fórmula es: # coeficiente * 256**(exponente-3) return coeficiente * 256**(exponente - 3)
def target(self): '''Devuelve el objetivo de la prueba de trabajo basándose en los bits''' # el último byte es el exponente exponente = self.bits[-1] # los primeros 3 bytes son el coeficiente en little endian coeficiente = little_endian_a_int(self.bits[:-1]) # la fórmula es: # coeficiente * 256**(exponente-3) return coeficiente * 256**(exponente - 3)
def parsear(cls, s): '''Toma un stream de bytes y parsea el tx_output al comienzo devuelve un objeto TxOut ''' # s.read(n) devolverá n bytes # cantidad es 8 bytes, little endian, interpretalo como entero cantidad = little_endian_a_int(s.read(8)) # script_pubkey es un campo variable (la longitud seguida de los datos) # puedes usar Script.parsear para obtener el script real script_pubkey = Script.parsear(s) # devolverá una instancia de la clase (cls(...)) return cls(cantidad, script_pubkey)
def parsear(cls, s): '''Toma un stream de bytes y parsea el tx_output al comienzo devuelve un objeto TxOut ''' # s.read(n) devolverá n bytes # la cantidad es 8 bytes, little endian, interprétalo como int cantidad = little_endian_a_int(s.read(8)) # script_pubkey es un campo variable (la longitud seguida de la fecha) # obtén la longitud usando read_varint(s) script_pubkey_longitud = read_varint(s) script_pubkey = s.read(script_pubkey_longitud) # devuelve una instancia de la clase (cls(...)) return cls(cantidad, script_pubkey)
def altura_coinbase(self): '''Devuelve la altura del bloque en que está transacción coinbase está Devuelve None si esta transacción no es coinbase ''' # si no es una transacción coinbase, devuelve None if not self.es_coinbase(): return None # obtén el primer input primer_input = self.tx_ins[0] # obtén el primer elemento del script_sig (.script_sig.cosas[0]) primer_elemento = primer_input.script_sig.cosas[0] # convierte el primer elemento desde little endian a int return little_endian_a_int(primer_elemento)
def parsear(cls, s, testnet=False): '''Toma un stream de bytes y parsea la transacción al comienzo devuelve un objeto Tx ''' # s.read(n) devolverá n bytes # la versión tiene 4 bytes, little-endian, interprétalo como int versión = little_endian_a_int(s.read(4)) # num_inputs es un varint, usa read_varint(s) num_inputs = read_varint(s) # cada input necesita parseo inputs = [] for _ in range(num_inputs): inputs.append(TxIn.parsear(s)) # num_outputs es un varint, usa read_varint(s) num_outputs = read_varint(s) # cada output necesita parseo outputs = [] for _ in range(num_outputs): outputs.append(TxOut.parsear(s)) # locktime es 4 bytes, little-endian locktime = little_endian_a_int(s.read(4)) # devuelve una instancia de la clase (cls(...)) return cls(versión, inputs, outputs, locktime, testnet=testnet)
def parsear(cls, s): '''Toma un stream de bytes y parsea la transacción al principio devuelve un objeto Tx ''' # s.read(n) devolverá n bytes # la version tiene 4 bytes, little-endian, interprétalo como int version = little_endian_a_int(s.read(4)) # num_inputs es un varint, usa read_varint(s) num_inputs = read_varint(s) # cada input necesita parseo inputs = [] for _ in range(num_inputs): inputs.append(TxIn.parsear(s)) # num_outputs es un varint, usa read_varint(s) num_outputs = read_varint(s) # cada output necesita ser parseado outputs = [] for _ in range(num_outputs): outputs.append(TxOut.parsear(s)) # locktime is 4 bytes, little-endian locktime = little_endian_a_int(s.read(4)) # return an instance of the class (cls(...)) return cls(version, inputs, outputs, locktime)