def test_little_endian_to_int(self):
     h = bytes.fromhex("99c3980000000000")
     want = 10011545
     self.assertEqual(little_endian_to_int(h), want)
     h = bytes.fromhex("a135ef0100000000")
     want = 32454049
     self.assertEqual(little_endian_to_int(h), want)
Beispiel #2
0
 def parse(cls, stream=None, raw=None):
     if stream and raw:
         raise ValueError("provide exactly one of stream/raw, not both")
     if raw is None:
         if stream is None:
             raise ValueError("provide one of stream/raw")
         raw = read_varstr(stream)
     length = len(raw)
     s = BytesIO(raw)
     # initialize the commands array
     commands = []
     # initialize the number of bytes we've read to 0
     count = 0
     # loop until we've read length bytes
     while count < length:
         # get the current byte
         current = s.read(1)
         # increment the bytes we've read
         count += 1
         # convert the current byte to an integer
         current_byte = current[0]
         # if the current byte is between 1 and 75 inclusive
         if current_byte >= 1 and current_byte <= 75:
             # we have an command set n to be the current byte
             n = current_byte
             # add the next n bytes as an command
             commands.append(s.read(n))
             # increase the count by n
             count += n
         elif current_byte == 76:
             # op_pushdata1
             data_length = little_endian_to_int(s.read(1))
             commands.append(s.read(data_length))
             count += data_length + 1
         elif current_byte == 77:
             # op_pushdata2
             data_length = little_endian_to_int(s.read(2))
             commands.append(s.read(data_length))
             count += data_length + 2
         elif current_byte == 78:
             # op_pushdata4
             data_length = little_endian_to_int(s.read(4))
             commands.append(s.read(data_length))
             count += data_length + 4
         else:
             # we have an op code. set the current byte to op_code
             op_code = current_byte
             # add the op_code to the list of commands
             commands.append(op_code)
     obj = cls(commands)
     if count != length:
         # Would throw error, but Bitcoin Core will read the number of bytes that are there (not what was promised)
         print(f"mismatch between length and consumed bytes {count} vs {length}")
         obj.raw = raw
     return obj
Beispiel #3
0
 def parse_segwit(cls, s, network="mainnet"):
     """Takes a byte stream and parses a segwit transaction"""
     # s.read(n) will return n bytes
     # version has 4 bytes, little-endian, interpret as int
     version = little_endian_to_int(s.read(4))
     # next two bytes need to be 0x00 and 0x01, otherwise raise RuntimeError
     marker = s.read(2)
     if marker != b"\x00\x01":
         raise RuntimeError(f"Not a segwit transaction {marker}")
     # num_inputs is a varint, use read_varint(s)
     num_inputs = read_varint(s)
     # each input needs parsing, create inputs array
     inputs = []
     # parse each input and add to the inputs array
     for _ in range(num_inputs):
         inputs.append(TxIn.parse(s))
     # num_outputs is a varint, use read_varint(s)
     num_outputs = read_varint(s)
     # each output needs parsing, create outputs array
     outputs = []
     # parse each output and add to the outputs array
     for _ in range(num_outputs):
         outputs.append(TxOut.parse(s))
     # there is a witness for each input
     for tx_in in inputs:
         tx_in.witness = Witness.parse(s)
     # locktime is 4 bytes, little-endian
     locktime = Locktime.parse(s)
     # return an instance of the class (cls(...))
     return cls(version,
                inputs,
                outputs,
                locktime,
                network=network,
                segwit=True)
Beispiel #4
0
 def parse_legacy(cls, s, network="mainnet"):
     """Takes a byte stream and parses a legacy transaction"""
     # s.read(n) will return n bytes
     # version has 4 bytes, little-endian, interpret as int
     version = little_endian_to_int(s.read(4))
     # num_inputs is a varint, use read_varint(s)
     num_inputs = read_varint(s)
     # each input needs parsing
     inputs = []
     for _ in range(num_inputs):
         inputs.append(TxIn.parse(s))
     # num_outputs is a varint, use read_varint(s)
     num_outputs = read_varint(s)
     # each output needs parsing
     outputs = []
     for _ in range(num_outputs):
         outputs.append(TxOut.parse(s))
     # locktime is 4 bytes, little-endian
     locktime = Locktime.parse(s)
     # return an instance of the class (cls(...))
     return cls(version,
                inputs,
                outputs,
                locktime,
                network=network,
                segwit=False)
Beispiel #5
0
 def check_pow(self):
     """Returns whether this block satisfies proof of work"""
     # get the hash256 of the serialization of this block
     h256 = hash256(self.serialize())
     # interpret this hash as a little-endian number
     proof = little_endian_to_int(h256)
     # return whether this integer is less than the target
     return proof < self.target()
Beispiel #6
0
 def parse(cls, s):
     """Takes a byte stream and parses the tx_output at the start
     return a TxOut object
     """
     # s.read(n) will return n bytes
     # amount is 8 bytes, little endian, interpret as int
     amount = little_endian_to_int(s.read(8))
     # script_pubkey is a variable field (length followed by the data)
     # you can use Script.parse to get the actual script
     script_pubkey = ScriptPubKey.parse(s)
     # return an instance of the class (cls(...))
     return cls(amount, script_pubkey)
Beispiel #7
0
 def parse_header(cls, stream=None, hex=None):
     """Takes a byte stream and parses block headers. Returns a Block object"""
     if hex:
         if stream:
             raise RuntimeError("One of stream or hex should be defined")
         stream = BytesIO(bytes.fromhex(hex))
     # stream.read(n) will read n bytes from the stream
     # version - 4 bytes, little endian, interpret as int
     version = little_endian_to_int(stream.read(4))
     # prev_block - 32 bytes, little endian (use [::-1] to reverse)
     prev_block = stream.read(32)[::-1]
     # merkle_root - 32 bytes, little endian (use [::-1] to reverse)
     merkle_root = stream.read(32)[::-1]
     # timestamp - 4 bytes, little endian, interpret as int
     timestamp = little_endian_to_int(stream.read(4))
     # bits - 4 bytes
     bits = stream.read(4)
     # nonce - 4 bytes
     nonce = stream.read(4)
     # initialize class
     return cls(version, prev_block, merkle_root, timestamp, bits, nonce)
Beispiel #8
0
 def coinbase_height(self):
     """Returns the height of the block this coinbase transaction is in
     Returns None if this transaction is not a coinbase transaction
     """
     # if this is NOT a coinbase transaction, return None
     if not self.is_coinbase():
         return None
     # grab the first input
     script_sig = self.tx_ins[0].script_sig
     # get the next length bytes
     command = script_sig.commands[0]
     # convert the command from little endian to int
     return little_endian_to_int(command)
Beispiel #9
0
 def test_parse(self):
     hex_merkle_block = "00000020df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4dc7c835b67d8001ac157e670bf0d00000aba412a0d1480e370173072c9562becffe87aa661c1e4a6dbc305d38ec5dc088a7cf92e6458aca7b32edae818f9c2c98c37e06bf72ae0ce80649a38655ee1e27d34d9421d940b16732f24b94023e9d572a7f9ab8023434a4feb532d2adfc8c2c2158785d1bd04eb99df2e86c54bc13e139862897217400def5d72c280222c4cbaee7261831e1550dbb8fa82853e9fe506fc5fda3f7b919d8fe74b6282f92763cef8e625f977af7c8619c32a369b832bc2d051ecd9c73c51e76370ceabd4f25097c256597fa898d404ed53425de608ac6bfe426f6e2bb457f1c554866eb69dcb8d6bf6f880e9a59b3cd053e6c7060eeacaacf4dac6697dac20e4bd3f38a2ea2543d1ab7953e3430790a9f81e1c67f5b58c825acf46bd02848384eebe9af917274cdfbb1a28a5d58a23a17977def0de10d644258d9c54f886d47d293a411cb6226103b55635"
     mb = MerkleBlock.parse(BytesIO(bytes.fromhex(hex_merkle_block)))
     version = 0x20000000
     self.assertEqual(mb.header.version, version)
     merkle_root_hex = (
         "ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4")
     merkle_root = bytes.fromhex(merkle_root_hex)[::-1]
     self.assertEqual(mb.header.merkle_root, merkle_root)
     prev_block_hex = (
         "df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000")
     prev_block = bytes.fromhex(prev_block_hex)[::-1]
     self.assertEqual(mb.header.prev_block, prev_block)
     timestamp = little_endian_to_int(bytes.fromhex("dc7c835b"))
     self.assertEqual(mb.header.timestamp, timestamp)
     bits = bytes.fromhex("67d8001a")
     self.assertEqual(mb.header.bits, bits)
     nonce = bytes.fromhex("c157e670")
     self.assertEqual(mb.header.nonce, nonce)
     total = little_endian_to_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)
Beispiel #10
0
 def parse(cls, s):
     """Takes a byte stream and parses the tx_input at the start
     return a TxIn object
     """
     # s.read(n) will return n bytes
     # prev_tx is 32 bytes, little endian
     prev_tx = s.read(32)[::-1]
     # prev_index is 4 bytes, little endian, interpret as int
     prev_index = little_endian_to_int(s.read(4))
     # script_sig is a variable field (length followed by the data)
     # you can use Script.parse to get the actual script
     script_sig = Script.parse(s)
     # sequence is 4 bytes, little-endian, interpret as int
     sequence = Sequence.parse(s)
     # return an instance of the class (cls(...))
     return cls(prev_tx, prev_index, script_sig, sequence)
Beispiel #11
0
 def parse(cls, s):
     """Takes a byte stream and parses a merkle block. Returns a Merkle Block object"""
     # s.read(n) will read n bytes from the stream
     # header - use Block.parse_header with the stream
     header = Block.parse_header(s)
     # total number of transactions (4 bytes, little endian)
     total = little_endian_to_int(s.read(4))
     # number of hashes is a varint
     num_txs = read_varint(s)
     # initialize the hashes array
     hashes = []
     # loop through the number of hashes times
     for _ in range(num_txs):
         # each hash is 32 bytes, little endian
         hashes.append(s.read(32)[::-1])
     # get the length of the flags field as a varint
     flags_length = read_varint(s)
     # read the flags field
     flags = s.read(flags_length)
     # initialize class
     return cls(header, total, hashes, flags)
Beispiel #12
0
 def parse(cls, s, network="mainnet"):
     """Takes a stream and creates a NetworkEnvelope"""
     # check the network magic
     magic = s.read(4)
     if magic == b"":
         raise RuntimeError("Connection reset!")
     expected_magic = MAGIC[network]
     if magic != expected_magic:
         raise RuntimeError("magic is not right {} vs {}".format(
             magic.hex(), expected_magic.hex()))
     # command 12 bytes, strip the trailing 0's using .strip(b'\x00')
     command = s.read(12).strip(b"\x00")
     # payload length 4 bytes, little endian
     payload_length = little_endian_to_int(s.read(4))
     # checksum 4 bytes, first four of hash256 of payload
     checksum = s.read(4)
     # payload is of length payload_length
     payload = s.read(payload_length)
     # verify checksum
     calculated_checksum = hash256(payload)[:4]
     if calculated_checksum != checksum:
         raise RuntimeError("checksum does not match")
     return cls(command, payload, network=network)
Beispiel #13
0
 def parse(cls, s):
     return cls(little_endian_to_int(s.read(4)))