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_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 add_blocks_unlock_reward(self.manager) 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, [out], self.tx_storage) 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_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) 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)
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, 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.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, self.manager.tx_storage) 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_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_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 self.storage.get_all_genesis() 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 add_blocks_unlock_reward(self.manager) new_address = w.get_unused_address() out = WalletOutputInfo(decode_address(new_address), 100, timelock=None) tx1 = w.prepare_transaction_compute_inputs(Transaction, [out], self.storage) 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)