def _revert_state_changes_for_PK(self, addresses_state, chain_manager): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.remove(self.txhash) addresses_state[addr_from_pk].decrease_nonce() addresses_state[addr_from_pk].unset_ots_key(self.ots_key, chain_manager)
def validate_slave(self, addr_from_state: AddressState, addr_from_pk_state: AddressState): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) # Validate Slave for CoinBase txn is no more required if isinstance(self, CoinBase): master_address = self.addr_to allowed_access_types = [0, 1] else: master_address = self.addr_from allowed_access_types = [0] if self.master_addr == addr_from_pk: logger.warning('Matching master_addr field and address from PK') return False if addr_from_pk != master_address: if str(self.PK) not in addr_from_state.slave_pks_access_type: logger.warning("Public key and address don't match") return False access_type = addr_from_pk_state.slave_pks_access_type[str( self.PK)] if access_type not in allowed_access_types: logger.warning('Access Type %s', access_type) logger.warning( 'Slave Address doesnt have sufficient permission') return False return True
def apply_on_state(self, addresses_state): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) owner_processed = False addr_from_processed = False addr_from_pk_processed = False for initial_balance in self.initial_balances: if initial_balance.address == self.owner: owner_processed = True if initial_balance.address == self.addr_from: addr_from_processed = True if initial_balance.address == addr_from_pk: addr_from_pk_processed = True if initial_balance.address in addresses_state: addresses_state[initial_balance.address].tokens[ bin2hstr(self.txhash).encode()] += initial_balance.amount addresses_state[initial_balance.address].transaction_hashes.append(self.txhash) if self.owner in addresses_state and not owner_processed: addresses_state[self.owner].transaction_hashes.append(self.txhash) if self.addr_from in addresses_state: addresses_state[self.addr_from].balance -= self.fee if not addr_from_processed: addresses_state[self.addr_from].transaction_hashes.append(self.txhash) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: if not addr_from_pk_processed: addresses_state[addr_from_pk].transaction_hashes.append(self.txhash) addresses_state[addr_from_pk].increase_nonce() addresses_state[addr_from_pk].set_ots_key(self.ots_key)
def unapply_on_state(self, addresses_state, state): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) owner_processed = False addr_from_processed = False addr_from_pk_processed = False for initial_balance in self.initial_balances: if initial_balance.address == self.owner: owner_processed = True if initial_balance.address == self.addr_from: addr_from_processed = True if initial_balance.address == addr_from_pk: addr_from_pk_processed = True if initial_balance.address in addresses_state: token_tx_hash = bin2hstr(self.txhash) addresses_state[initial_balance.address].tokens[ token_tx_hash] -= initial_balance.amount if addresses_state[initial_balance.address].tokens[token_tx_hash] == 0: del addresses_state[initial_balance.address].tokens[token_tx_hash] addresses_state[initial_balance.address].transaction_hashes.remove(self.txhash) if self.owner in addresses_state and not owner_processed: addresses_state[self.owner].transaction_hashes.remove(self.txhash) if self.addr_from in addresses_state: addresses_state[self.addr_from].balance += self.fee if not addr_from_processed: addresses_state[self.addr_from].transaction_hashes.remove(self.txhash) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: if not addr_from_pk_processed: addresses_state[addr_from_pk].transaction_hashes.remove(self.txhash) addresses_state[addr_from_pk].decrease_nonce() addresses_state[addr_from_pk].unset_ots_key(self.ots_key, state)
def validate_all(self, state_container: StateContainer, check_nonce=True) -> bool: if self.pbdata.WhichOneof('transactionType') == 'coinbase': if not self._validate_extended(state_container): return False return True if not self.validate(True): # It also calls _validate_custom return False if not self.validate_slave(state_container): return False if not self._validate_extended(state_container): return False addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) addr_from_pk_state = state_container.addresses_state[addr_from_pk] expected_nonce = addr_from_pk_state.nonce + 1 if check_nonce and self.nonce != expected_nonce: logger.warning('nonce incorrect, invalid tx') logger.warning('subtype: %s', self.type) logger.warning('%s actual: %s expected: %s', OptimizedAddressState.bin_to_qaddress(addr_from_pk), self.nonce, expected_nonce) return False if state_container.paginated_bitfield.load_bitfield_and_ots_key_reuse(addr_from_pk_state.address, self.ots_key): logger.warning('pubkey reuse detected: invalid tx %s', bin2hstr(self.txhash)) logger.warning('subtype: %s', self.type) return False return True
def address_is_valid(address: bytes) -> bool: # Warning: Never pass this validation True for Coinbase Address if not QRLHelper.addressIsValid(address): return False if address[0:1] == b'\x11': return False return True
def revert_state_changes(self, addresses_state, chain_manager): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) owner_processed = False addr_from_processed = False addr_from_pk_processed = False for initial_balance in self.initial_balances: if initial_balance.address == self.owner: owner_processed = True if initial_balance.address == self.addr_from: addr_from_processed = True if initial_balance.address == addr_from_pk: addr_from_pk_processed = True if initial_balance.address in addresses_state: addresses_state[initial_balance.address].update_token_balance(self.txhash, initial_balance.amount * -1) addresses_state[initial_balance.address].transaction_hashes.remove(self.txhash) if self.owner in addresses_state and not owner_processed: addresses_state[self.owner].transaction_hashes.remove(self.txhash) if self.addr_from in addresses_state: addresses_state[self.addr_from].balance += self.fee if not addr_from_processed and self.addr_from != self.owner: addresses_state[self.addr_from].transaction_hashes.remove(self.txhash) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk and addr_from_pk != self.owner: if not addr_from_pk_processed: addresses_state[addr_from_pk].transaction_hashes.remove(self.txhash) addresses_state[addr_from_pk].decrease_nonce() addresses_state[addr_from_pk].unset_ots_key(self.ots_key, chain_manager)
def validate_slave(self, addr_from_state: AddressState, addr_from_pk_state: AddressState): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) master_address = self._get_master_address() allowed_access_types = self._get_allowed_access_types() if self.master_addr == addr_from_pk: logger.warning('Matching master_addr field and address from PK') return False if addr_from_pk != master_address: if str(self.PK) not in addr_from_state.slave_pks_access_type: logger.warning("Public key and address don't match") return False access_type = addr_from_pk_state.slave_pks_access_type[str( self.PK)] if access_type not in allowed_access_types: logger.warning('Access Type %s', access_type) logger.warning( 'Slave Address doesnt have sufficient permission') return False return True
def apply_on_state(self, addresses_state): if self.addr_from in addresses_state: addresses_state[self.addr_from].tokens[bin2hstr( self.token_txhash).encode()] -= self.total_amount if addresses_state[self.addr_from].tokens[bin2hstr( self.token_txhash).encode()] == 0: del addresses_state[self.addr_from].tokens[bin2hstr( self.token_txhash).encode()] addresses_state[self.addr_from].balance -= self.fee addresses_state[self.addr_from].transaction_hashes.append( self.txhash) for index in range(0, len(self.addrs_to)): addr_to = self.addrs_to[index] amount = self.amounts[index] if addr_to in addresses_state: if self.addr_from != addr_to: addresses_state[addr_to].transaction_hashes.append( self.txhash) addresses_state[addr_to].tokens[bin2hstr( self.token_txhash).encode()] += amount addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.append( self.txhash) addresses_state[addr_from_pk].increase_nonce() addresses_state[addr_from_pk].set_ots_key(self.ots_key)
def revert(self, state_container, multi_sig_spend, addresses_state: dict, paginated_tx_hash: PaginatedData, current_block_number: int, threshold: int) -> bool: if not self.executed: return True if self.total_weight < threshold: return False if current_block_number > self.expiry_block_number: return False addresses_state[self.multi_sig_address].update_balance( state_container, multi_sig_spend.total_amount) addr_from_pk = bytes(QRLHelper.getAddress(multi_sig_spend.PK)) for index in range(0, len(multi_sig_spend.addrs_to)): addr_to = multi_sig_spend.addrs_to[index] address_state = addresses_state[addr_to] if addr_to not in (multi_sig_spend.addr_from, addr_from_pk): paginated_tx_hash.remove(address_state, multi_sig_spend.txhash) address_state.update_balance(state_container, multi_sig_spend.amounts[index], subtract=True) self._data.executed = False return True
def _apply_state_changes_for_PK(self, addresses_state: dict): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.append(self.txhash) addresses_state[addr_from_pk].increase_nonce() addresses_state[addr_from_pk].set_ots_key(self.ots_key)
def calc_addr_ots_hash(tx): addr = tx.master_addr if not addr: addr = bytes(QRLHelper.getAddress(tx.PK)) addr_ots_hash = sha2_256( addr + tx.ots_key.to_bytes(8, byteorder='big', signed=False)) return addr_ots_hash
def apply(self, state: State, state_container: StateContainer) -> bool: addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) owner_processed = False addr_from_processed = False addr_from_pk_processed = False for initial_balance in self.initial_balances: if initial_balance.address == self.owner: owner_processed = True if initial_balance.address == self.addr_from: addr_from_processed = True if initial_balance.address == addr_from_pk: addr_from_pk_processed = True # If a QRL address has been mentioned multiple times in initial balance # then check if that address has already been initialized with some token # balance, if found, then add the new balance the already initialized balance if (initial_balance.address, self.txhash) in state_container.tokens.data: state_container.tokens.data[( initial_balance.address, self.txhash)].balance += initial_balance.amount else: state_container.tokens.data[( initial_balance.address, self.txhash)] = TokenBalance( balance=initial_balance.amount, decimals=self.decimals, tx_hash=self.txhash, delete=False) address_state = state_container.addresses_state[ initial_balance.address] state_container.paginated_tx_hash.insert(address_state, self.txhash) state_container.paginated_tokens_hash.insert( address_state, self.txhash) if not owner_processed: address_state = state_container.addresses_state[self.owner] state_container.paginated_tx_hash.insert(address_state, self.txhash) address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee, subtract=True) if not addr_from_processed and self.addr_from != self.owner: state_container.paginated_tx_hash.insert(address_state, self.txhash) address_state = state_container.addresses_state[addr_from_pk] if self.addr_from != addr_from_pk and addr_from_pk != self.owner: if not addr_from_pk_processed: state_container.paginated_tx_hash.insert( address_state, self.txhash) address_state.increase_nonce() state_container.paginated_bitfield.set_ots_key( state_container.addresses_state, addr_from_pk, self.ots_key) return True
def _revert_state_changes_for_PK(self, state_container: StateContainer) -> bool: addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) address_state = state_container.addresses_state[addr_from_pk] if self.addr_from != addr_from_pk: state_container.paginated_tx_hash.remove(address_state, self.txhash) address_state.decrease_nonce() state_container.paginated_bitfield.unset_ots_key(state_container.addresses_state, addr_from_pk, self.ots_key) return True
def unapply_on_state(self, addresses_state, state): if self.addr_from in addresses_state: addresses_state[self.addr_from].balance += self.fee addresses_state[self.addr_from].transaction_hashes.remove(self.txhash) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.remove(self.txhash) addresses_state[addr_from_pk].decrease_nonce() addresses_state[addr_from_pk].unset_ots_key(self.ots_key, state)
def apply_on_state(self, addresses_state): if self.addr_from in addresses_state: addresses_state[self.addr_from].balance -= self.fee addresses_state[self.addr_from].transaction_hashes.append(self.txhash) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.append(self.txhash) addresses_state[addr_from_pk].increase_nonce() addresses_state[addr_from_pk].set_ots_key(self.ots_key)
def unapply_on_state(self, addresses_state, state): if self.addr_from in addresses_state: addresses_state[self.addr_from].balance += self.fee for index in range(0, len(self.slave_pks)): addresses_state[self.addr_from].remove_slave_pks_access_type(self.slave_pks[index]) addresses_state[self.addr_from].transaction_hashes.remove(self.txhash) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.remove(self.txhash) addresses_state[addr_from_pk].decrease_nonce() addresses_state[addr_from_pk].unset_ots_key(self.ots_key, state)
def ParseAddress(self, request: qrl_pb2.ParseAddressReq, context) -> qrl_pb2.ParseAddressResp: response = qrl_pb2.ParseAddressResp() response.is_valid = QRLHelper.addressIsValid(request.address) descriptor = QRLDescriptor.fromBytes(request.address[:3]) hf_dict = {0: 'SHA2-256', 1: 'SHAKE-128', 2: 'SHAKE-256', 3: 'RESERVED'} ss_dict = {0: 'XMSS', 1: 'XMSS-MT'} af_dict = {0: 'SHA2-256', 1: 'RESERVED', 3: 'RESERVED'} response.desc.hash_function = hf_dict[descriptor.getHashFunction()] response.desc.tree_height = descriptor.getHeight() response.desc.signatures = 2**response.desc.tree_height response.desc.signature_scheme = ss_dict[descriptor.getSignatureType()] response.desc.address_format = af_dict[descriptor.getAddrFormatType()] return response
def get_slaves_by_address(self, address: bytes, item_per_page: int, page_number: int): if item_per_page > config.dev.data_per_page or item_per_page == 0: return None slave_hashes = self._load_slave_transaction_hashes(address, item_per_page, page_number) response = qrl_pb2.GetSlavesByAddressResp() for tx_hash in slave_hashes: tx, _ = self._chain_manager.get_tx_metadata(tx_hash) for index in range(0, len(tx.slave_pks)): transaction_detail = qrl_pb2.SlaveDetail(slave_address=bytes(QRLHelper.getAddress(tx.slave_pks[index])), access_type=tx.access_types[index]) response.slaves_detail.extend([transaction_detail]) return response
def apply_on_state(self, addresses_state): if self.addr_from in addresses_state: addresses_state[self.addr_from].balance -= self.fee for index in range(0, len(self.slave_pks)): addresses_state[self.addr_from].add_slave_pks_access_type(self.slave_pks[index], self.access_types[index]) addresses_state[self.addr_from].transaction_hashes.append(self.txhash) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.append(self.txhash) addresses_state[addr_from_pk].increase_nonce() addresses_state[addr_from_pk].set_ots_key(self.ots_key)
def apply(self, state: State, state_container: StateContainer) -> bool: address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee, subtract=True) state_container.paginated_tx_hash.insert(address_state, self.txhash) if self.addr_to: addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) address_state = state_container.addresses_state[self.addr_to] if self.addr_to not in (self.addr_from, addr_from_pk): state_container.paginated_tx_hash.insert(address_state, self.txhash) state_container.paginated_inbox_message.insert(address_state, self.txhash) return self._apply_state_changes_for_PK(state_container)
def revert(self, state: State, state_container: StateContainer) -> bool: addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) owner_processed = False addr_from_processed = False addr_from_pk_processed = False for initial_balance in self.initial_balances: if initial_balance.address == self.owner: owner_processed = True if initial_balance.address == self.addr_from: addr_from_processed = True if initial_balance.address == addr_from_pk: addr_from_pk_processed = True address_state = state_container.addresses_state[ initial_balance.address] state_container.tokens.data[(initial_balance.address, self.txhash)] = TokenBalance( balance=0, delete=True) state_container.paginated_tx_hash.remove(address_state, self.txhash) state_container.paginated_tokens_hash.remove( address_state, self.txhash) if not owner_processed: address_state = state_container.addresses_state[self.owner] state_container.paginated_tx_hash.remove(address_state, self.txhash) address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee) if not addr_from_processed and self.addr_from != self.owner: state_container.paginated_tx_hash.remove(address_state, self.txhash) address_state = state_container.addresses_state[addr_from_pk] if self.addr_from != addr_from_pk and addr_from_pk != self.owner: if not addr_from_pk_processed: state_container.paginated_tx_hash.remove( address_state, self.txhash) address_state.decrease_nonce() state_container.paginated_bitfield.unset_ots_key( state_container.addresses_state, addr_from_pk, self.ots_key) return True
def apply(self, state: State, state_container: StateContainer) -> bool: address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.total_amount + self.fee, subtract=True) state_container.paginated_tx_hash.insert(address_state, self.txhash) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) for index in range(0, len(self.addrs_to)): addr_to = self.addrs_to[index] amount = self.amounts[index] address_state = state_container.addresses_state[addr_to] address_state.update_balance(state_container, amount) if addr_to in (self.addr_from, addr_from_pk): continue state_container.paginated_tx_hash.insert(address_state, self.txhash) return self._apply_state_changes_for_PK(state_container)
def apply_on_state(self, addresses_state): if self.addr_from in addresses_state: addresses_state[self.addr_from].balance -= (self.total_amount + self.fee) addresses_state[self.addr_from].transaction_hashes.append(self.txhash) for index in range(0, len(self.addrs_to)): addr_to = self.addrs_to[index] amount = self.amounts[index] if addr_to in addresses_state: addresses_state[addr_to].balance += amount if addr_to == self.addr_from: continue addresses_state[addr_to].transaction_hashes.append(self.txhash) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if addr_from_pk in addresses_state: if self.addr_from != addr_from_pk: addresses_state[addr_from_pk].transaction_hashes.append(self.txhash) addresses_state[addr_from_pk].increase_nonce() addresses_state[addr_from_pk].set_ots_key(self.ots_key)
def validate_slave(self, addr_from_state, addr_from_pk_state): addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) if isinstance(self, CoinBase): master_address = self.addr_to allowed_access_types = [0, 1] else: master_address = self.addr_from allowed_access_types = [0] if addr_from_pk != master_address: if str(self.PK) not in addr_from_state.slave_pks_access_type: logger.warning('Public key and address dont match') return False access_type = addr_from_pk_state.slave_pks_access_type[str(self.PK)] if access_type not in allowed_access_types: logger.warning('Access Type %s', access_type) logger.warning('Slave Address doesnt have sufficient permission') return False return True
def apply(self, state_container, multi_sig_spend, addresses_state: dict, paginated_tx_hash: PaginatedData, current_block_number: int, threshold: int) -> bool: # TODO: return False if executed if self.executed: return True if self.total_weight < threshold: return False if current_block_number > self.expiry_block_number: return False if multi_sig_spend.total_amount > addresses_state[ self.multi_sig_address].balance: logger.info( "[VoteStats] Insufficient funds to execute Multi Sig Spend") logger.info("Multi Sig Spend Amount: %s, Funds Available: %s", multi_sig_spend.total_amount, addresses_state[self.multi_sig_address].balance) logger.info("Multi Sig Spend txn hash: %s", bin2hstr(multi_sig_spend.txhash)) logger.info("Multi Sig Address: %s", bin2hstr(multi_sig_spend.multi_sig_address)) return False addresses_state[self.multi_sig_address].update_balance( state_container, multi_sig_spend.total_amount, subtract=True) addr_from_pk = bytes(QRLHelper.getAddress(multi_sig_spend.PK)) for index in range(0, len(multi_sig_spend.addrs_to)): addr_to = multi_sig_spend.addrs_to[index] address_state = addresses_state[addr_to] if addr_to not in (multi_sig_spend.addr_from, addr_from_pk): paginated_tx_hash.insert(address_state, multi_sig_spend.txhash) address_state.update_balance(state_container, multi_sig_spend.amounts[index]) self._data.executed = True return True
def validate_slave(self, state_container: StateContainer) -> bool: addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) master_address = self._get_master_address() allowed_access_types = self._get_allowed_access_types() if self.master_addr == addr_from_pk: logger.warning('Matching master_addr field and address from PK') return False if addr_from_pk != master_address: if (self.addr_from, self.PK) not in state_container.slaves.data: logger.warning("Public key and address doesn't match") return False slave_access_type = state_container.slaves.data[(self.addr_from, self.PK)].access_type if slave_access_type not in allowed_access_types: logger.warning('Access Type %s', slave_access_type) logger.warning('Slave Address doesnt have sufficient permission') return False return True
def apply(self, state: State, state_container: StateContainer) -> bool: address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee, subtract=True) state_container.paginated_tx_hash.insert(address_state, self.txhash) multi_sig_address_state = MultiSigAddressState.get_default( self.txhash, self.signatories, self.weights, self.threshold) state_container.addresses_state[ multi_sig_address_state.address] = multi_sig_address_state state_container.paginated_tx_hash.insert(multi_sig_address_state, self.txhash) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) for index in range(0, len(self.signatories)): signatory = self.signatories[index] address_state = state_container.addresses_state[signatory] if signatory not in (self.addr_from, addr_from_pk): state_container.paginated_tx_hash.insert( address_state, self.txhash) state_container.paginated_multisig_address.insert( address_state, multi_sig_address_state.address) return self._apply_state_changes_for_PK(state_container)
def revert(self, state: State, state_container: StateContainer) -> bool: address_state = state_container.addresses_state[self.addr_from] address_state.update_balance(state_container, self.fee) state_container.paginated_tx_hash.remove(address_state, self.txhash) multi_sig_address = MultiSigAddressState.generate_multi_sig_address( self.txhash) MultiSigAddressState.remove_multi_sig_address_state( state, multi_sig_address, state_container.batch) addr_from_pk = bytes(QRLHelper.getAddress(self.PK)) for index in range(0, len(self.signatories)): signatory = self.signatories[index] address_state = state_container.addresses_state[signatory] if signatory not in (self.addr_from, addr_from_pk): state_container.paginated_tx_hash.remove( address_state, self.txhash) state_container.paginated_multisig_address.remove( address_state, multi_sig_address) if multi_sig_address in state_container.addresses_state: del state_container.addresses_state[multi_sig_address] return self._revert_state_changes_for_PK(state_container)
def test_transferCoins_sign(self): with set_qrl_dir('wallet_ver1'): with State() as db_state: p2p_factory = Mock(spec=P2PFactory) p2p_factory.pow = Mock(spec=POW) chain_manager = ChainManager(db_state) qrlnode = QRLNode(db_state, mining_address=b'') qrlnode.set_chain_manager(chain_manager) qrlnode._p2pfactory = p2p_factory qrlnode._pow = p2p_factory.pow qrlnode._peer_addresses = ['127.0.0.1', '192.168.1.1'] service = PublicAPIService(qrlnode) context = Mock(spec=ServicerContext) alice = get_alice_xmss() bob = get_bob_xmss() request = qrl_pb2.TransferCoinsReq( addresses_to=[bob.address], amounts=[101], fee=12, xmss_pk=alice.pk ) response = service.TransferCoins(request=request, context=context) context.set_code.assert_not_called() context.set_details.assert_not_called() self.assertIsNotNone(response) self.assertIsNotNone(response.extended_transaction_unsigned.tx) self.assertEqual('transfer', response.extended_transaction_unsigned.tx.WhichOneof('transactionType')) self.assertEqual(12, response.extended_transaction_unsigned.tx.fee) self.assertEqual(alice.pk, response.extended_transaction_unsigned.tx.public_key) self.assertEqual(0, response.extended_transaction_unsigned.tx.nonce) self.assertEqual(b'', response.extended_transaction_unsigned.tx.signature) self.assertEqual(b'', response.extended_transaction_unsigned.tx.transaction_hash) self.assertEqual(bob.address, response.extended_transaction_unsigned.tx.transfer.addrs_to[0]) self.assertEqual(101, response.extended_transaction_unsigned.tx.transfer.amounts[0]) tmp_hash_pre = bytes(QRLHelper.getAddress(response.extended_transaction_unsigned.tx.public_key)) tmp_hash_pre += str(response.extended_transaction_unsigned.tx.fee).encode() tmp_hash_pre += response.extended_transaction_unsigned.tx.transfer.addrs_to[0] tmp_hash_pre += str(response.extended_transaction_unsigned.tx.transfer.amounts[0]).encode() self.assertEqual('010300a1da274e68c88b0ccf448e0b1916fa789b01eb2ed4e9ad565ce264c939078' '2a9c61ac02f31320103001d65d7e59aed5efbeae64246e0f3184d7c42411421eb38' '5ba30f2c1c005a85ebc4419cfd313031', bin2hstr(tmp_hash_pre)) tmp_hash = sha256(tmp_hash_pre) self.assertEqual('3645f2819aba65479f9a7fad3f5d7a41a9357410a595fa02fb947bfe3ed96e0f', bin2hstr(tmp_hash)) signed_transaction = response.extended_transaction_unsigned.tx signed_transaction.signature = alice.sign(tmp_hash) req_push = qrl_pb2.PushTransactionReq(transaction_signed=signed_transaction) resp_push = service.PushTransaction(req_push, context=context) context.set_code.assert_not_called() context.set_details.assert_not_called() self.assertIsNotNone(resp_push) self.assertEqual(qrl_pb2.PushTransactionResp.SUBMITTED, resp_push.error_code) self.assertEqual('30955fdc5e2d9dbe5fb9bf812f2e1b6c4b409a8a7c7a75f1c3e9ba1ffdd8e60e', bin2hstr(resp_push.tx_hash))