Created
January 31, 2014 14:47
-
-
Save killerstorm/8733404 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ITOGColorDefinition(GenesisColorDefinition): | |
CLASS_CODE = 'itog' | |
class GenesisDesc(object): | |
def __init__(self, exponent, mantissa): | |
self.exponent = exponent | |
self.mantissa = mantissa | |
@classmethod | |
def from_value(cls, value): | |
mantissa = value | |
exponent = 1 | |
while mantissa >= (2 ** 24): | |
if mantissa % 10 != 0: | |
raise Exception('cannot create ITOG genesis for this value \ | |
(must be divisible by 10^n') | |
mantissa /= 10 | |
exponent += 1 | |
return cls(exponent, mantissa) | |
@classmethod | |
def from_nSequence(cls, nSequence): | |
bits = uint_to_bit_list(nSequence) | |
tag_bits = bits[0:4] | |
if tag_bits != [1, 0, 1, 0]: | |
return None | |
exponent = bit_list_to_integer(bits[4:8]) | |
mantissa = bit_list_to_integer(bits[8:32]) | |
total = (10 ** exponent) * mantissa | |
if (total < 1) or (total > 10 ** 16): | |
return None | |
return cls(exponent, mantissa) | |
def to_nSequence(self): | |
bits = [1, 0, 1, 0] | |
bits += uint_to_bit_list(self.exponent, 4) | |
bits += uint_to_bit_list(self.mantissa, 24) | |
return bit_list_to_integer(bits) | |
def get_total(self): | |
return (10 ** self.exponent) * self.mantissa | |
class TargetDesc(object): | |
def __init__(self, padding_code, outputs_start, outputs_length): | |
self.padding_code = padding_code | |
self.outputs_start = outputs_start | |
self.outputs_length = outputs_length | |
@classmethod | |
def from_nSequence(cls, nSequence): | |
bits = uint_to_bit_list(nSequence) | |
tag_bits = bits[0:4] | |
if tag_bits != [1, 0, 0, 1]: | |
return None | |
padding_code = bit_list_to_integer(bits[4:8]) | |
outputs_start = bit_list_to_integer(bits[8:20]) | |
outputs_length = bit_list_to_integer(bits[20:32]) | |
return cls(padding_code, outputs_start, outputs_length) | |
@classmethod | |
def closest_padding_code(cls, dust_threshold): | |
if dust_threshold <= 0: | |
return 0 | |
padding_code = 1 | |
while 10 ** padding_code < dust_threshold: | |
padding_code += 1 | |
if padding_code > 15: | |
raise Exception('requires too much padding') | |
return padding_code | |
def to_nSequence(self): | |
bits = [1, 0, 0, 1] | |
bits += uint_to_bit_list(self.padding_code, 4) | |
bits += uint_to_bit_list(self.outputs_start, 12) | |
bits += uint_to_bit_list(self.outputs_end, 12) | |
return bit_list_to_integer(bits) | |
def includes(self, out_idx): | |
return (out_idx >= self.outputs_start) \ | |
and (out_idx < (self.outputs_start + self.outputs_length)) | |
def get_padding(self): | |
if self.padding_code == 0: | |
return | |
else: | |
return 10 ** self.padding_code | |
@classmethod | |
def is_genesis_tx(cls, tx): | |
if tx.raw.vin[0].prevout.is_null(): | |
return False # coinbase tx cannot be a genesis | |
if cls.GenesisDesc.from_nSequence(tx.raw.vin[0].nSequence): | |
return True | |
else: | |
return False | |
def get_output_group(self, tx, out_idx): | |
nSequence = None | |
target_desc = None | |
input_group = [] | |
for i in range(len(tx.inputs)): | |
i_nSequence = tx.raw.vin[i].nSequence | |
if nSequence is None: | |
i_target_desc = self.TargetDesc(nSequence) | |
if i_target_desc.is_valid() and i_target_desc.includes(out_idx): | |
nSequence = i_nSequence | |
target_desc = i_target_desc | |
input_group.append(i) | |
else: | |
if i_nSequence == nSequence: | |
input_group.append(i) | |
if not target_desc: | |
return (None, None, None) | |
output_group = target_desc.get_output_group(tx) | |
return output_group, target_desc, input_group | |
def get_affecting_inputs(self, tx, output_set): | |
if self.is_genesis_tx(tx): | |
return set() | |
inputs = set() | |
for out_idx in output_set: | |
output_group, target_desc, input_group = \ | |
self.get_output_group(tx, out_idx) | |
if input_group: | |
inputs += set(input_group) | |
return inputs | |
def run_kernel(self, tx, in_colorvalues): | |
if self.is_genesis_tx(tx): | |
if tx.hash == self.genesis['txhash']: | |
genesis_desc = self.GenesisDesc.from_nSequence( | |
tx.raw.vin[0].nSequence) | |
return ([SimpleColorValue(colordef=self, | |
value=genesis_desc.get_total())] + | |
[None] * (len(tx.outputs) - 1)) | |
else: | |
return [None] * len(tx.outputs) | |
output_colorvalues = [] | |
for out_idx, output in tx.outputs: | |
output_group, target_desc, input_group = \ | |
self.get_output_group(tx, out_idx) | |
padding = target_desc.get_padding() if target_desc else None | |
if (output_group is None) or (output.value <= padding): | |
output_colorvalues.append(None) | |
continue | |
sum_of_input_cvalues = sum([in_colorvalues[i] | |
for i in input_group]) | |
sum_of_output_svalues_wop = sum([tx.outputs[j].value - padding | |
for j in output_group | |
if tx.outputs[j].value > padding]) | |
if sum_of_input_cvalues % sum_of_output_svalues_wop != 0: | |
output_colorvalues.append(None) | |
continue | |
factor = sum_of_input_cvalues / sum_of_output_svalues_wop | |
output_colorvalue = (output_svalue - padding) * factor | |
output_colorvalues.append(SimpleColorValue(colordef=self, | |
value=output_colorvalue)) | |
return output_colorvalues | |
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 = [] | |
uncolored_needed = ColorTarget.sum(uncolored_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)) | |
value_gcd = list_gcd([t.get_value() for t in targets]) | |
min_tgt = min([t.get_value() / value_gcd for t in targets]) | |
padding_code = self.TargetDesc.closest_padding_code( | |
op_tx_spec.get_dust_threshold().get_value() - min_tgt) | |
target_desc = self.TargetDesc(padding_code, | |
len(colored_txouts), | |
len(targets)) | |
padding = target_desc.get_padding() | |
nSequence = target_desc.to_nSequence() | |
for target in targets: | |
sval = target.get_value() / value_gcd + padding | |
uncolored_needed += SimpleColorValue(colordef=UNCOLORED_MARKER, | |
value=sval) | |
colored_txouts.append(txspec.ComposedTxSpec.TxOut( | |
sval, target.get_address())) | |
for inp in inputs: | |
inp.set_nSequence(nSequence) | |
colored_txins.append(inp) | |
fee = op_tx_spec.get_required_fee(250 * (len(txins) + 1)) | |
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 | |
if uncolored_change > 0: | |
uncolored_txouts.append(txspec.ComposedTxSpec.TxOut( | |
uncolored_change.get_value(), | |
op_tx_spec.get_change_addr(UNCOLORED_MARKER))) | |
return txspec.ComposedTxSpec(colored_txins + uncolored_inputs, | |
colored_txouts + uncolored_txouts) | |
@classmethod | |
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') | |
target = op_tx_spec.get_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 = op_tx_spec.get_dust_threshold() | |
inputs, total = op_tx_spec.select_coins(fee + uncolored_value) | |
change = total - fee - uncolored_value | |
targets = [] | |
targets.append(ColorTarget( | |
op_tx_spec.get_change_addr(UNCOLORED_MARKER), 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] | |
inputs[0].set_nSequence( | |
cls.GenesisDesc.from_value(target.get_value()).to_nSequence) | |
return txspec.ComposedTxSpec(inputs, txouts) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment