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, []) # 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 uncolored_needed = ColorTarget.sum(uncolored_targets) fee = op_tx_spec.get_required_fee(250 * (len(colored_inputs) + 1)) uncolored_inputs, uncolored_total = op_tx_spec.select_coins( uncolored_needed + fee) uncolored_change = uncolored_total - uncolored_needed - fee if uncolored_change > 0: uncolored_targets.append( ColorTarget(op_tx_spec.get_change_addr(UNCOLORED_MARKER), uncolored_change)) txins = colored_inputs + uncolored_inputs txouts = [ txspec.ComposedTxSpec.TxOut(target.get_satoshi(), target.get_address()) for target in (colored_targets + uncolored_targets) ] return txspec.ComposedTxSpec(txins, txouts)
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)
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)
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() # get inputs for each color 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)) composed_tx_spec.add_txins(inputs) for target in targets: composed_tx_spec.add_txout(value=target.get_value(), target=target) uncolored_needed = ColorTarget.sum(uncolored_targets) 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 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) return composed_tx_spec
def compose_tx_spec(self, op_tx_spec): targets_by_color = defaultdict(list) uncolored_targets = [] # group targets by color for target in op_tx_spec.get_targets(): color_def = target.get_colordef() if color_def == UNCOLORED_MARKER: uncolored_targets.append(target) elif isinstance(color_def, OBColorDefinition): targets_by_color[color_def.color_id].append(target) else: raise InvalidColorError('incompatible color definition') # 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 uncolored_needed = ColorTarget.sum(uncolored_targets) fee = op_tx_spec.get_required_fee(250 * (len(colored_inputs) + 1)) uncolored_inputs, uncolored_total = op_tx_spec.select_coins(uncolored_needed + fee) uncolored_change = uncolored_total - uncolored_needed - fee if uncolored_change > 0: uncolored_targets.append( ColorTarget(op_tx_spec.get_change_addr(UNCOLORED_MARKER), uncolored_change)) txins = colored_inputs + uncolored_inputs txouts = [txspec.ComposedTxSpec.TxOut(target.get_satoshi(), target.get_address()) for target in (colored_targets + uncolored_targets)] return txspec.ComposedTxSpec(txins, txouts)
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)
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