Exemplo n.º 1
0
    def compose_tx_spec(self, op_tx_spec):
        # group targets by color
        targets_by_color = defaultdict(list)
        # group targets by color
        for target in op_tx_spec.get_targets():
            color_def = target.get_colordef()
            if color_def == UNCOLORED_MARKER \
                    or isinstance(color_def, POBColorDefinition):
                targets_by_color[color_def.color_id].append(target)
            else:
                raise InvalidColorError('incompatible color definition')
        uncolored_targets = targets_by_color.pop(UNCOLORED_MARKER.color_id, [])

        # get inputs for each color
        colored_inputs = []
        colored_targets = []
        for color_id, targets in targets_by_color.items():
            color_def = targets[0].get_colordef()
            needed_sum = ColorTarget.sum(targets)
            inputs, total = op_tx_spec.select_coins(needed_sum)
            change = total - needed_sum
            if change > 0:
                targets.append(ColorTarget(
                        op_tx_spec.get_change_addr(color_def), change))
            colored_inputs += inputs
            colored_targets += targets

        # we also need some amount of extra "uncolored" coins
        # for padding purposes, possibly
        padding_needed = (len(colored_targets) - len(colored_inputs)) \
            * self.PADDING
        uncolored_needed = ColorTarget.sum(uncolored_targets) \
            + SimpleColorValue(colordef=UNCOLORED_MARKER, value=padding_needed)
        fee = op_tx_spec.get_required_fee(250 * (len(colored_inputs) + 1))
        amount_needed = uncolored_needed + fee
        zero = SimpleColorValue(colordef=UNCOLORED_MARKER, value=0)
        if amount_needed == zero:
            uncolored_change = zero
            uncolored_inputs = []
        else:
            uncolored_inputs, uncolored_total = op_tx_spec.select_coins(amount_needed)
            uncolored_change = uncolored_total - amount_needed
        if uncolored_change > zero:
            uncolored_targets.append(
                ColorTarget(op_tx_spec.get_change_addr(UNCOLORED_MARKER),
                 uncolored_change))

        # compose the TxIn and TxOut elements
        txins = colored_inputs + uncolored_inputs
        txouts = [
            txspec.ComposedTxSpec.TxOut(target.get_satoshi(),
                                        target.get_address())
            for target in colored_targets]
        txouts += [txspec.ComposedTxSpec.TxOut(target.get_satoshi(),
                                               target.get_address())
                   for target in uncolored_targets]
        return txspec.ComposedTxSpec(txins, txouts)
Exemplo n.º 2
0
    def compose_genesis_tx_spec(cls, op_tx_spec):
        if len(op_tx_spec.get_targets()) != 1:
            raise InvalidTargetError(
                'genesis transaction spec needs exactly one target')
        g_target = op_tx_spec.get_targets()[0]
        if g_target.get_colordef() != GENESIS_OUTPUT_MARKER:
            raise InvalidColorError(
                'genesis transaction target should use -1 color_id')
        fee = op_tx_spec.get_required_fee(300)
        g_value = g_target.get_value()
        padding_needed = op_tx_spec.get_dust_threshold().get_value() - g_value
        tag = cls.Tag(cls.Tag.closest_padding_code(padding_needed), True)
        padding = tag.get_padding()
        uncolored_needed = SimpleColorValue(colordef=UNCOLORED_MARKER,
                                            value=padding + g_value)
        uncolored_inputs, uncolored_total = op_tx_spec.select_coins(
            uncolored_needed + fee)
        change = uncolored_total - uncolored_needed - fee

        txouts = []
        txouts.append(
            txspec.ComposedTxSpec.TxOut(padding + g_value,
                                        g_target.get_address()))
        if change > 0:
            txouts.append(
                txspec.ComposedTxSpec.TxOut(
                    change.get_value(),
                    op_tx_spec.get_change_addr(UNCOLORED_MARKER)))
        uncolored_inputs[0].set_nSequence(tag.to_nSequence())
        return txspec.ComposedTxSpec(uncolored_inputs, txouts)
Exemplo n.º 3
0
 def compose_genesis_tx_spec(self, op_tx_spec):
     targets = op_tx_spec.get_targets()[:]
     if len(targets) != 1:
         raise InvalidTargetError(
             'genesis transaction spec needs exactly one target')
     target = targets[0]
     if target.get_colordef() != GENESIS_OUTPUT_MARKER:
         raise InvalidColorError(
             'genesis transaction target should use -1 color_id')
     fee = op_tx_spec.get_required_fee(300)
     uncolored_value = SimpleColorValue(colordef=UNCOLORED_MARKER,
                                        value=target.get_value())
     colorvalue = fee + uncolored_value
     inputs, total = op_tx_spec.select_coins(colorvalue)
     change = total - fee - uncolored_value
     if change > 0:
         targets.append(
             ColorTarget(op_tx_spec.get_change_addr(UNCOLORED_MARKER),
                         change))
     txouts = [
         txspec.ComposedTxSpec.TxOut(target.get_satoshi(),
                                     target.get_address())
         for target in targets
     ]
     return txspec.ComposedTxSpec(inputs, txouts)
Exemplo n.º 4
0
    def run_kernel(self, tx, in_colorvalues):
        out_colorvalues = []
        inp_index = 0
        cur_value = 0
        colored = False

        is_genesis = (tx.hash == self.genesis['txhash'])

        tx.ensure_input_values()

        for out_index in xrange(len(tx.outputs)):
            o = tx.outputs[out_index]
            if cur_value == 0:
                colored = True  # reset
            while cur_value < o.value:
                cur_value += tx.inputs[inp_index].value
                if colored:
                    colored = (in_colorvalues[inp_index] is not None)
                inp_index += 1

            is_genesis_output = is_genesis and (out_index
                                                == self.genesis['outindex'])

            if colored or is_genesis_output:
                cv = SimpleColorValue(colordef=self, value=o.value)
                out_colorvalues.append(cv)
            else:
                out_colorvalues.append(None)
        return out_colorvalues
Exemplo n.º 5
0
    def compose_genesis_tx_spec(cls, op_tx_spec):
        if len(op_tx_spec.get_targets()) != 1:
            raise InvalidTargetError(
                'genesis transaction spec needs exactly one target')
        g_target = op_tx_spec.get_targets()[0]
        if g_target.get_colordef() != GENESIS_OUTPUT_MARKER:
            raise InvalidColorError(
                'genesis transaction target should use -1 color_id')
        g_value = g_target.get_value()
        padding_needed = op_tx_spec.get_dust_threshold().get_value() - g_value
        tag = cls.Tag(cls.Tag.closest_padding_code(padding_needed), True)
        padding = tag.get_padding()
        composed_tx_spec = op_tx_spec.make_composed_tx_spec()
        composed_tx_spec.add_txout(value=padding + g_value, target=g_target)
        uncolored_needed = SimpleColorValue(colordef=UNCOLORED_MARKER,
                                            value=padding + g_value)
        uncolored_inputs, uncolored_total = op_tx_spec.select_coins(
            uncolored_needed, composed_tx_spec)
        composed_tx_spec.add_txins(uncolored_inputs)

        fee = composed_tx_spec.estimate_required_fee()
        change = uncolored_total - uncolored_needed - fee
        if change > op_tx_spec.get_dust_threshold():
            composed_tx_spec.add_txout(
                value=change,
                target_addr=op_tx_spec.get_change_addr(UNCOLORED_MARKER),
                is_fee_change=True)
        composed_tx_spec.txins[0].set_nSequence(tag.to_nSequence())
        return composed_tx_spec
Exemplo n.º 6
0
 def compose_genesis_tx_spec(self, op_tx_spec):
     targets = op_tx_spec.get_targets()
     if len(targets) != 1:
         raise InvalidTargetError(
             'genesis transaction spec needs exactly one target')
     target = targets[0]
     if target.get_colordef() != GENESIS_OUTPUT_MARKER:
         raise InvalidColorError(
             'genesis transaction target should use -1 color_id')
     composed_tx_spec = op_tx_spec.make_composed_tx_spec()
     composed_tx_spec.add_txout(value=target.get_value(),
                                target_addr=target.get_address())
     uncolored_value = SimpleColorValue(colordef=UNCOLORED_MARKER,
                                        value=target.get_value())
     inputs, total = op_tx_spec.select_coins(uncolored_value,
                                             composed_tx_spec)
     composed_tx_spec.add_txins(inputs)
     change = total - uncolored_value - composed_tx_spec.estimate_required_fee(
     )
     if change > op_tx_spec.get_dust_threshold():
         composed_tx_spec.add_txout(
             value=change,
             target_addr=op_tx_spec.get_change_addr(UNCOLORED_MARKER),
             is_fee_change=True)
     return composed_tx_spec
Exemplo n.º 7
0
    def run_kernel(self, tx, in_colorvalues):
        """Given a transaction tx and the colorvalues in a list
        in_colorvalues, output the colorvalues of the outputs in a list
        """
        log("in_colorvalues: %s", in_colorvalues)
        tag = self.get_tag(tx)
        if tag is None:
            return [None] * len(tx.outputs)
        if tag.is_genesis:
            if tx.hash == self.genesis['txhash']:
                value_wop = tx.outputs[0].value - tag.get_padding()
                if value_wop > 0:
                    return ([SimpleColorValue(colordef=self,
                                              value=value_wop)] + 
                            [None] * (len(tx.outputs) - 1))
            # we get here if it is a genesis for a different color
            # or if genesis transaction is misconstructed
            return [None] * len(tx.outputs)

        tx.ensure_input_values()
        padding = tag.get_padding()
        out_colorvalues = []
        for out_idx, output in enumerate(tx.outputs):
            out_value_wop = tx.outputs[out_idx].value - padding
            if out_value_wop <= 0:
                out_colorvalues.append(None)
                continue
            affecting_inputs = self.get_xfer_affecting_inputs(tx, tag.get_padding(),
                                                              out_idx)
            log("affecting inputs: %s", affecting_inputs)
            ai_colorvalue = SimpleColorValue(colordef=self, value=0)
            all_colored = True
            for ai in affecting_inputs:
                if in_colorvalues[ai] is None:
                    all_colored = False
                    break
                ai_colorvalue += in_colorvalues[ai]
            log("all colored: %s, colorvalue:%s", all_colored, ai_colorvalue.get_value())
            if (not all_colored) or (ai_colorvalue.get_value() < out_value_wop):
                out_colorvalues.append(None)
                continue
            out_colorvalues.append(SimpleColorValue(colordef=self, value=out_value_wop))
        log("out_colorvalues: %s", out_colorvalues)
        return out_colorvalues
Exemplo n.º 8
0
    def run_kernel(self, tx, in_colorvalues):
        tag = self.get_tag(tx)
        if tag is None:
            return [None] * len(tx.outputs)
        if tag.is_genesis:
            if tx.hash == self.genesis['txhash']:
                value_wop = tx.outputs[0].value - tag.get_padding()
                if value_wop > 0:
                    return (
                        [SimpleColorValue(colordef=self, value=value_wop)] +
                        [None] * (len(tx.outputs) - 1))
            # we get here if it is a genesis for a different color
            # or if genesis transaction is misconstructed
            return [None] * len(tx.outputs)

        tx.ensure_input_values()
        padding = tag.get_padding()
        out_colorvalues = []
        for out_idx, output in enumerate(tx.outputs):
            out_value_wop = tx.outputs[out_idx].value - padding
            if out_value_wop <= 0:
                out_colorvalues.append(None)
                continue
            affecting_inputs = self.get_xfer_affecting_inputs(
                tx, tag.get_padding(), out_idx)
            ai_colorvalue = SimpleColorValue(colordef=self, value=0)
            all_colored = True
            for ai in affecting_inputs:
                if in_colorvalues[ai] is None:
                    all_colored = False
                    break
                ai_colorvalue += in_colorvalues[ai]
            if (not all_colored) or (ai_colorvalue.get_value() <
                                     out_value_wop):
                out_colorvalues.append(None)
                continue
            out_colorvalues.append(
                SimpleColorValue(colordef=self, value=out_value_wop))
        return out_colorvalues
Exemplo n.º 9
0
    def scan_tx(self, tx):
        """ Scan transaction to obtain color data for its outputs. """
        in_colorvalues = []
        empty = True
        for inp in tx.inputs:
            val = self.cdstore.get(self.color_id, inp.prevout.hash,
                                   inp.prevout.n)
            cv = None
            if val:
                empty = False
                cv = SimpleColorValue(colordef=self.colordef, value=val[0])

            in_colorvalues.append(cv)
        if empty and not self.colordef.is_special_tx(tx):
            return
        out_colorvalues = self.colordef.run_kernel(tx, in_colorvalues)
        for o_index, val in enumerate(out_colorvalues):
            if val:
                self.cdstore.add(self.color_id, tx.hash, o_index,
                                 val.get_value(), val.get_label())
Exemplo n.º 10
0
    def compose_tx_spec(self, op_tx_spec):
        targets_by_color = group_targets_by_color(op_tx_spec.get_targets(),
                                                  self.__class__)
        uncolored_targets = targets_by_color.pop(UNCOLORED_MARKER.color_id, [])
        colored_txins = []
        colored_txouts = []
        if uncolored_targets:
            uncolored_needed = ColorTarget.sum(uncolored_targets)
        else:
            uncolored_needed = SimpleColorValue(colordef=UNCOLORED_MARKER,
                                                value=0)

        dust_threshold = op_tx_spec.get_dust_threshold().get_value()

        inputs_by_color = dict()

        min_padding = 0
        # step 1: get inputs, create change targets, compute min padding
        for color_id, targets in targets_by_color.items():
            color_def = targets[0].get_colordef()
            needed_sum = ColorTarget.sum(targets)
            inputs, total = op_tx_spec.select_coins(needed_sum)
            inputs_by_color[color_id] = inputs
            change = total - needed_sum
            if change > 0:
                targets.append(
                    ColorTarget(op_tx_spec.get_change_addr(color_def), change))
            for target in targets:
                padding_needed = dust_threshold - target.get_value()
                if padding_needed > min_padding:
                    min_padding = padding_needed

        tag = self.Tag(self.Tag.closest_padding_code(min_padding), False)
        padding = tag.get_padding()

        # step 2: create txins & txouts, compute uncolored requirements
        for color_id, targets in targets_by_color.items():
            color_def = targets[0].get_colordef()
            for inp in inputs_by_color[color_id]:
                colored_txins.append(inp)
                uncolored_needed -= SimpleColorValue(colordef=UNCOLORED_MARKER,
                                                     value=inp.value)
                print uncolored_needed
            for target in targets:
                svalue = target.get_value() + padding
                colored_txouts.append(
                    txspec.ComposedTxSpec.TxOut(svalue, target.get_address()))
                uncolored_needed += SimpleColorValue(colordef=UNCOLORED_MARKER,
                                                     value=svalue)
                print uncolored_needed

        fee = op_tx_spec.get_required_fee(250 * (len(colored_txins) + 1))

        uncolored_txouts = []
        uncolored_inputs = []
        uncolored_change = None

        if uncolored_needed + fee > 0:
            uncolored_inputs, uncolored_total = op_tx_spec.select_coins(
                uncolored_needed + fee)
            uncolored_txouts = [
                txspec.ComposedTxSpec.TxOut(target.get_satoshi(),
                                            target.get_address())
                for target in uncolored_targets
            ]
            uncolored_change = uncolored_total - uncolored_needed - fee
        else:
            uncolored_change = (-uncolored_needed) - fee

        if uncolored_change > 0:
            uncolored_txouts.append(
                txspec.ComposedTxSpec.TxOut(
                    uncolored_change.get_value(),
                    op_tx_spec.get_change_addr(UNCOLORED_MARKER)))

        all_inputs = colored_txins + uncolored_inputs

        all_inputs[0].set_nSequence(tag.to_nSequence())

        return txspec.ComposedTxSpec(all_inputs,
                                     colored_txouts + uncolored_txouts)
Exemplo n.º 11
0
 def satoshi_to_color(self, satoshivalue):
     return SimpleColorValue(colordef=self, value=satoshivalue)
Exemplo n.º 12
0
    def run_kernel(self, tx, in_colorvalues):
        """Computes the output colorvalues"""
        # special case: genesis tx output
        tx.ensure_input_values()
        if tx.hash == self.genesis['txhash']:
            return [SimpleColorValue(colordef=self, value=out.value)
                    if self.genesis['outindex'] == i else None \
                        for i, out in enumerate(tx.outputs)]
            
        # start with all outputs having null colorvalue
        nones = [None for _ in tx.outputs]
        out_colorvalues = [None for _ in tx.outputs]
        output_groups = {}
        # go through all inputs
        for inp_index in xrange(len(tx.inputs)):
            # if input has non-null colorvalue, check its nSequence
            color_value = in_colorvalues[inp_index]
            if color_value:
                nSequence = tx.raw.vin[inp_index].nSequence
                # nSequence is converted to a set of output indices
                output_group = list(ones(nSequence))
                
                # exceptions; If exceptional situation is detected, we return a list of null colorvalues.
                # nSequence is 0
                if nSequence == 0:
                    return nones
                
                # nSequence has output indices exceeding number of outputs of this transactions
                for out_idx in output_group:
                    if out_idx >= len(tx.inputs):
                        return nones
                # there are intersecting 'output groups' (i.e output belongs to more than one group)
                if not nSequence in output_groups:
                    for og in output_groups:
                        if len(set(ones(og)).intersection(output_group)) != 0:
                            return nones
                    output_groups[nSequence] = SimpleColorValue(colordef=self,
                                                                value=0)
                        
                # add colorvalue of this input to colorvalue of output group
                output_groups[nSequence] += color_value

        # At this step we have total colorvalue for each output group.
        # For each output group:
        for nSequence in output_groups:
            output_group = list(ones(nSequence))
            in_colorvalue = output_groups[nSequence]
            # sum satoshi-values of outputs in it (let's call it ssvalue)
            ssvalue = sum(tx.outputs[out_idx].value for out_idx in output_group)
            #find n such that 2^n*ssvalue = total colorvalue (loop over all |n|<32, positive and negative)
            for n in xrange(-31,32):
                if ssvalue*2**n == in_colorvalue.get_value():
                    # if n exists, each output of this group is assigned colorvalue svalue*2^n, where svalue is its satoshi-value
                    for out_idx in output_group:
                        svalue = tx.outputs[out_idx].value
                        out_colorvalues[out_idx] = SimpleColorValue(colordef=self, value=svalue*2**n, label=in_colorvalue.get_label())
                    break
            else:
                # if n doesn't exist, we treat is as an exceptional sitation and return a list of None values.
                return nones  # pragma: no cover

        return out_colorvalues
Exemplo n.º 13
0
    def run_kernel(self, tx, in_colorvalues):
        """Computes the output colorvalues"""

        is_genesis = (tx.hash == self.genesis['txhash'])

        # it turns out having a running sum in an array is easier
        #  than constructing segments
        input_running_sums = []
        ZERO_COLORVALUE = SimpleColorValue(colordef=self, value=0)
        running_sum = ZERO_COLORVALUE.clone()
        tx.ensure_input_values()
        for element in tx.inputs:
            colorvalue = self.satoshi_to_color(element.value)
            if colorvalue <= ZERO_COLORVALUE:
                break
            running_sum += colorvalue
            input_running_sums.append(running_sum.clone())

        output_running_sums = []
        running_sum = ZERO_COLORVALUE.clone()
        for element in tx.outputs:
            colorvalue = self.satoshi_to_color(element.value)
            if colorvalue <= ZERO_COLORVALUE:
                break
            running_sum += colorvalue
            output_running_sums.append(running_sum.clone())

        # default is that every output has a null colorvalue
        out_colorvalues = [None for i in output_running_sums]

        # see if this is a genesis transaction
        if is_genesis:
            # adjust the single genesis index to have the right value
            #  and return it
            i = self.genesis['outindex']
            out_colorvalues[i] = self.satoshi_to_color(tx.outputs[i].value)
            return out_colorvalues

        # determine if the in_colorvalues are well-formed:
        # after that, there should be some number of non-null values
        # if another null is hit, there should be no more non-null values
        nonnull_sequences = 0
        if in_colorvalues[0] is None:
            current_sequence_is_null = True
            input_color_start = None
        else:
            current_sequence_is_null = False
            input_color_start = 0
        input_color_end = None
        for i, colorvalue in enumerate(in_colorvalues):
            if colorvalue is not None and current_sequence_is_null:
                current_sequence_is_null = False
                input_color_start = i
            elif colorvalue is None and not current_sequence_is_null:
                current_sequence_is_null = True
                input_color_end = i - 1
                nonnull_sequences += 1
        if not current_sequence_is_null:
            nonnull_sequences += 1
            input_color_end = len(in_colorvalues) - 1

        if nonnull_sequences > 1:
            return out_colorvalues

        # now figure out which segments correspond in the output
        if input_color_start == 0:
            sum_before_sequence = ZERO_COLORVALUE.clone()
        else:
            sum_before_sequence = input_running_sums[input_color_start - 1]
        sum_after_sequence = input_running_sums[input_color_end]

        # the sum of the segment before the sequence must be equal
        #  as the sum of the sequence itself must also be equal
        if (sum_before_sequence != ZERO_COLORVALUE
            and sum_before_sequence not in output_running_sums) or \
                sum_after_sequence not in output_running_sums:
            # this means we don't have matching places to start and/or end
            return out_colorvalues

        # now we know exactly where the output color sequence should start
        #  and end
        if sum_before_sequence == ZERO_COLORVALUE:
            output_color_start = 0
        else:
            output_color_start = output_running_sums.index(
                sum_before_sequence) + 1
        output_color_end = output_running_sums.index(sum_after_sequence)

        # calculate what the color value at that point is
        for i in range(output_color_start, output_color_end + 1):
            previous_sum = ZERO_COLORVALUE if i == 0 \
                else output_running_sums[i - 1]
            out_colorvalues[i] = output_running_sums[i] - previous_sum

        return out_colorvalues
Exemplo n.º 14
0
 def sum(cls, targets):
     if len(targets) == 0:
         from colordef import UNCOLORED_MARKER  # circular import
         return SimpleColorValue(colordef=UNCOLORED_MARKER, value=0)
     c = targets[0].colorvalue.__class__
     return c.sum([t.colorvalue for t in targets])
Exemplo n.º 15
0
    def compose_tx_spec(self, op_tx_spec):
        targets_by_color = group_targets_by_color(op_tx_spec.get_targets(),
                                                  self.__class__)
        uncolored_targets = targets_by_color.pop(UNCOLORED_MARKER.color_id, [])
        composed_tx_spec = op_tx_spec.make_composed_tx_spec()
        if uncolored_targets:
            uncolored_needed = ColorTarget.sum(uncolored_targets)
        else:
            uncolored_needed = SimpleColorValue(colordef=UNCOLORED_MARKER,
                                                value=0)
        dust_threshold = op_tx_spec.get_dust_threshold().get_value()
        inputs_by_color = dict()
        min_padding = 0

        # step 1: get inputs, create change targets, compute min padding
        for color_id, targets in targets_by_color.items():
            color_def = targets[0].get_colordef()
            needed_sum = ColorTarget.sum(targets)
            inputs, total = op_tx_spec.select_coins(needed_sum)
            inputs_by_color[color_id] = inputs
            change = total - needed_sum
            if change > 0:
                targets.append(
                    ColorTarget(op_tx_spec.get_change_addr(color_def), change))
            for target in targets:
                padding_needed = dust_threshold - target.get_value()
                if padding_needed > min_padding:
                    min_padding = padding_needed

        tag = self.Tag(self.Tag.closest_padding_code(min_padding), False)
        padding = tag.get_padding()

        # step 2: create txins & txouts, compute uncolored requirements
        for color_id, targets in targets_by_color.items():
            color_def = targets[0].get_colordef()
            for inp in inputs_by_color[color_id]:
                composed_tx_spec.add_txin(inp)
                uncolored_needed -= SimpleColorValue(colordef=UNCOLORED_MARKER,
                                                     value=inp.value)
            for target in targets:
                svalue = target.get_value() + padding
                composed_tx_spec.add_txout(value=svalue, target=target)
                uncolored_needed += SimpleColorValue(colordef=UNCOLORED_MARKER,
                                                     value=svalue)
                print uncolored_needed

        composed_tx_spec.add_txouts(uncolored_targets)
        fee = composed_tx_spec.estimate_required_fee()

        uncolored_change = None

        if uncolored_needed + fee > 0:
            uncolored_inputs, uncolored_total = op_tx_spec.select_coins(
                uncolored_needed, composed_tx_spec)
            composed_tx_spec.add_txins(uncolored_inputs)
            fee = composed_tx_spec.estimate_required_fee()
            uncolored_change = uncolored_total - uncolored_needed - fee
        else:
            uncolored_change = (-uncolored_needed) - fee

        if uncolored_change > op_tx_spec.get_dust_threshold():
            composed_tx_spec.add_txout(
                value=uncolored_change,
                target_addr=op_tx_spec.get_change_addr(UNCOLORED_MARKER),
                is_fee_change=True)

        composed_tx_spec.txins[0].set_nSequence(tag.to_nSequence())

        return composed_tx_spec