def test_sequence(self): sequence_0 = Sequence() self.assertEqual(sequence_0, MAX_SEQUENCE) self.assertTrue(sequence_0.is_max()) self.assertFalse(sequence_0.is_relative()) self.assertIsNone(sequence_0.relative_blocks()) self.assertIsNone(sequence_0.relative_time()) self.assertEqual(Sequence.parse(BytesIO(sequence_0.serialize())), sequence_0) time_amount = 512 * 1000 sequence_1 = Sequence.from_relative_time(time_amount) self.assertFalse(sequence_1.is_comparable(sequence_0)) self.assertIsNone(sequence_1.relative_blocks()) self.assertEqual(sequence_1.relative_time(), time_amount) self.assertEqual(Sequence.parse(BytesIO(sequence_1.serialize())), sequence_1) blocks_amount = 144 sequence_2 = Sequence.from_relative_blocks(blocks_amount) self.assertIsNone(sequence_2.relative_time()) self.assertEqual(sequence_2.relative_blocks(), blocks_amount) self.assertFalse(sequence_1.is_comparable(sequence_2)) sequence_3 = Sequence.from_relative_time(512 * 100) self.assertTrue(sequence_3 < sequence_1) with self.assertRaises(ValueError): sequence_2 < sequence_0 with self.assertRaises(ValueError): sequence_2 < sequence_1 with self.assertRaises(ValueError): Sequence(-1) with self.assertRaises(ValueError): Sequence(1 << 32)
def __init__(self, prev_tx, prev_index, script_sig=None, sequence=None): self.prev_tx = prev_tx self.prev_index = prev_index if script_sig is None: self.script_sig = Script() else: self.script_sig = script_sig if sequence is None: self.sequence = Sequence() else: self.sequence = Sequence(sequence) self._value = None self._script_pubkey = None self.witness = Witness() self.tap_script = None
def test_op_cltv(self): locktime_0 = Locktime(1234) locktime_1 = Locktime(2345) sequence = Sequence() tx_in = TxIn(b"\x00" * 32, 0, sequence=sequence) tx_out = TxOut(1, Script()) tx_obj = Tx(1, [tx_in], [tx_out], locktime_1) stack = [] self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) tx_in.sequence = Sequence(0xFFFFFFFE) self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) stack = [encode_num(-5)] self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) stack = [encode_num(locktime_0)] self.assertTrue(op_checklocktimeverify(stack, tx_obj, 0)) tx_obj.locktime = Locktime(1582820194) self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0)) tx_obj.locktime = Locktime(500) self.assertFalse(op_checklocktimeverify(stack, tx_obj, 0))
def test_op_csv(self): sequence_0 = Sequence() sequence_1 = Sequence(2345) tx_in = TxIn(b"\x00" * 32, 0, sequence=sequence_0) tx_out = TxOut(1, Script()) tx_obj = Tx(1, [tx_in], [tx_out]) stack = [] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) tx_in.sequence = sequence_1 self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(-5)] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) tx_obj.version = 2 self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(1234 | (1 << 22))] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(9999)] self.assertFalse(op_checksequenceverify(stack, tx_obj, 0)) stack = [encode_num(1234)] self.assertTrue(op_checksequenceverify(stack, tx_obj, 0))
def degrading_multisig_tap_node(self, sequence_block_interval=None, sequence_time_interval=None): """Can unlock with multisig as k-of-n, or (k-1)-of-n after a sequence_block_interval/sequence_time_interval amount of time, (k-2)-of-n after 2*sequence_block_interval/sequence_time_interval amount of time, (k-3)-of-n after 3*sequence_block_interval/ sequence_time_interval amount of time and so on.""" leaves = [] for num_keys_needed in range(self.k, 0, -1): if num_keys_needed == self.k: sequence = None elif sequence_block_interval: sequence = Sequence.from_relative_blocks( sequence_block_interval * (self.k - num_keys_needed)) elif sequence_time_interval: sequence = Sequence.from_relative_time( sequence_time_interval * (self.k - num_keys_needed)) for pubkeys in combinations(self.points, num_keys_needed): tap_script = MultiSigTapScript(pubkeys, num_keys_needed, sequence=sequence) leaves.append(tap_script.tap_leaf()) return TapBranch.combine(leaves)
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)
def op_checksequenceverify(stack, tx_obj, input_index): sequence = tx_obj.tx_ins[input_index].sequence if not sequence.is_relative(): return False if len(stack) < 1: return False element = decode_num(stack[-1]) if element < 0: return False if tx_obj.version < 2: return False stack_sequence = Sequence(element) if not sequence.is_comparable(stack_sequence): return False if sequence < stack_sequence: return False return True
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)