def give_block_template(self): if not self.key_manager: raise Exception("Key manager is not set") transaction_fees = self.give_tx().relay_fee if self.give_tx() else 0 value = next_reward( self.storage_space.blockchain.current_tip, self.storage_space.headers_storage) + transaction_fees coinbase = IOput() coinbase.fill( self.key_manager.new_address(), value, relay_fee=0, coinbase=True, lock_height=self.storage_space.blockchain.current_height + 1 + coinbase_maturity) coinbase.generate() self.storage_space.txos_storage.mempool[ coinbase.serialized_index] = coinbase tx = Transaction(txos_storage=self.storage_space.txos_storage, key_manager=self.key_manager) tx.add_coinbase(coinbase) tx.compose_block_transaction() block = generate_block_template(tx, self.storage_space) self.add_block_template(block) return block
def test_ioput_indexes(): _input1 = IOput() _input1.fill(adr1, 100) _input1.generate(exp=2, concealed_bits=3) assert _input1.index_len == 32 + 33 assert len(_input1.serialized_index) == 32 + 33 assert len(_input1.commitment_index) == 32 + 33 print("ioput_indexes len OK")
def ioput_encrypted_messsage(): _input1 = IOput() _input1.fill(adr1, 100) _input1.generate(exp=2, concealed_bits=3) _input2 = IOput(binary_object=_input1.serialize()) _input2.verify() _input2.detect_value({'priv_by_pub': {adr1.serialized_pubkey: keys[0]}}) assert _input1.value == _input2.value assert _input1.blinding_key.serialize() == _input2.blinding_key.serialize() print("ioput_encrypted_message OK")
def generate(self, change_address=None): if self.coinbase: raise Exception( "generate() can be used only for common transaction, to create block transaction as miner use compose_block_transaction" ) if not len(self.inputs): raise Exception("Tx should have at least one input") if not len(self._destinations): raise Exception("Tx should have at least one destination") for ioput in self.inputs: if not self.key_manager: raise Exception( "Trying to generate tx which spends unknown input (KeyManager is None)" ) if not ioput.detect_value(self.key_manager): raise Exception( "Trying to generate tx which spends unknown input") in_value = sum([ioput.value for ioput in self.inputs]) out_value = sum([destination[1] for destination in self._destinations]) relay_fee = self.calc_relay_fee() # +1 for destination is for change address self.fee = relay_fee + self.calc_new_outputs_fee( len(self.inputs), len(self._destinations) + 1) remainder = in_value - out_value - self.fee if remainder < 0: raise Exception("Not enough money in inputs to cover outputs") # TODO We need logic here to cover too low remainders (less than new output fee) change_address = change_address if change_address else self.key_manager.new_address( ) # TODO Check: for first glance, we cannot get here if key_manager is None. self._destinations.append((change_address, remainder)) privkey_sum = 0 out_blinding_key_sum = None for out_index in range(len(self._destinations) - 1): address, value = self._destinations[out_index] output = IOput() output.fill(address, value, generator=default_generator_ser) self.outputs.append(output) out_blinding_key_sum = out_blinding_key_sum + output.blinding_key if out_blinding_key_sum else output.blinding_key # privkey for the last one output isn't arbitrary address, value = self._destinations[-1] in_blinding_key_sum = None for _input in self.inputs: in_blinding_key_sum = in_blinding_key_sum + _input.blinding_key if in_blinding_key_sum else _input.blinding_key in_blinding_key_sum += self.key_manager.priv_by_pub( _input.address.pubkey ) # we can't get here if key_manager is None output = IOput() output.fill( address, value, blinding_key=in_blinding_key_sum - out_blinding_key_sum, relay_fee=relay_fee, generator=default_generator_ser ) #TODO relay fee should be distributed uniformly, privacy leak self.outputs.append(output) [output.generate() for output in self.outputs] self.sort_ioputs() self.verify()
def ioput_serialize_deserialize(): options = { 'address': [adr1], 'value': [1, 100, int(705e6 * 1e8)], 'relay_fee': [0, 100, int(1e8)], 'generator': [ None ], #b'\x0b\xf8x*\xe9\xdc\xb1\xfd\xe9k\x8eZ\xf9\x8250\xdcrLU`p\xbaD\xf1\xfdh\x93\xd7\x85\xb9\x9e\x07'], #TODO arbitraty value gen 'burden_hash': [None, b"\x44" * 32], 'coinbase': [True, False], 'lock_height': [0, 10, 10000] } all_possible_options = [{}] for opt in options: c = all_possible_options all_possible_options = [] for i in options[opt]: for prev in c: n = prev.copy() if i != None: n[opt] = i all_possible_options.append(n) for var in all_possible_options: _input1 = IOput() _input1.fill(**var) _input1.generate() _input2 = IOput(binary_object=_input1.serialize()) _input2.verify() assert _input1.version == _input2.version assert _input1.block_version == _input2.block_version assert _input1.lock_height == _input2.lock_height assert _input1.authorized_pedersen_commitment.serialize( ) == _input2.authorized_pedersen_commitment.serialize() assert _input1.address.serialize() == _input2.address.serialize() assert _input1.rangeproof.proof == _input2.rangeproof.proof assert _input1.encrypted_message == _input2.encrypted_message assert _input1.generator == _input2.generator assert _input1.relay_fee == _input2.relay_fee assert _input1.authorized_burden == _input2.authorized_burden print("ioput_serialize_deserialize OK")
def ioput_proofs_info(): _input1 = IOput() _input1.fill(adr1, 100) _input1.version = 1 #To use RangeProof instead of BulletProofs _input1.generate(exp=2, concealed_bits=3) info = _input1.info() assert info['exp'] == 2 assert info['min_value'] == 0 assert info['max_value'] == 700 _input1 = IOput() _input1.fill(adr1, 3) _input1.version = 1 #To use RangeProof instead of BulletProofs _input1.generate(exp=0, concealed_bits=3, min_value=1) info = _input1.info() assert info['exp'] == 0 assert info['mantissa'] == 3 assert info['min_value'] == 1 assert info['max_value'] == 8 _input1 = IOput() _input1.fill(adr1, 300) _input1.version = 1 #To use RangeProof instead of BulletProofs #coceanled bits cannot be less than bits in value _input1.generate(exp=0, concealed_bits=3) info = _input1.info() assert info['exp'] == 0 assert info['mantissa'] == 9 assert info['min_value'] == 0 assert info['max_value'] == 511 print("ioput_proofs_info OK") _input1 = IOput() _input1.fill(adr1, 300) _input1.generate() info = _input1.info() assert info['exp'] == 0 assert info['mantissa'] == 0 assert info['min_value'] == 0 assert info['max_value'] == 2**64 - 1
def blindly_generate(self, change_address, input_data, relay_fee_per_kb=0): self.serialized = None if self.coinbase: raise Exception( "generate() can be used only for common transaction, to create block transaction as miner use compose_block_transaction" ) if not len(input_data): raise Exception("Tx should have at least one input") if not len(self._destinations): raise Exception("Tx should have at least one destination") in_value = sum([ioput[1] for ioput in input_data]) out_value = sum([destination[1] for destination in self._destinations]) relay_fee = self.calc_relay_fee(relay_fee_per_kb=relay_fee_per_kb) # +1 for destination is for change address self.fee = relay_fee + self.calc_new_outputs_fee( len(input_data), len(self._destinations) + 1) remainder = in_value - out_value - self.fee if remainder < 0: raise Exception("Not enough money in inputs to cover outputs") # TODO We need logic here to cover too low remainders (less than new output fee) self._destinations.append((change_address, remainder, True)) privkey_sum = 0 out_blinding_key_sum = None need_proofs = [] excesses_key_sum = None for out_index in range(len(self._destinations) - 1): address, value, need_proof = self._destinations[out_index] output = IOput() output.fill(address, value, generator=default_generator_ser) self.outputs.append(output) out_blinding_key_sum = ( out_blinding_key_sum + output.blinding_key ) if out_blinding_key_sum else output.blinding_key if need_proof: need_proofs.append( (output, PrivateKey() )) #excesses will be generated after output generation offset_pk = PrivateKey() self.mixer_offset = int.from_bytes(offset_pk.private_key, "big") # privkey for the last one output isn't arbitrary address, value, need_proof = self._destinations[-1] if need_proof: need_proofs.append( (output, PrivateKey() )) #excesses will be generated after output generation in_blinding_key_sum = None burdens_to_be_covered = [] for i, v, priv_key, blinding_key, ser_apc in input_data: in_blinding_key_sum = ( in_blinding_key_sum + blinding_key) if in_blinding_key_sum else blinding_key in_blinding_key_sum += priv_key self.updated_excesses[i] = excess_from_private_key( priv_key, b"\x01\x00" + ser_apc) self.inputs.append(pseudoinput( i, ser_apc)) #For tx serialization and sorts if len(need_proofs): excesses_key_sum = need_proofs[0][1] for i in need_proofs[1:]: excesses_key_sum += i[1] output = IOput() last_blinding_key = in_blinding_key_sum - out_blinding_key_sum - offset_pk if excesses_key_sum: last_blinding_key += excesses_key_sum output.fill( address, value, blinding_key=last_blinding_key, relay_fee=relay_fee, generator=default_generator_ser ) #TODO relay fee should be distributed uniformly, privacy leak self.outputs.append(output) [output.generate() for output in self.outputs] for ae in need_proofs: script = generate_proof_script(ae[0]) e = excess_from_private_key(ae[1], script) self.additional_excesses.append(e) self.sort_lists()