def test_revert_slave_txn(self): tx = SlaveTransaction.create(**self.params) tx.sign(self.alice) addresses_state = dict(self.addresses_state) addresses_state[self.alice.address].pbdata.balance = 100 state_container = StateContainer(addresses_state=addresses_state, tokens=Indexer(b'token', None), slaves=Indexer(b'slave', None), lattice_pk=Indexer( b'lattice_pk', None), multi_sig_spend_txs=dict(), votes_stats=dict(), block_number=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) tx.apply(self.state, state_container) tx.revert(self.state, state_container) self.assertEqual(addresses_state[self.alice.address].balance, 100) storage_key = state_container.paginated_tx_hash.generate_key( self.alice.address, 1) self.assertIn(storage_key, state_container.paginated_tx_hash.key_value) self.assertEqual( [], state_container.paginated_tx_hash.key_value[storage_key]) self.assertIn((tx.addr_from, tx.slave_pks[0]), state_container.slaves.data) data = state_container.slaves.data[(tx.addr_from, tx.slave_pks[0])] self.assertIsInstance(data, SlaveMetadata) self.assertEqual(tx.access_types[0], data.access_type) self.assertEqual(tx.txhash, data.tx_hash)
def generate_slave_tx(self, signer_pk: bytes, slave_pk_list: list, master_addr=None): return SlaveTransaction.create(slave_pks=slave_pk_list, access_types=[0] * len(slave_pk_list), fee=0, xmss_pk=signer_pk, master_addr=master_addr)
def test_broadcast_tx(self, m_reactor, m_logger): # broadcast_tx() should handle all Transaction Types self.factory.broadcast_tx(MessageTransaction()) self.factory.broadcast_tx(TransferTransaction()) self.factory.broadcast_tx(TokenTransaction()) self.factory.broadcast_tx(TransferTokenTransaction()) self.factory.broadcast_tx(SlaveTransaction()) with self.assertRaises(ValueError): m_tx = Mock(autospec=TransferTransaction, txhash=bhstr2bin('deadbeef')) self.factory.broadcast_tx(m_tx)
def create_slave_tx(slave_pks: list, access_types: list, fee: int, xmss_pk: bytes, master_addr: bytes) -> SlaveTransaction: return SlaveTransaction.create(slave_pks=slave_pks, access_types=access_types, fee=fee, xmss_pk=xmss_pk, master_addr=master_addr)
def test_validate_custom(self, m_logger): """ SlaveTransaction._validate_custom() checks for the following things: 1. if you specify more than 100 slave_pks at once 2. if len(slave_pks) != len(access_types) 3. access_types can only be 0, 1 """ # Unequal length slave_pks and access_types params = self.params.copy() params["slave_pks"] = [self.slave.pk] params["access_types"] = [0, 1] with self.assertRaises(ValueError): SlaveTransaction.create(**params) # access_type is a weird, undefined number params = self.params.copy() params["access_types"] = [5] with self.assertRaises(ValueError): SlaveTransaction.create(**params)
def test_validate_tx(self, m_logger): tx = SlaveTransaction.create(**self.params) tx.sign(self.alice) self.assertTrue(tx.validate_or_raise()) tx._data.transaction_hash = b'abc' # Should fail, as we have modified with invalid transaction_hash with self.assertRaises(ValueError): tx.validate_or_raise()
def slave_tx_generate(ctx, src, master, number_of_slaves, access_type, fee, pk, ots_key_index): """ Generates Slave Transaction for the wallet """ try: _, src_xmss = _select_wallet(ctx, src) ots_key_index = validate_ots_index(ots_key_index, src_xmss) src_xmss.set_ots_index(ots_key_index) if src_xmss: address_src_pk = src_xmss.pk else: address_src_pk = pk.encode() master_addr = None if master: master_addr = parse_qaddress(master) fee_shor = _quanta_to_shor(fee) except Exception as e: click.echo("Error validating arguments: {}".format(e)) quit(1) slave_xmss = [] slave_pks = [] access_types = [] slave_xmss_seed = [] if number_of_slaves > 100: click.echo("Error: Max Limit for the number of slaves is 100") quit(1) for i in range(number_of_slaves): print("Generating Slave #" + str(i + 1)) xmss = XMSS.from_height(config.dev.xmss_tree_height) slave_xmss.append(xmss) slave_xmss_seed.append(xmss.extended_seed) slave_pks.append(xmss.pk) access_types.append(access_type) print("Successfully Generated Slave %s/%s" % (str(i + 1), number_of_slaves)) try: tx = SlaveTransaction.create(slave_pks=slave_pks, access_types=access_types, fee=fee_shor, xmss_pk=address_src_pk, master_addr=master_addr) tx.sign(src_xmss) with open('slaves.json', 'w') as f: json.dump([bin2hstr(src_xmss.address), slave_xmss_seed, tx.to_json()], f) click.echo('Successfully created slaves.json') click.echo('Move slaves.json file from current directory to the mining node inside ~/.xrd/') except Exception as e: click.echo("Unhandled error: {}".format(str(e))) quit(1)
def relay_slave_txn(self, slave_pks: list, access_types: list, fee: int, master_qaddress, signer_address: str, ots_index: int): self.authenticate() index, xmss = self._get_wallet_index_xmss(signer_address, ots_index) self.verify_ots(signer_address, xmss, user_ots_index=ots_index) tx = SlaveTransaction.create( slave_pks=slave_pks, access_types=access_types, fee=fee, xmss_pk=xmss.pk, master_addr=self.qaddress_to_address(master_qaddress)) self.sign_and_push_transaction(tx, xmss, index) return self.to_plain_transaction(tx.pbdata)
def get_slaves(alice_ots_index, txn_nonce): # [master_address: bytes, slave_seeds: list, slave_txn: json] slave_xmss = get_slave_xmss() alice_xmss = get_alice_xmss() alice_xmss.set_ots_index(alice_ots_index) slave_txn = SlaveTransaction.create([slave_xmss.pk], [1], 0, alice_xmss.pk) slave_txn._data.nonce = txn_nonce slave_txn.sign(alice_xmss) slave_data = json.loads( json.dumps([ bin2hstr(alice_xmss.address), [slave_xmss.extended_seed], slave_txn.to_json() ])) slave_data[0] = bytes(hstr2bin(slave_data[0])) return slave_data
def relay_slave_txn_by_slave(self, slave_pks: list, access_types: list, fee: int, master_qaddress): self.authenticate() index, group_index, slave_index, slave_xmss = self.get_slave_xmss( master_qaddress) if slave_index == -1: raise Exception("No Slave Found") tx = SlaveTransaction.create( slave_pks=slave_pks, access_types=access_types, fee=fee, xmss_pk=slave_xmss.pk, master_addr=self.qaddress_to_address(master_qaddress)) self.sign_and_push_transaction(tx, slave_xmss, index, group_index, slave_index) return self.to_plain_transaction(tx.pbdata)
def create_block(self, prev_hash, mining_address=None): if not mining_address: mining_address = self.alice_xmss.address transactions = [] block_prev = self.xrdnode.get_block_from_hash(prev_hash) block_idx = block_prev.block_number + 1 if block_idx == 1: slave_tx = SlaveTransaction.create(slave_pks=[self.bob_xmss.pk], access_types=[0], fee=0, xmss_pk=self.alice_xmss.pk) slave_tx.sign(self.alice_xmss) slave_tx._data.nonce = 1 transactions = [slave_tx] time_offset = 60 if block_idx % 2 == 0: time_offset += 2 self.time_mock.return_value = self.time_mock.return_value + time_offset self.ntp_mock.return_value = self.ntp_mock.return_value + time_offset block_new = Block.create(dev_config=config.dev, block_number=block_idx, prev_headerhash=block_prev.headerhash, prev_timestamp=block_prev.timestamp, transactions=transactions, miner_address=mining_address, seed_height=0, seed_hash=None) dev_config = self.xrdnode._chain_manager.get_config_by_block_number( block_new.block_number) while not self.xrdnode._chain_manager.validate_mining_nonce( blockheader=block_new.blockheader, dev_config=dev_config): block_new.set_nonces(config.dev, block_new.mining_nonce + 1, 0) return block_new
def test_validate_extended(self, m_validate_slave, m_logger): """ SlaveTransaction._validate_extended checks for: 1. valid master/slave 2. negative fee, 3. addr_from has enough funds for the fee 4. addr_from ots_key reuse """ alice_address_state = OptimizedAddressState.get_default(self.alice.address) alice_address_state.pbdata.balance = 100 addresses_state = { alice_address_state.address: alice_address_state } tx = SlaveTransaction.create(**self.params) tx.sign(self.alice) state_container = StateContainer(addresses_state=addresses_state, tokens=Indexer(b'token', None), slaves=Indexer(b'slave', None), lattice_pk=Indexer(b'lattice_pk', None), multi_sig_spend_txs=dict(), votes_stats=dict(), block_number=1, total_coin_supply=100, current_dev_config=config.dev, write_access=True, my_db=self.state._db, batch=None) result = tx._validate_extended(state_container) self.assertTrue(result) # Invalid master XMSS/slave XMSS relationship m_validate_slave.return_value = False result = tx.validate_all(state_container) self.assertFalse(result) m_validate_slave.return_value = True # fee = -1 with patch('xrd.core.txs.SlaveTransaction.SlaveTransaction.fee', new_callable=PropertyMock) as m_fee: m_fee.return_value = -1 result = tx._validate_custom() self.assertFalse(result) # balance = 0, cannot pay the Transaction fee alice_address_state.pbdata.balance = 0 result = tx._validate_extended(state_container) self.assertFalse(result) alice_address_state.pbdata.balance = 100 addresses_state = { self.alice.address: alice_address_state } # addr_from_pk has used this OTS key before state_container.paginated_bitfield.set_ots_key(addresses_state, alice_address_state.address, tx.ots_key) result = tx.validate_all(state_container) self.assertFalse(result) bob = get_bob_xmss() # Too many slave_pks with patch('xrd.core.config', autospec=True) as m_config: m_config.dev = config.dev.create(config.dev.prev_state_key, config.dev.current_state_key, b'', 10, True, True) m_config.dev.pbdata.transaction.multi_output_limit = 2 state_container.current_dev_config = m_config.dev params = self.params.copy() params["slave_pks"] = [self.alice.pk, bob.pk, self.slave.pk] params["access_types"] = [0, 0, 0] tx = SlaveTransaction.create(**params) self.assertFalse(tx._validate_extended(state_container))
def test_create_validate(self, m_logger): """Default self.params should result in a valid SlaveTransaction""" tx = SlaveTransaction.create(**self.params) tx.sign(self.alice) result = tx.validate_or_raise() self.assertTrue(result)