def commit_batch(self, three_pc_batch, prev_handler_result=None): committed_txns = prev_handler_result token_state_root, token_txn_root, _ = self.token_tracker.commit_batch() committed_seq_nos_with_fees = [ get_seq_no(t) for t in committed_txns if self._fees_tracker.has_deducted_fees(get_type(t), get_seq_no(t)) ] if len(committed_seq_nos_with_fees) > 0: # This is a fake txn only for commit to token ledger token_fake_three_pc_batch = ThreePcBatch( ledger_id=TOKEN_LEDGER_ID, inst_id=three_pc_batch.inst_id, view_no=three_pc_batch.view_no, pp_seq_no=three_pc_batch.pp_seq_no, pp_time=three_pc_batch.pp_time, state_root=token_state_root, txn_root=txn_root_serializer.serialize(token_txn_root), primaries=three_pc_batch.primaries, valid_digests=[ i for i in range(len(committed_seq_nos_with_fees)) ]) committed_token_txns = super()._commit(self.token_ledger, self.token_state, token_fake_three_pc_batch) TokenStaticHelper.commit_to_utxo_cache(self.utxo_cache, token_state_root) i = 0 # We are adding fees txn to the reply, so that client could get information about token transition for txn in committed_txns: if get_seq_no(txn) in committed_seq_nos_with_fees: txn[FEES] = committed_token_txns[i] i += 1 self._fees_tracker.fees_in_current_batch = 0 return committed_txns
def test_get_more_then_thousand_utxos(helpers, addresses, nodeSetWithIntegratedTokenPlugin): """ test if we send more have more than a 1000 UTXO's we still receive a response. """ _, address_2 = addresses states = [ n.db_manager.get_state(TOKEN_LEDGER_ID) for n in nodeSetWithIntegratedTokenPlugin ] utxos = [] for i in range(UTXO_LIMIT + 200): amount = randint(1, 5) key = TokenStaticHelper.create_state_key( libsovtoken_address_to_address(address_2), i + 5) utxos.append((key, amount)) for state in states: state.set(key, str(amount).encode()) request = helpers.request.get_utxo(address_2) responses = helpers.sdk.send_and_check_request_objects([request]) for response in responses: result = response[1]['result'] assert len(result[OUTPUTS]) == UTXO_LIMIT for output in result[OUTPUTS]: assert (TokenStaticHelper.create_state_key(output[ADDRESS], output[SEQNO]), output[AMOUNT]) in utxos assert result.get(NEXT_SEQNO, None)
def _validate_fees_can_pay(self, request, inputs, outputs, required_fees): """ Calculate and verify that inputs and outputs for fees can both be paid and change is properly specified This function ASSUMES that validation of the fees for the request has already been done. :param request: :param required_fees: :return: """ try: sum_inputs = self.utxo_cache.sum_inputs(inputs, is_committed=False) except UTXOError as ex: raise InvalidFundsError(request.identifier, request.reqId, "{}".format(ex)) except Exception as ex: error = 'Exception {} while processing inputs/outputs'.format(ex) raise UnauthorizedClientRequest(request.identifier, request.reqId, error) else: change_amount = sum([a[AMOUNT] for a in outputs]) expected_amount = change_amount + required_fees TokenStaticHelper.validate_given_inputs_outputs( sum_inputs, change_amount, expected_amount, request, 'fees: {}'.format(required_fees))
def update_state(self, txn, prev_result, request, is_committed=False): try: payload = get_payload_data(txn) seq_no = get_seq_no(txn) for output in payload[OUTPUTS]: TokenStaticHelper.add_new_output( self.state, self.database_manager.get_store(UTXO_CACHE_LABEL), Output(output["address"], seq_no, output["amount"]), is_committed=is_committed) except UTXOError as ex: error = 'Exception {} while updating state'.format(ex) raise OperationError(error)
def update_token_state(self, txn, request, is_committed=False): for utxo in txn[TXN_PAYLOAD][TXN_PAYLOAD_DATA][INPUTS]: TokenStaticHelper.spend_input(state=self.token_state, utxo_cache=self.utxo_cache, address=utxo[ADDRESS], seq_no=utxo[SEQNO], is_committed=is_committed) seq_no = get_seq_no(txn) for output in txn[TXN_PAYLOAD][TXN_PAYLOAD_DATA][OUTPUTS]: TokenStaticHelper.add_new_output(state=self.token_state, utxo_cache=self.utxo_cache, output=Output( output[ADDRESS], seq_no, output[AMOUNT]), is_committed=is_committed)
def _do_validate_inputs_ouputs(self, request): try: sum_in = TokenStaticHelper.sum_inputs(self.utxo_cache, request, is_committed=False) sum_out = TokenStaticHelper.sum_outputs(request) except Exception as ex: if isinstance(ex, InvalidClientMessageException): raise ex error = 'Exception {} while processing inputs/outputs'.format(ex) raise InvalidClientMessageException( request.identifier, getattr(request, 'reqId', None), error) else: return TokenStaticHelper.validate_given_inputs_outputs( sum_in, sum_out, sum_out, request)
def insert_over_thousand_utxos(db_manager, payment_address): token_state = db_manager.get_state(TOKEN_LEDGER_ID) for i in range(1200): token_state.set( TokenStaticHelper.create_state_key( libsovtoken_address_to_address(payment_address), i), str(i).encode())
def insert_utxos_after_gap(db_manager, payment_address): token_state = db_manager.get_state(TOKEN_LEDGER_ID) for i in range(1300, 2400): token_state.set( TokenStaticHelper.create_state_key( libsovtoken_address_to_address(payment_address), i), str(i).encode()) return 1300
def get_result(self, request: Request): address = request.operation[ADDRESS] from_seqno = request.operation.get(FROM_SEQNO) encoded_root_hash = state_roots_serializer.serialize( bytes(self.state.committedHeadHash)) proof, rv = self.state.generate_state_proof_for_keys_with_prefix( address, serialize=True, get_value=True) multi_sig = self.database_manager.bls_store.get(encoded_root_hash) if multi_sig: encoded_proof = proof_nodes_serializer.serialize(proof) proof = { MULTI_SIGNATURE: multi_sig.as_dict(), ROOT_HASH: encoded_root_hash, PROOF_NODES: encoded_proof } else: proof = {} # The outputs need to be returned in sorted order since each node's reply should be same. # Since no of outputs can be large, a concious choice to not use `operator.attrgetter` on an # already constructed list was made outputs = SortedItems() for k, v in rv.items(): addr, seq_no = TokenStaticHelper.parse_state_key(k.decode()) amount = rlp_decode(v)[0] if not amount: continue outputs.add(Output(addr, int(seq_no), int(amount))) utxos = outputs.sorted_list next_seqno = None if from_seqno: idx = next((idx for utxo, idx in zip(utxos, range(len(utxos))) if utxo.seqNo >= from_seqno), None) if idx: utxos = utxos[idx:] else: utxos = [] if len(utxos) > UTXO_LIMIT: next_seqno = utxos[UTXO_LIMIT].seqNo utxos = utxos[:UTXO_LIMIT] result = { f.IDENTIFIER.nm: request.identifier, f.REQ_ID.nm: request.reqId, OUTPUTS: utxos } result.update(request.operation) if next_seqno: result[NEXT_SEQNO] = next_seqno if proof: res_sub = deepcopy(result) res_sub[STATE_PROOF] = proof if len(json.dumps(res_sub)) <= self._msg_limit: result = res_sub return result
def update_state(self, txn, prev_result, request, is_committed=False): try: payload = get_payload_data(txn) for inp in payload[INPUTS]: TokenStaticHelper.spend_input(self.state, self.utxo_cache, inp["address"], inp["seqNo"], is_committed=is_committed) for output in payload[OUTPUTS]: seq_no = get_seq_no(txn) TokenStaticHelper.add_new_output(self.state, self.utxo_cache, Output( output["address"], seq_no, output["amount"]), is_committed=is_committed) except UTXOError as ex: error = 'Exception {} while updating state'.format(ex) raise OperationError(error)
def test_get_more_then_thousand_utxos_with_from( helpers, addresses, nodeSetWithIntegratedTokenPlugin): """ test if we send more have more than a thousand of UTXO's we will still receive a response. """ address_1, address_2 = addresses states = [ n.db_manager.get_state(TOKEN_LEDGER_ID) for n in nodeSetWithIntegratedTokenPlugin ] utxos = [] for i in range(UTXO_LIMIT + 200): amount = randint(1, 5) seq_no = i + 5 key = TokenStaticHelper.create_state_key( libsovtoken_address_to_address(address_2), seq_no) utxos.append((key, amount, seq_no)) for state in states: state.set(key, str(amount).encode()) # NB: this transaction is needed just to update bls_store with new root hash total = 1000 outputs = [{"address": address_1, "amount": total}] mint_result = helpers.general.do_mint(outputs) shift = 50 request = helpers.request.get_utxo(address_2, utxos[shift][2]) responses = helpers.sdk.send_and_check_request_objects([request]) utxos = utxos[shift:shift + UTXO_LIMIT] for response in responses: result = response[1]['result'] assert result[STATE_PROOF] assert len(result[OUTPUTS]) == UTXO_LIMIT for output in result[OUTPUTS]: assert (TokenStaticHelper.create_state_key(output[ADDRESS], output[SEQNO]), output[AMOUNT], output[SEQNO]) in utxos assert result.get(NEXT_SEQNO)
def make_tokens(helpers, nodeSetWithIntegratedTokenPlugin, addresses): address, address_2 = addresses states = [n.db_manager.get_state(TOKEN_LEDGER_ID) for n in nodeSetWithIntegratedTokenPlugin] utxos = [] for i in range(UTXO_LIMIT+200): amount = randint(1, 5) seq_no = i+5 key = TokenStaticHelper.create_state_key(libsovtoken_address_to_address(address), seq_no) utxos.append((key, amount, seq_no)) for state in states: state.set(key, str(amount).encode()) total = 1000 outputs = [{"address": address_2, "amount": total}] helpers.general.do_mint(outputs)
def test_xfer_public_txn_inputs_not_greater(): with pytest.raises(ExtraFundsError): TokenStaticHelper.validate_given_inputs_outputs(2, 1, 1, None) with pytest.raises(InvalidClientMessageException): TokenStaticHelper.validate_given_inputs_outputs(1, 2, 2, None) with pytest.raises(InvalidClientMessageException): TokenStaticHelper.validate_given_inputs_outputs( 100000000000000000000000, 100000000000000000000001, 100000000000000000000001, None)
def test_state_after_xfer(helpers, initial_mint, addresses, nodeSetWithIntegratedTokenPlugin): mint_seq_no = get_seq_no(initial_mint) [address1, address2, *_] = addresses inputs = helpers.general.get_utxo_addresses([address1]) inputs = [utxo for utxos in inputs for utxo in utxos] outputs = [{"address": address2, "amount": 100}] helpers.general.do_transfer(inputs, outputs) key = TokenStaticHelper.create_state_key( libsovtoken_address_to_address(address1), mint_seq_no) for n in nodeSetWithIntegratedTokenPlugin: res = n.db_manager.get_state(TOKEN_LEDGER_ID).get(key) assert not res
def test_state_proof(public_minting, looper, # noqa sdk_pool_handle, sdk_wallet_client, seller_token_wallet, seller_address, user1_token_wallet, user1_address): res = send_get_utxo(looper, seller_address, sdk_wallet_client, sdk_pool_handle) update_token_wallet_with_result(seller_token_wallet, res) # Do 20 XFER txns for _ in range(20): utxos = [_ for lst in seller_token_wallet.get_all_wallet_utxos().values() for _ in lst] seq_no, amount = utxos[0] inputs = [[seller_token_wallet, seller_address, seq_no]] outputs = [{"address": user1_address, "amount": 1}, {"address": seller_address, "amount": amount-1}] res = send_xfer(looper, inputs, outputs, sdk_pool_handle) update_token_wallet_with_result(seller_token_wallet, res) res = send_get_utxo(looper, seller_address, sdk_wallet_client, sdk_pool_handle) update_token_wallet_with_result(seller_token_wallet, res) res = send_get_utxo(looper, user1_address, sdk_wallet_client, sdk_pool_handle) update_token_wallet_with_result(user1_token_wallet, res) res = send_get_utxo(looper, user1_address, sdk_wallet_client, sdk_pool_handle) # Check presence of state proof assert res[STATE_PROOF] encoded = {} outputs = res[OUTPUTS] for out in outputs: state_key = TokenStaticHelper.create_state_key(out["address"], out["seqNo"]) encoded[state_key] = rlp_encode([str(out["amount"])]) proof_nodes = decode_proof(res[STATE_PROOF][PROOF_NODES]) client_trie = Trie(PersistentDB(KeyValueStorageInMemory())) assert client_trie.verify_spv_proof_multi( state_roots_serializer.deserialize(res[STATE_PROOF][ROOT_HASH]), encoded, proof_nodes)
def test_token_req_handler_update_state_XFER_PUBLIC_success( helpers, addresses, xfer_handler_a): [address1, address2] = addresses seq_no = 1 inputs = [{"source": utxo_from_addr_and_seq_no(address1, seq_no)}] outputs = [{"address": address2, "amount": 40}] request = helpers.request.transfer(inputs, outputs) txn = reqToTxn(request) append_txn_metadata(txn, seq_no=seq_no) xfer_handler_a.dynamic_validation(request) xfer_handler_a.update_state(txn, None, request) state_key = TokenStaticHelper.create_state_key( libsovtoken_address_to_address(address1), seq_no) utxo_cache = xfer_handler_a.database_manager.get_store(UTXO_CACHE_LABEL) key = utxo_cache._create_key( Output(libsovtoken_address_to_address(address1), seq_no, 60)) assert utxo_cache._store._has_key(key) try: xfer_handler_a.state.get(state_key, False) except Exception: pytest.fail("This state key isn't in the state")
def test_token_req_handler_commit_batch_different_state_root(xfer_handler_a): utxo_cache = xfer_handler_a.database_manager.get_store(UTXO_CACHE_LABEL) with pytest.raises(TokenValueError): TokenStaticHelper.commit_to_utxo_cache(utxo_cache, 1)
def add_utxo(payment_address, get_utxo_handler): get_utxo_handler.state.set( TokenStaticHelper.create_state_key( libsovtoken_address_to_address(payment_address), 1), "3".encode())
def commit_batch(self, three_pc_batch, prev_handler_result=None): TokenStaticHelper.commit_to_utxo_cache(self.utxo_cache, three_pc_batch.state_root)
def create_state_key(address: str, seq_no: int) -> bytes: return TokenStaticHelper.create_state_key(address=address, seq_no=seq_no)
def test_xfer_public_txn_equal(): TokenStaticHelper.validate_given_inputs_outputs(1, 1, 1, None) TokenStaticHelper.validate_given_inputs_outputs(10, 10, 10, None) TokenStaticHelper.validate_given_inputs_outputs(100, 100, 100, None) TokenStaticHelper.validate_given_inputs_outputs(100000000000000, 100000000000000, 100000000000000, None) TokenStaticHelper.validate_given_inputs_outputs(9223372036854775807, 9223372036854775807, 9223372036854775807, None) TokenStaticHelper.validate_given_inputs_outputs(9223372036854775807000, 9223372036854775807000, 9223372036854775807000, None)