def test_balance_update2(self): # Tx2 is twin with tx1 with equal acc weight, so both will get voided # Start balance self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) # Change of parents only, so it's a twin. # Same weight, so both will be voided then the balance increases tx2 = Transaction.create_from_struct(self.tx1.get_struct()) tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]] tx2.resolve() # Propagate a conflicting twin transaction self.manager.propagate_tx(tx2) self.run_to_completion() meta1 = self.tx1.get_metadata(force_reload=True) self.assertEqual(meta1.twins, [tx2.hash]) self.assertEqual(meta1.voided_by, {self.tx1.hash}) meta2 = tx2.get_metadata() self.assertEqual(meta2.voided_by, {tx2.hash}) # Balance changed self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, sum(self.blocks_tokens[:3])))
def test_balance_update3(self): # Tx2 is twin with tx1 with higher acc weight, so tx1 will get voided # Start balance self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) # Change of parents only, so it's a twin. # With higher weight, so the balance will continue because tx2 will be the winner tx2 = Transaction.create_from_struct(self.tx1.get_struct()) tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]] tx2.weight = 13 tx2.resolve() # Propagate a conflicting twin transaction self.manager.propagate_tx(tx2) self.run_to_completion() meta1 = self.tx1.get_metadata(force_reload=True) self.assertEqual(meta1.twins, [tx2.hash]) self.assertEqual(meta1.voided_by, {self.tx1.hash}) meta2 = tx2.get_metadata() self.assertEqual(meta2.voided_by, None) # Balance is the same self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance))
def test_balance_update1(self): # Tx2 is twin with tx1 but less acc weight, so it will get voided # Start balance self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) # Change of parents only, so it's a twin. # With less weight, so the balance will continue because tx1 will be the winner tx2 = Transaction.create_from_struct(self.tx1.get_struct()) tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]] tx2.weight = 9 tx2.resolve() # Propagate a conflicting twin transaction self.manager.propagate_tx(tx2) self.run_to_completion() meta1 = self.tx1.get_metadata(force_reload=True) self.assertEqual(meta1.twins, [tx2.hash]) meta2 = tx2.get_metadata(force_reload=True) self.assertEqual(meta2.voided_by, {tx2.hash}) # Balance is the same self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) # Voided wallet history index_voided = 0 output_voided = tx2.outputs[index_voided] address = output_voided.to_human_readable()['address'] voided_unspent = UnspentTx(tx2.hash, index_voided, output_voided.value, tx2.timestamp, address, output_voided.token_data, voided=True) self.assertEqual(len(self.manager.wallet.voided_unspent), 1) voided_utxo = self.manager.wallet.voided_unspent.get( (voided_unspent.tx_id, index_voided)) self.assertIsNotNone(voided_utxo) self.assertEqual(voided_utxo.to_dict(), voided_unspent.to_dict()) input_voided = tx2.inputs[0] key = (input_voided.tx_id, input_voided.index) voided_spent = SpentTx(tx2.hash, input_voided.tx_id, input_voided.index, self.blocks_tokens[0], tx2.timestamp, voided=True) self.assertEqual(len(self.manager.wallet.voided_spent), 1) self.assertEqual(len(self.manager.wallet.voided_spent[key]), 1) self.assertEqual(self.manager.wallet.voided_spent[key][0].to_dict(), voided_spent.to_dict())
def test_balance_update4(self): # Tx2 spends Tx1 output # Tx3 is twin of Tx2 with same acc weight, so both will get voided self.manager.reactor.advance(1) # Start balance self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) address = self.manager.wallet.get_unused_address_bytes() value = self.blocks_tokens[0] - 100 inputs = [WalletInputInfo(tx_id=self.tx1.hash, index=0, private_key=None)] outputs = [WalletOutputInfo(address=address, value=int(value), timelock=None)] tx2 = self.manager.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs, outputs, self.manager.tx_storage) tx2.weight = 10 tx2.parents = [self.tx1.hash, self.tx1.parents[0]] tx2.timestamp = int(self.clock.seconds()) tx2.resolve() self.manager.propagate_tx(tx2) self.run_to_completion() # Test create same tx with allow double spending with self.assertRaises(PrivateKeyNotFound): self.manager.wallet.prepare_transaction_incomplete_inputs( Transaction, inputs=inputs, outputs=outputs, tx_storage=self.manager.tx_storage ) self.manager.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs=inputs, outputs=outputs, force=True, tx_storage=self.manager.tx_storage) # Change of parents only, so it's a twin. tx3 = Transaction.create_from_struct(tx2.get_struct()) tx3.parents = [tx2.parents[1], tx2.parents[0]] tx3.resolve() # Propagate a conflicting twin transaction self.manager.propagate_tx(tx3) self.run_to_completion() meta2 = tx2.get_metadata(force_reload=True) self.assertEqual(meta2.twins, [tx3.hash]) self.assertEqual(meta2.voided_by, {tx2.hash}) meta3 = tx3.get_metadata() self.assertEqual(meta3.voided_by, {tx3.hash}) # Balance is the same self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance))
def test_choose_inputs(self): blocks = add_new_blocks(self.manager, 1, advance_clock=15) blocks_tokens = [ sum(txout.value for txout in blk.outputs) for blk in blocks ] add_blocks_unlock_reward(self.manager) address = self.manager.wallet.get_unused_address(mark_as_used=False) outputs = [ WalletOutputInfo(address=decode_address(address), value=blocks_tokens[0], timelock=int(self.clock.seconds()) + 10) ] tx1 = self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs, self.manager.tx_storage) tx1.weight = 10 tx1.parents = self.manager.get_new_tx_parents() tx1.timestamp = int(self.clock.seconds()) tx1.resolve() self.manager.propagate_tx(tx1) self.clock.advance(1) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(blocks_tokens[0], 0)) outputs = [ WalletOutputInfo(address=decode_address(address), value=blocks_tokens[0], timelock=None) ] with self.assertRaises(InsufficientFunds): self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs, self.manager.tx_storage) self.clock.advance(10) tx2 = self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs, self.manager.tx_storage) tx2.weight = 10 tx2.parents = self.manager.get_new_tx_parents() tx2.timestamp = int(self.clock.seconds()) tx2.resolve() self.manager.propagate_tx(tx2) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, blocks_tokens[0]))
def test_balance_update5(self): # Tx2 spends Tx1 output # Tx3 is twin of Tx1, with less acc weight # So we have conflict between all three txs but tx1 and tx2 are winners and tx3 is voided self.clock.advance(1) # Start balance self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) address = self.manager.wallet.get_unused_address_bytes() value = self.blocks_tokens[0] - 100 inputs = [ WalletInputInfo(tx_id=self.tx1.hash, index=0, private_key=None) ] outputs = [ WalletOutputInfo(address=address, value=int(value), timelock=None) ] tx2 = self.manager.wallet.prepare_transaction_incomplete_inputs( Transaction, inputs, outputs, self.manager.tx_storage) tx2.weight = 10 tx2.parents = [self.tx1.hash, self.tx1.parents[0]] tx2.timestamp = int(self.clock.seconds()) tx2.resolve() # Change of parents only, so it's a twin. tx3 = Transaction.create_from_struct(self.tx1.get_struct()) tx3.parents = [self.tx1.parents[1], self.tx1.parents[0]] tx3.resolve() # Propagate a conflicting twin transaction self.manager.propagate_tx(tx2) self.manager.propagate_tx(tx3) self.run_to_completion() meta2 = tx2.get_metadata() self.assertEqual(meta2.twins, []) self.assertEqual(meta2.voided_by, None) meta3 = tx3.get_metadata() self.assertEqual(meta3.voided_by, {tx3.hash}) self.assertEqual(meta3.twins, [self.tx1.hash]) # Balance is the same self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance))
def test_transaction_and_balance(self): # generate a new block and check if we increase balance new_address = self.wallet.get_unused_address() out = WalletOutputInfo(decode_address(new_address), self.TOKENS, timelock=None) block = add_new_block(self.manager) block.verify() utxo = self.wallet.unspent_txs[settings.HATHOR_TOKEN_UID].get((block.hash, 0)) self.assertIsNotNone(utxo) self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.BLOCK_TOKENS)) # create transaction spending this value, but sending to same wallet new_address2 = self.wallet.get_unused_address() out = WalletOutputInfo(decode_address(new_address2), self.TOKENS, timelock=None) tx1 = self.wallet.prepare_transaction_compute_inputs(Transaction, outputs=[out]) tx1.update_hash() tx1.verify_script(tx1.inputs[0], block) tx1.storage = self.tx_storage self.wallet.on_new_tx(tx1) self.tx_storage.save_transaction(tx1) self.assertEqual(len(self.wallet.spent_txs), 1) utxo = self.wallet.unspent_txs[settings.HATHOR_TOKEN_UID].get((tx1.hash, 0)) self.assertIsNotNone(utxo) self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.TOKENS)) # pass inputs and outputs to prepare_transaction, but not the input keys # spend output last transaction input_info = WalletInputInfo(tx1.hash, 0, None) new_address3 = self.wallet.get_unused_address() out = WalletOutputInfo(decode_address(new_address3), self.TOKENS, timelock=None) tx2 = self.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs=[input_info], outputs=[out], tx_storage=self.tx_storage) tx2.storage = self.tx_storage tx2.update_hash() tx2.storage = self.tx_storage tx2.verify_script(tx2.inputs[0], tx1) self.tx_storage.save_transaction(tx2) self.wallet.on_new_tx(tx2) self.assertEqual(len(self.wallet.spent_txs), 2) self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.TOKENS)) # Test getting more unused addresses than the gap limit for i in range(3): kwargs = {'mark_as_used': True} if i == 2: # Last one we dont mark as used kwargs['mark_as_used'] = False self.wallet.get_unused_address(**kwargs)
def test_tokens_balance(self): # create tokens and check balances # initial tokens address_b58 = self.manager.wallet.get_unused_address() address = decode_address(address_b58) tx = create_tokens(self.manager, address_b58) token_id = tx.tokens[0] amount = tx.outputs[0].value # initial token balance self.assertEqual(self.manager.wallet.balance[token_id], WalletBalance(0, amount)) # initial hathor balance # we don't consider HTR balance 0 because we transfer genesis tokens to this # wallet during token creation hathor_balance = self.manager.wallet.balance[settings.HATHOR_TOKEN_UID] # transfer token to another wallet and check balance again parents = self.manager.get_new_tx_parents() _input1 = TxInput(tx.hash, 0, b'') script = P2PKH.create_output_script(address) token_output1 = TxOutput(30, b'', 0b00000001) token_output2 = TxOutput(amount - 30, script, 0b00000001) tx2 = Transaction(weight=1, inputs=[_input1], outputs=[token_output1, token_output2], parents=parents, tokens=[token_id], storage=self.manager.tx_storage, timestamp=int(self.manager.reactor.seconds())) data_to_sign = tx2.get_sighash_all(clear_input_data=True) public_bytes, signature = self.manager.wallet.get_input_aux_data( data_to_sign, self.manager.wallet.get_private_key(address_b58)) tx2.inputs[0].data = P2PKH.create_input_data(public_bytes, signature) tx2.resolve() tx2.verify() self.manager.propagate_tx(tx2) self.run_to_completion() # verify balance self.assertEqual(self.manager.wallet.balance[token_id], WalletBalance(0, amount - 30)) # hathor balance remains the same self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], hathor_balance)
def test_balance(self): self.factory.connections.add(self.protocol) self.protocol.state = HathorAdminWebsocketProtocol.STATE_OPEN self.manager.pubsub.publish(HathorEvents.WALLET_BALANCE_UPDATED, balance={settings.HATHOR_TOKEN_UID: WalletBalance(10, 20)}) self.run_to_completion() value = self._decode_value(self.transport.value()) self.assertEqual(value['balance']['locked'], 10) self.assertEqual(value['balance']['available'], 20) self.assertEqual(value['type'], 'wallet:balance_updated')
def test_balance_update6(self): # Tx2 is twin of tx1, so both voided # Tx3 has tx1 as parent, so increases tx1 acc weight, then tx1 is winner against tx2 self.manager.reactor.advance(1) # Start balance self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) # Change of parents only, so it's a twin. tx2 = Transaction.create_from_struct(self.tx1.get_struct()) tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]] tx2.resolve() address = self.get_address(0) value = 100 outputs = [ WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None) ] tx3 = self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs) tx3.weight = 10 tx3.parents = [self.tx1.hash, self.tx1.parents[0]] tx3.timestamp = int(self.clock.seconds()) tx3.resolve() # Propagate a conflicting twin transaction self.manager.propagate_tx(tx2) self.manager.propagate_tx(tx3) self.run_to_completion() # Balance is the same self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance - 100))
def test_block_increase_balance(self): # generate a new block and check if we increase balance w = Wallet(directory=self.directory) w.unlock(PASSWORD) new_address = w.get_unused_address() key = w.keys[new_address] out = WalletOutputInfo(decode_address(key.address), BLOCK_REWARD, timelock=None) tx = w.prepare_transaction(Transaction, inputs=[], outputs=[out]) tx.update_hash() w.on_new_tx(tx) utxo = w.unspent_txs[settings.HATHOR_TOKEN_UID].get((tx.hash, 0)) self.assertIsNotNone(utxo) self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, BLOCK_REWARD))
def test_spend_multisig(self): # Adding funds to the wallet # XXX: note further down the test, 20.00 HTR will be used, block_count must yield at least that amount block_count = 3 # 3 * 8.00 -> 24.00 HTR is enough blocks = add_new_blocks(self.manager, block_count, advance_clock=15) add_blocks_unlock_reward(self.manager) blocks_tokens = [sum(txout.value for txout in blk.outputs) for blk in blocks] available_tokens = sum(blocks_tokens) self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, available_tokens)) # First we send tokens to a multisig address block_reward = blocks_tokens[0] outputs = [WalletOutputInfo(address=self.multisig_address, value=block_reward, timelock=None)] tx1 = self.manager.wallet.prepare_transaction_compute_inputs(Transaction, outputs, self.manager.tx_storage) tx1.weight = 10 tx1.parents = self.manager.get_new_tx_parents() tx1.timestamp = int(self.clock.seconds()) tx1.resolve() self.manager.propagate_tx(tx1) self.clock.advance(10) wallet_balance = WalletBalance(0, available_tokens - block_reward) self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], wallet_balance) # Then we create a new tx that spends this tokens from multisig wallet tx = Transaction.create_from_struct(tx1.get_struct()) tx.weight = 10 tx.parents = self.manager.get_new_tx_parents() tx.timestamp = int(self.clock.seconds()) multisig_script = create_output_script(self.multisig_address) multisig_output = TxOutput(200, multisig_script) wallet_output = TxOutput(300, create_output_script(self.address)) outside_output = TxOutput(block_reward - 200 - 300, create_output_script(self.outside_address)) tx.outputs = [multisig_output, wallet_output, outside_output] tx_input = TxInput(tx1.hash, 0, b'') tx.inputs = [tx_input] signatures = [] for private_key_hex in self.private_keys: signature = generate_signature(tx, bytes.fromhex(private_key_hex), password=b'1234') signatures.append(signature) parser = create_parser() # Generate spend tx args = parser.parse_args([ tx.get_struct().hex(), '{},{}'.format(signatures[0].hex(), signatures[1].hex()), self.redeem_script.hex() ]) f = StringIO() with capture_logs(): with redirect_stdout(f): execute(args) # Transforming prints str in array output = f.getvalue().strip().splitlines() tx_raw = output[0].split(':')[1].strip() tx = Transaction.create_from_struct(bytes.fromhex(tx_raw)) self.assertTrue(self.manager.propagate_tx(tx, False))
def test_spend_multisig(self): # Adding funds to the wallet blocks = add_new_blocks(self.manager, 2, advance_clock=15) add_blocks_unlock_reward(self.manager) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, sum(blk.outputs[0].value for blk in blocks))) first_block_amount = blocks[0].outputs[0].value # First we send tokens to a multisig address outputs = [ WalletOutputInfo(address=self.multisig_address, value=first_block_amount, timelock=int(self.clock.seconds()) + 15) ] tx1 = self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs) tx1.weight = 10 tx1.parents = self.manager.get_new_tx_parents() tx1.timestamp = int(self.clock.seconds()) tx1.resolve() self.manager.propagate_tx(tx1) self.clock.advance(10) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, first_block_amount)) # Then we create a new tx that spends this tokens from multisig wallet tx = Transaction.create_from_struct(tx1.get_struct()) tx.weight = 10 tx.parents = self.manager.get_new_tx_parents() tx.timestamp = int(self.clock.seconds()) multisig_script = create_output_script(self.multisig_address) multisig_output = TxOutput(200, multisig_script) wallet_output = TxOutput(300, create_output_script(self.address)) outside_output = TxOutput(first_block_amount - 200 - 300, create_output_script(self.outside_address)) tx.outputs = [multisig_output, wallet_output, outside_output] tx_input = TxInput(tx1.hash, 0, b'') tx.inputs = [tx_input] signatures = [] for private_key_hex in self.private_keys: signature = generate_signature(tx, bytes.fromhex(private_key_hex), password=b'1234') signatures.append(signature) input_data = MultiSig.create_input_data(self.redeem_script, signatures) tx.inputs[0].data = input_data tx.resolve() # Transaction is still locked self.assertFalse(self.manager.propagate_tx(tx)) self.clock.advance(6) tx.timestamp = int(self.clock.seconds()) tx.resolve() # First we try to propagate with a P2PKH input private_key_obj = get_private_key_from_bytes(bytes.fromhex( self.private_keys[0]), password=b'1234') pubkey_obj = private_key_obj.public_key() public_key_compressed = get_public_key_bytes_compressed(pubkey_obj) p2pkh_input_data = P2PKH.create_input_data(public_key_compressed, signatures[0]) tx2 = Transaction.create_from_struct(tx.get_struct()) tx2.inputs[0].data = p2pkh_input_data tx2.resolve() self.assertFalse(self.manager.propagate_tx(tx2)) # Now we propagate the correct self.assertTrue(self.manager.propagate_tx(tx)) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, first_block_amount + 300)) # Testing the MultiSig class methods cls_script = parse_address_script(multisig_script) self.assertTrue(isinstance(cls_script, MultiSig)) self.assertEqual(cls_script.address, self.multisig_address_b58) expected_dict = { 'type': 'MultiSig', 'address': self.multisig_address_b58, 'timelock': None } self.assertEqual(cls_script.to_human_readable(), expected_dict) script_eval(tx, tx_input, tx1) # Script error with self.assertRaises(ScriptError): create_output_script( base58.b58decode('55d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'))
def test_balance_update_twin_tx(self): # Start balance self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance)) wallet_address = self.manager.wallet.get_unused_address() outputs2 = [ WalletOutputInfo(address=decode_address(wallet_address), value=500, timelock=None) ] tx2 = self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs2) tx2.weight = 10 tx2.parents = self.manager.get_new_tx_parents() tx2.timestamp = int(self.clock.seconds()) tx2.resolve() self.manager.propagate_tx(tx2) self.run_to_completion() outputs3 = [ WalletOutputInfo(address=decode_address(wallet_address), value=self.blocks_tokens[0], timelock=None) ] tx3 = self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs3) tx3.weight = 10 tx3.parents = self.manager.get_new_tx_parents() tx3.timestamp = int(self.clock.seconds()) tx3.resolve() self.manager.propagate_tx(tx3) self.run_to_completion() self.clock.advance(1) new_address = self.manager.wallet.get_unused_address_bytes() inputs = [WalletInputInfo(tx_id=tx3.hash, index=0, private_key=None)] outputs = [ WalletOutputInfo(address=new_address, value=self.blocks_tokens[0], timelock=None) ] tx4 = self.manager.wallet.prepare_transaction_incomplete_inputs( Transaction, inputs, outputs, self.manager.tx_storage) tx4.weight = 10 tx4.parents = [tx3.hash, tx3.parents[0]] tx4.timestamp = int(self.clock.seconds()) tx4.resolve() self.manager.propagate_tx(tx4) self.run_to_completion() # Change of parents only, so it's a twin. tx5 = Transaction.create_from_struct(tx4.get_struct()) tx5.parents = [tx4.parents[1], tx4.parents[0]] tx5.weight = 10 tx5.resolve() # Propagate a conflicting twin transaction self.manager.propagate_tx(tx5) self.run_to_completion() meta4 = tx4.get_metadata(force_reload=True) self.assertEqual(meta4.twins, [tx5.hash]) meta5 = tx5.get_metadata(force_reload=True) self.assertEqual(meta5.voided_by, {tx5.hash}) # Balance is the same self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.initial_balance))
def test_wallet_create_transaction(self): genesis_private_key_bytes = get_private_key_bytes( self.genesis_private_key, encryption_algorithm=serialization.BestAvailableEncryption( PASSWORD)) genesis_address = get_address_b58_from_public_key( self.genesis_public_key) # create wallet with genesis block key key_pair = KeyPair(private_key_bytes=genesis_private_key_bytes, address=genesis_address, used=True) keys = {} keys[key_pair.address] = key_pair w = Wallet(keys=keys, directory=self.directory) w.unlock(PASSWORD) genesis_blocks = [ tx for tx in get_genesis_transactions(None) if tx.is_block ] genesis_block = genesis_blocks[0] genesis_value = sum([output.value for output in genesis_block.outputs]) # wallet will receive genesis block and store in unspent_tx w.on_new_tx(genesis_block) for index in range(len(genesis_block.outputs)): utxo = w.unspent_txs[settings.HATHOR_TOKEN_UID].get( (genesis_block.hash, index)) self.assertIsNotNone(utxo) self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, genesis_value)) # create transaction spending this value, but sending to same wallet new_address = w.get_unused_address() out = WalletOutputInfo(decode_address(new_address), 100, timelock=None) tx1 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out]) tx1.storage = self.storage tx1.update_hash() self.storage.save_transaction(tx1) w.on_new_tx(tx1) self.assertEqual(len(w.spent_txs), 1) self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, genesis_value)) # pass inputs and outputs to prepare_transaction, but not the input keys # spend output last transaction input_info = WalletInputInfo(tx1.hash, 1, None) new_address = w.get_unused_address() key2 = w.keys[new_address] out = WalletOutputInfo(decode_address(key2.address), 100, timelock=None) tx2 = w.prepare_transaction_incomplete_inputs(Transaction, inputs=[input_info], outputs=[out], tx_storage=self.storage) tx2.storage = self.storage tx2.update_hash() self.storage.save_transaction(tx2) w.on_new_tx(tx2) self.assertEqual(len(w.spent_txs), 2) self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, genesis_value)) # test keypair exception with self.assertRaises(WalletLocked): key_pair.get_private_key(None)
def test_timelock(self): blocks = add_new_blocks(self.manager, 5, advance_clock=15) blocks_tokens = [ sum(txout.value for txout in blk.outputs) for blk in blocks ] add_blocks_unlock_reward(self.manager) address = self.manager.wallet.get_unused_address() outside_address = self.get_address(0) outputs = [ WalletOutputInfo(address=decode_address(address), value=500, timelock=int(self.clock.seconds()) + 10), WalletOutputInfo(address=decode_address(address), value=700, timelock=int(self.clock.seconds()) - 10), WalletOutputInfo(address=decode_address(address), value=sum(blocks_tokens[:2]) - 500 - 700, timelock=None) ] tx1 = self.manager.wallet.prepare_transaction_compute_inputs( Transaction, outputs, self.manager.tx_storage) tx1.weight = 10 tx1.parents = self.manager.get_new_tx_parents() tx1.timestamp = int(self.clock.seconds()) tx1.resolve() self.manager.propagate_tx(tx1) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(500, sum(blocks_tokens) - 500)) self.clock.advance(1) outputs1 = [ WalletOutputInfo(address=decode_address(outside_address), value=500, timelock=None) ] inputs1 = [WalletInputInfo(tx_id=tx1.hash, index=0, private_key=None)] tx2 = self.manager.wallet.prepare_transaction_incomplete_inputs( Transaction, inputs1, outputs1, self.manager.tx_storage) tx2.weight = 10 tx2.parents = self.manager.get_new_tx_parents() tx2.timestamp = int(self.clock.seconds()) tx2.resolve() propagated = self.manager.propagate_tx(tx2) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(500, sum(blocks_tokens) - 500)) self.assertFalse(propagated) self.clock.advance(1) outputs2 = [ WalletOutputInfo(address=decode_address(outside_address), value=700, timelock=None) ] inputs2 = [WalletInputInfo(tx_id=tx1.hash, index=1, private_key=None)] tx3 = self.manager.wallet.prepare_transaction_incomplete_inputs( Transaction, inputs2, outputs2, self.manager.tx_storage) tx3.weight = 10 tx3.parents = self.manager.get_new_tx_parents() tx3.timestamp = int(self.clock.seconds()) tx3.resolve() propagated = self.manager.propagate_tx(tx3, False) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(500, sum(blocks_tokens) - 500 - 700)) self.assertTrue(propagated) self.clock.advance(1) outputs3 = [ WalletOutputInfo(address=decode_address(outside_address), value=sum(blocks_tokens[:2]) - 500 - 700, timelock=None) ] inputs3 = [WalletInputInfo(tx_id=tx1.hash, index=2, private_key=None)] tx4 = self.manager.wallet.prepare_transaction_incomplete_inputs( Transaction, inputs3, outputs3, self.manager.tx_storage) tx4.weight = 10 tx4.parents = self.manager.get_new_tx_parents() tx4.timestamp = int(self.clock.seconds()) tx4.resolve() propagated = self.manager.propagate_tx(tx4, False) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(500, sum(blocks_tokens[:3]))) self.assertTrue(propagated) self.clock.advance(8) tx2.timestamp = int(self.clock.seconds()) tx2.resolve() propagated = self.manager.propagate_tx(tx2, False) self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, sum(blocks_tokens[:3]))) self.assertTrue(propagated)