def test_int_to_little_endian(self): n = 1 want = b"\x01\x00\x00\x00" self.assertEqual(int_to_little_endian(n, 4), want) n = 10011545 want = b"\x99\xc3\x98\x00\x00\x00\x00\x00" self.assertEqual(int_to_little_endian(n, 8), want)
def sig_hash_bip143( self, input_index, redeem_script=None, witness_script=None, hash_type=SIGHASH_ALL, ): """Returns the integer representation of the hash that needs to get signed for index input_index""" # grab the input being signed by looking up the input_index tx_in = self.tx_ins[input_index] # start with the version in 4 bytes, little endian s = int_to_little_endian(self.version, 4) # add the HashPrevouts and HashSequence if hash_type & SIGHASH_ANYONECANPAY != SIGHASH_ANYONECANPAY: s += self.hash_prevouts() if hash_type & SIGHASH_ANYONECANPAY != SIGHASH_ANYONECANPAY and ( hash_type & 3) not in (SIGHASH_SINGLE, SIGHASH_NONE): s += self.hash_sequence() # add the previous transaction hash in little endian s += tx_in.prev_tx[::-1] # add the previous transaction index in 4 bytes, little endian s += int_to_little_endian(tx_in.prev_index, 4) # for p2wpkh, we need to compute the ScriptCode # Exercise 1: account for p2wsh. Check first for the existence of a WitnessScript if witness_script: # for p2wsh and p2sh-p2wsh the ScriptCode is the WitnessScript script_code = witness_script elif redeem_script: # for p2sh-p2wpkh, get the hash160 which is the 2nd command of the RedeemScript h160 = redeem_script.commands[1] # the ScriptCode is the P2PKHScriptPubKey created using the hash160 script_code = P2PKHScriptPubKey(h160) else: # get the script pubkey associated with the previous output (remember network) script_pubkey = tx_in.script_pubkey(self.network) # next get the hash160 in the script_pubkey. for p2wpkh, it's the second command h160 = script_pubkey.commands[1] # finally the ScriptCode is the P2PKHScriptPubKey created using the hash160 script_code = P2PKHScriptPubKey(h160) # add the serialized ScriptCode s += script_code.serialize() # add the value of the input in 8 bytes, little endian s += int_to_little_endian(tx_in.value(network=self.network), 8) # add the sequence of the input in 4 bytes, little endian s += tx_in.sequence.serialize() # add the HashOutputs if (hash_type & 3) not in (SIGHASH_SINGLE, SIGHASH_NONE): s += self.hash_outputs() elif hash_type & SIGHASH_SINGLE == SIGHASH_SINGLE: s += self.tx_outs[input_index].serialize() # add the locktime in 4 bytes, little endian s += self.locktime.serialize() # add the sighash (SIGHASH_ALL) in 4 bytes, little endian s += int_to_little_endian(hash_type, 4) # hash256 the whole thing, interpret the as a big endian integer using int_to_big_endian return big_endian_to_int(hash256(s))
def filterload(self, flag=1): """Return a network message whose command is filterload""" # encode_varint self.size payload = encode_varint(self.size) # next is the self.filter_bytes() payload += self.filter_bytes() # function count is 4 bytes little endian payload += int_to_little_endian(self.function_count, 4) # tweak is 4 bytes little endian payload += int_to_little_endian(self.tweak, 4) # flag is 1 byte little endian payload += int_to_byte(flag) # return a GenericMessage with b'filterload' as the command return GenericMessage(b"filterload", payload)
def serialize(self): """Serialize this message to send over the network""" # version is 4 bytes little endian result = int_to_little_endian(self.version, 4) # services is 8 bytes little endian result += int_to_little_endian(self.services, 8) # timestamp is 8 bytes little endian result += int_to_little_endian(self.timestamp, 8) # receiver services is 8 bytes little endian result += int_to_little_endian(self.receiver_services, 8) # IPV4 is 10 00 bytes and 2 ff bytes then receiver ip result += b"\x00" * 10 + b"\xff\xff" + self.receiver_ip # receiver port is 2 bytes, little endian result += int_to_little_endian(self.receiver_port, 2) # sender services is 8 bytes little endian result += int_to_little_endian(self.sender_services, 8) # IPV4 is 10 00 bytes and 2 ff bytes then sender ip result += b"\x00" * 10 + b"\xff\xff" + self.sender_ip # sender port is 2 bytes, little endian result += int_to_little_endian(self.sender_port, 2) # nonce result += self.nonce # useragent is a variable string, so varint first result += encode_varint(len(self.user_agent)) result += self.user_agent # latest block is 4 bytes little endian result += int_to_little_endian(self.latest_block, 4) # relay is 00 if false, 01 if true if self.relay: result += b"\x01" else: result += b"\x00" return result
def serialize(self): """Returns the 80 byte block header""" # version - 4 bytes, little endian result = int_to_little_endian(self.version, 4) # prev_block - 32 bytes, little endian result += self.prev_block[::-1] # merkle_root - 32 bytes, little endian result += self.merkle_root[::-1] # timestamp - 4 bytes, little endian result += int_to_little_endian(self.timestamp, 4) # bits - 4 bytes result += self.bits # nonce - 4 bytes result += self.nonce return result
def serialize(self): """Returns the byte serialization of the transaction output""" # serialize amount, 8 bytes, little endian result = int_to_little_endian(self.amount, 8) # serialize the script_pubkey result += self.script_pubkey.serialize() return result
def raw_serialize(self): if self.raw: return self.raw # initialize what we'll send back result = b"" # go through each command for command in self.commands: # if the command is an integer, it's an op code if type(command) == int: # turn the command into a single byte integer using int_to_byte result += int_to_byte(command) else: # otherwise, this is an element # get the length in bytes length = len(command) # for large lengths, we have to use a pushdata op code if length < 75: # turn the length into a single byte integer result += int_to_byte(length) elif length > 75 and length < 0x100: # 76 is pushdata1 result += int_to_byte(76) result += int_to_byte(length) elif length >= 0x100 and length <= 520: # 77 is pushdata2 result += int_to_byte(77) result += int_to_little_endian(length, 2) else: raise ValueError("too long a command") result += command return result
def __init__( self, version=70015, services=0, timestamp=None, receiver_services=0, receiver_ip=b"\x00\x00\x00\x00", receiver_port=8333, sender_services=0, sender_ip=b"\x00\x00\x00\x00", sender_port=8333, nonce=None, user_agent=b"/programmingblockchain:0.1/", latest_block=0, relay=True, ): self.version = version self.services = services if timestamp is None: self.timestamp = int(time.time()) else: self.timestamp = timestamp self.receiver_services = receiver_services self.receiver_ip = receiver_ip self.receiver_port = receiver_port self.sender_services = sender_services self.sender_ip = sender_ip self.sender_port = sender_port if nonce is None: self.nonce = int_to_little_endian(randint(0, 2**64), 8) else: self.nonce = nonce self.user_agent = user_agent self.latest_block = latest_block self.relay = relay
def serialize(self): # start with the number of items as a varint result = encode_varint(len(self.data)) for data_type, identifier in self.data: # data type is 4 bytes little endian result += int_to_little_endian(data_type, 4) # identifier needs to be in little endian result += identifier[::-1] return result
def sha_prevouts(self): if self._sha_prevouts is None: all_prevouts = b"" all_amounts = b"" all_script_pubkeys = b"" all_sequence = b"" for tx_in in self.tx_ins: all_prevouts += tx_in.prev_tx[::-1] + int_to_little_endian( tx_in.prev_index, 4) all_amounts += int_to_little_endian(tx_in.value(self.network), 8) all_script_pubkeys += tx_in.script_pubkey( self.network).serialize() all_sequence += tx_in.sequence.serialize() self._sha_prevouts = sha256(all_prevouts) self._sha_amounts = sha256(all_amounts) self._sha_script_pubkeys = sha256(all_script_pubkeys) self._sha_sequences = sha256(all_sequence) return self._sha_prevouts
def serialize(self): """Returns the byte serialization of the transaction input""" # serialize prev_tx, little endian result = self.prev_tx[::-1] # serialize prev_index, 4 bytes, little endian result += int_to_little_endian(self.prev_index, 4) # serialize the script_sig result += self.script_sig.serialize() # serialize sequence, 4 bytes, little endian result += self.sequence.serialize() return result
def hash_prevouts(self): if self._hash_prevouts is None: all_prevouts = b"" all_sequence = b"" for tx_in in self.tx_ins: all_prevouts += tx_in.prev_tx[::-1] + int_to_little_endian( tx_in.prev_index, 4) all_sequence += tx_in.sequence.serialize() self._hash_prevouts = hash256(all_prevouts) self._hash_sequence = hash256(all_sequence) return self._hash_prevouts
def serialize(self): """Serialize this message to send over the network""" # protocol version is 4 bytes little-endian result = int_to_little_endian(self.version, 4) # number of hashes is a varint result += encode_varint(self.num_hashes) # start block is in little-endian result += self.start_block[::-1] # end block is also in little-endian result += self.end_block[::-1] return result
def serialize(self): """Returns the byte serialization of the entire network message""" # add the network magic using self.magic result = self.magic # command 12 bytes, fill leftover with b'\x00' * (12 - len(self.command)) result += self.command + b"\x00" * (12 - len(self.command)) # payload length 4 bytes, little endian result += int_to_little_endian(len(self.payload), 4) # checksum 4 bytes, first four of hash256 of payload result += hash256(self.payload)[:4] # payload result += self.payload return result
def sig_hash_bip341(self, input_index, ext_flag=0, hash_type=SIGHASH_DEFAULT): """Returns the root message being signed for p2tr""" tx_in = self.tx_ins[input_index] s = b"\x00" s += int_to_byte(hash_type) s += int_to_little_endian(self.version, 4) s += self.locktime.serialize() if not hash_type & SIGHASH_ANYONECANPAY: s += self.sha_prevouts() s += self.sha_amounts() s += self.sha_script_pubkeys() s += self.sha_sequences() if (hash_type & 3) not in (SIGHASH_NONE, SIGHASH_SINGLE): s += self.sha_outputs() spend_type = ext_flag * 2 if tx_in.witness.has_annex(): spend_type += 1 s += int_to_byte(spend_type) if hash_type & SIGHASH_ANYONECANPAY: s += tx_in.prev_tx[::-1] + int_to_little_endian( tx_in.prev_index, 4) s += int_to_little_endian(tx_in.value(), 8) s += tx_in.script_pubkey().serialize() s += tx_in.sequence.serialize() else: s += int_to_little_endian(input_index, 4) if hash_type & SIGHASH_SINGLE == SIGHASH_SINGLE: s += sha256(self.tx_outs[input_index].serialize()) if tx_in.witness.has_annex(): s += sha256(encode_varstr(tx_in.witness[-1])) if ext_flag == 1: tapleaf_hash = tx_in.witness.tap_leaf().hash() # extension defined in BIP0342 s += tapleaf_hash + b"\x00\xff\xff\xff\xff" return hash_tapsighash(s)
def serialize_legacy(self): """Returns the byte serialization of the transaction""" # serialize version (4 bytes, little endian) result = int_to_little_endian(self.version, 4) # encode_varint on the number of inputs result += encode_varint(len(self.tx_ins)) # iterate inputs for tx_in in self.tx_ins: # serialize each input result += tx_in.serialize() # encode_varint on the number of outputs result += encode_varint(len(self.tx_outs)) # iterate outputs for tx_out in self.tx_outs: # serialize each output result += tx_out.serialize() # serialize locktime (4 bytes, little endian) result += self.locktime.serialize() return result
def serialize(self): result = self.filter_type.to_bytes(1, "big") result += int_to_little_endian(self.start_height, 4) result += self.stop_hash[::-1] return result
def sig_hash_legacy(self, input_index, redeem_script=None, hash_type=SIGHASH_ALL): """Returns the integer representation of the hash that needs to get signed for index input_index""" # consensus bugs related to invalid input indices DEFAULT = 1 << 248 if input_index >= len(self.tx_ins): return DEFAULT elif hash_type & 3 == SIGHASH_SINGLE and input_index >= len( self.tx_outs): return DEFAULT # create the serialization per spec # start with version: int_to_little_endian in 4 bytes s = int_to_little_endian(self.version, 4) # next, how many inputs there are: encode_varint s += encode_varint(len(self.tx_ins)) # loop through each input: for i, tx_in in enumerate(self.tx_ins) for i, tx_in in enumerate(self.tx_ins): sequence = tx_in.sequence # if the input index is the one we're signing if i == input_index: # if the RedeemScript was passed in, that's the ScriptSig if redeem_script: script_sig = redeem_script # otherwise the previous tx's ScriptPubkey is the ScriptSig else: script_sig = tx_in.script_pubkey(self.network) # Otherwise, the ScriptSig is empty else: script_sig = None if hash_type & 3 in (SIGHASH_NONE, SIGHASH_SINGLE): sequence = Sequence(0) # create a TxIn object with the prev_tx, prev_index and sequence # the same as the current tx_in and the script_sig from above new_tx_in = TxIn( prev_tx=tx_in.prev_tx, prev_index=tx_in.prev_index, script_sig=script_sig, sequence=sequence, ) # add the serialization of the TxIn object if hash_type & SIGHASH_ANYONECANPAY: if i == input_index: s += new_tx_in.serialize() else: s += new_tx_in.serialize() # add how many outputs there are using encode_varint s += encode_varint(len(self.tx_outs)) # add the serialization of each output for i, tx_out in enumerate(self.tx_outs): if hash_type & 3 == SIGHASH_NONE: continue elif hash_type & 3 == SIGHASH_SINGLE: if i == input_index: s += tx_out.serialize() break else: s += b"\xff\xff\xff\xff\xff\xff\xff\xff\x00" else: s += tx_out.serialize() # add the locktime using int_to_little_endian in 4 bytes s += self.locktime.serialize() # add SIGHASH_ALL using int_to_little_endian in 4 bytes s += int_to_little_endian(hash_type, 4) # hash256 the serialization h256 = hash256(s) # convert the result to an integer using big_endian_to_int(x) return big_endian_to_int(h256)
def serialize(self): return int_to_little_endian(self, 4)