def sum(self, x1, x2): # each index is 33 bytes for commitments and 32 for hash comm1, hash1 = x1[:33], x1[33:65] comm2, hash2 = x2[:33], x2[33:65] comm1, comm2 = PedersenCommitment( commitment=comm1, raw=True), PedersenCommitment(commitment=comm2, raw=True) #XXX we definetely need sum of pedersen commitments on libsecp256 level. pk = PublicKey() pk.combine([ comm1.to_public_key().public_key, comm2.to_public_key().public_key ]) sm = pk.to_pedersen_commitment() first_part = sm.serialize() second_part = _hash(hash1 + hash2) return first_part + second_part
def non_context_verify(self, block_height): #Actually we partially use context via block_height. Consider renaming. try: if verification_cache[(self.serialize(), block_height)]: #We set coinbase during verification, thus if we scip verification #we need to set it manually. TODO (verification should be free from initialisation stuff) for output in self.outputs: if output.is_coinbase: self.coinbase = output elif output.is_dev_reward: self.dev_reward = output return verification_cache[(self.serialize(), block_height)] except KeyError: pass assert is_sorted( self.inputs, key=lambda _input: _input.authorized_pedersen_commitment.serialize( )), "Inputs are not sorted" assert is_sorted( self.outputs, key=lambda _output: _output.authorized_pedersen_commitment. serialize()), "Outputs are not sorted" assert is_sorted( self.additional_excesses, key=lambda e: e.index), "Additional excesses are not sorted" assert len(self.inputs) == len(self.updated_excesses) for _input in self.inputs: assert _input.lock_height < block_height, "Timelocked input" s_i = _input.serialized_index assert s_i in self.updated_excesses, "Updated excesses do not contain update for address %s" % _input.address.to_text( ) assert _input.address.serialized_pubkey == self.updated_excesses[ s_i].serialized_pubkey #Check that there are no duplicated outputs #TODO probably authorized???? assert len( set([ _output.unauthorized_pedersen_commitment.serialize() for _output in self.outputs ])) == len(self.outputs), "Duplicated output" coinbase_num = 0 dev_reward_num = 0 output_apcs = [] for output in self.outputs: assert output.verify(), "Nonvalid output" _o_index = output.serialized_index output_apcs.append(_o_index[:33]) if output.is_coinbase: coinbase_num += 1 self.coinbase = output elif output.is_dev_reward: dev_reward_num += 1 self.dev_reward = output assert coinbase_num < 2, "More then one coinbase" assert dev_reward_num < 2, "More then one dev reward output" for excess in self.additional_excesses: assert excess.verify(), "Nonvalid excess" #if not excess.message in output_apcs: # return False #else: # output_apcs.remove(excess.message) left_side, right_side = [], [] _t = PublicKey() # Transaction should contain outputs (while it may not contain inputs) assert len(self.outputs), "Empty outputs" if len(self.inputs): _t.combine([ _input.authorized_pedersen_commitment.to_public_key(). public_key for _input in self.inputs ]) inputs_pedersen_commitment_sum = _t.to_pedersen_commitment() left_side.append(inputs_pedersen_commitment_sum) if len(self.outputs): _t.combine([ _output.authorized_pedersen_commitment.to_public_key(). public_key for _output in self.outputs ]) outputs_pedersen_commitment_sum = _t.to_pedersen_commitment() right_side.append(outputs_pedersen_commitment_sum) _t.combine([ _output.address.pubkey.public_key for _output in self.outputs ]) outputs_excesses_sum = _t.to_pedersen_commitment() left_side.append(outputs_excesses_sum) if len(self.additional_excesses): _t.combine([ excess.pubkey.public_key for excess in self.additional_excesses ]) additional_excesses_sum = _t.to_pedersen_commitment() left_side.append(additional_excesses_sum) if coinbase_num or dev_reward_num: minted_value = 0 if coinbase_num: minted_value += self.coinbase.value if dev_reward_num: minted_value += self.dev_reward.value minted_pc = PedersenCommitment(value_generator=default_generator) minted_pc.create(minted_value, b'\x00' * 32) left_side.append(minted_pc) relay_fee = 0 for _output in self.outputs: if not _output.version == 0: if _output.generator == default_generator_ser: relay_fee += _output.relay_fee new_outputs_fee = self.calc_new_outputs_fee() fee = relay_fee + new_outputs_fee negative_fee = False if fee < 0: # It's okay, transaction has consumed so many inputs that it is profitable by itself # however we need to handle it manually: libsecp256k1 cannot handle negative value negative_fee = True fee = -fee if not fee == 0: fee_pc = PedersenCommitment(value_generator=default_generator ) #TODO think about fees for assets fee_pc.create(fee, b'\x00' * 32) if negative_fee: left_side.append(fee_pc) else: right_side.append(fee_pc) mixer_pc = (Point(default_blinding_generator) * self.mixer_offset).to_pedersen_commitment( ) #TODO we should optimise here and generate fee_mixer pc right_side.append(mixer_pc) checker = PedersenCommitment() # For transaction which contains coinbase only, both sides will be empty if len(left_side) or len(right_side): sum_to_zero = checker.verify_sum(left_side, right_side) assert sum_to_zero, "Non-zero Pedersen commitments summ" if self.coinbase: info = self.coinbase.info() assert info['exp'] == -1, "Non-transparent coinbase" assert self.coinbase.lock_height >= block_height + coinbase_maturity,\ "Wrong coinbase maturity timelock: %d should be %d"%(\ self.coinbase.lock_height, block_height + coinbase_maturity) if self.dev_reward: assert self.dev_reward.lock_height >= block_height + dev_reward_maturity, \ "Wrong dev reward maturity: %d should be %d"%(\ self.dev_reward.lock_height, block_height + dev_reward_maturity) tx_skel = TransactionSkeleton(tx=self) assert len(tx_skel.serialize( rich_format=False)) < 50000, "Too big tx_skeleton" verification_cache[(self.serialize(), block_height)] = True return True
def non_context_verify(self, block_height): #Actually we partially use context via block_height. Consider renaming. assert is_sorted( self.inputs, key=lambda _input: _input.authorized_pedersen_commitment.serialize( )), "Inputs are not sorted" assert is_sorted( self.outputs, key=lambda _output: _output.authorized_pedersen_commitment. serialize()), "Outputs are not sorted" for _input in self.inputs: assert _input.lock_height < block_height, "Timelocked input" #Check that there are no duplicated outputs #TODO probably authorized???? assert len( set([ _output.unauthorized_pedersen_commitment.serialize() for _output in self.outputs ])) == len(self.outputs), "Duplicated output" coinbase_num = 0 output_apcs = [] for output in self.outputs: assert output.verify(), "Nonvalid output" _o_index = output.serialized_index output_apcs.append(_o_index[:33]) if output.version == 0: coinbase_num += 1 self.coinbase = output assert coinbase_num < 2, "More then one coinbase" for excess in self.additional_excesses: assert excess.verify(), "Nonvalid excess" if not excess.message in output_apcs: return False else: output_apcs.remove(excess.message) left_side, right_side = [], [] _t = PublicKey() # Transaction should contain either outputs (while it may not contain inputs) # or combined excesses (for transactions which only delete excesses) assert len(self.outputs) or len( self.combined_excesses), "Empty outputs" if len(self.inputs): _t.combine([ _input.authorized_pedersen_commitment.to_public_key(). public_key for _input in self.inputs ]) inputs_pedersen_commitment_sum = _t.to_pedersen_commitment() left_side.append(inputs_pedersen_commitment_sum) if len(self.outputs): _t.combine([ _output.authorized_pedersen_commitment.to_public_key(). public_key for _output in self.outputs ]) outputs_pedersen_commitment_sum = _t.to_pedersen_commitment() right_side.append(outputs_pedersen_commitment_sum) _t.combine([ _output.address.pubkey.public_key for _output in self.outputs ]) outputs_excesses_sum = _t.to_pedersen_commitment() left_side.append(outputs_excesses_sum) if len(self.additional_excesses): _t.combine([ excess.pubkey.public_key for excess in self.additional_excesses ]) additional_excesses_sum = _t.to_pedersen_commitment() left_side.append(additional_excesses_sum) if coinbase_num: minted_pc = PedersenCommitment(blinded_generator=default_generator) minted_pc.create(self.coinbase.value, b'\x00' * 32) left_side.append(minted_pc) relay_fee = 0 for _output in self.outputs: if not _output.version == 0: if _output.generator == default_generator_ser: relay_fee += _output.relay_fee new_outputs_fee = self.calc_new_outputs_fee() fee = relay_fee + new_outputs_fee negative_fee = False if fee < 0: # It's okay, transaction has consumed so many inputs that it is profitable by itself # however we need to handle it manually: libsecp256k1 cannot handle negative value negative_fee = True fee = -fee if not fee == 0: fee_pc = PedersenCommitment(blinded_generator=default_generator ) #TODO think about fees for assets fee_pc.create(fee, b'\x00' * 32) if negative_fee: left_side.append(fee_pc) else: right_side.append(fee_pc) checker = PedersenCommitment() # For transaction which contains coinbase only, both sides will be empty if len(left_side) or len(right_side): sum_to_zero = checker.verify_sum(left_side, right_side) assert sum_to_zero, "Non-zero Pedersen commitments summ" if not GLOBAL_TEST['skip combined excesses']: raise NotImplemented if self.coinbase: info = self.coinbase.info() assert info['exp'] == -1, "Non-transparent coinbase" # TODO Ugly ->`self.txos_storage.storage_space.blockchain.current_height` assert self.coinbase.lock_height >= block_height + coinbase_maturity,\ "Wrong coinbase maturity timelock: %d should be %d"%(\ self.coinbase.lock_height, block_height + coinbase_maturity) tx_skel = TransactionSkeleton(tx=self) assert len(tx_skel.serialize( rich_format=False)) < 50000, "Too big tx_skeleton" return True