Skip to content

Instantly share code, notes, and snippets.

@killerstorm
Created January 31, 2014 14:47
Show Gist options
  • Save killerstorm/8733404 to your computer and use it in GitHub Desktop.
Save killerstorm/8733404 to your computer and use it in GitHub Desktop.
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