mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-12-03 10:05:49 +00:00
Migrate dev-blog to Redocly (#2346)
* Step 1 to migrate the blog - Add blog pages from the dev-blog repo * Add blog-specific sidebar (& update contents) * Add new dev reflections blog post from 01-23-2024 * Blog migration: fix markdoc errors and broken links * Remove community pages from the sidebar --------- Co-authored-by: mDuo13 <mduo13@gmail.com>
This commit is contained in:
committed by
mDuo13
parent
33cd1f54a5
commit
b4489f8649
@@ -0,0 +1,201 @@
|
||||
# Calculating Balance Changes for a Transaction
|
||||
|
||||
## Introduction
|
||||
|
||||
When interacting with Transactions on the Ripple Network you often care about the changes that have been made to a specific account. For example, if you make a payment, you want to know by how much the balance on your account has been decreased. Parsing out the exact balance changes from a transaction can often be complicated and we’ve spend a lot of time to provide a simple method for getting accurate information. In an effort to standardize the way we deal with balance changes and make sure we have one place that captures our efforts, we released a module, called [ripple-lib-transactionparser](https://www.npmjs.com/package/ripple-lib-transactionparser). We’ve made the module available on npm and you can find the source on our [github](https://github.com/ripple/ripple-lib-extensions/tree/master/transactionparser)
|
||||
|
||||
In this post we want to provide some background on why parsing balance changes is hard and give details on how a transaction’s meta should be interpreted.
|
||||
|
||||
### Table of Contents
|
||||
|
||||
- **[Installation Instructions](#installation-instructions)**
|
||||
- **[Why is parsing transactions hard?](#why-is-parsing-transactions-hard)**
|
||||
- **[The solution](#the-solution)**
|
||||
- **[Counterparty vs Issuer](#counterparty-vs-issuer)**
|
||||
- **[Additonal notes on meta](#additional-notes-on-meta)**
|
||||
|
||||
## Installation Instructions
|
||||
|
||||
To install this package in your Node project, run:
|
||||
|
||||
npm install ripple-lib-transactionparser
|
||||
|
||||
Given a `transactionResponse`, which is a return for a transaction submission or transaction lookup, you can use it as follows:
|
||||
|
||||
var parseBalanceChanges = require(‘ripple-lib-transactionparser’).parseBalanceChanges;
|
||||
…
|
||||
var balanceChanges = parseBalanceChanges(transactionResponse.meta);
|
||||
|
||||
## Why is parsing Transactions hard?
|
||||
|
||||
Each host on the Ripple Network runs a program called [rippled](https://github.com/ripple/rippled) that is responsible for maintaining the Ripple ledger and reaching consensus. The rippled application is written in C++ and it’s interface is geared more for machine consumption than human consumption, so it doesn’t directly tell you higher-level information like what balances were affected by a specific transaction. Instead, it provides some `meta` about how various nodes changed in the ledger, which can be used to compute balance changes for transactions that affect balances.
|
||||
|
||||
Extracting the balance changes from a transaction’s meta isn’t straightforward. Objects in the AffectedNodes array describe the changes to nodes in the ledger, but how a balance or a affected account has changed requires some parsing of the given meta.
|
||||
|
||||
### Transaction meta
|
||||
|
||||
Below is an example of what the relevant transaction meta that comes from rippled looks like
|
||||
|
||||
{
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "1.525330905250352"
|
||||
},
|
||||
"Flags": 1114112,
|
||||
"HighLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"value": "0"
|
||||
},
|
||||
"HighNode": "00000000000001E8",
|
||||
"LowLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc",
|
||||
"value": "1000000000"
|
||||
},
|
||||
"LowNode": "0000000000000000"
|
||||
},
|
||||
"LedgerEntryType": "RippleState",
|
||||
"LedgerIndex": "2F323020B4288ACD4066CC64C89DAD2E4D5DFC2D44571942A51C005BF79D6E25",
|
||||
"PreviousFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "1.535330905250352"
|
||||
}
|
||||
},
|
||||
"PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD",
|
||||
"PreviousTxnLgrSeq": 10459364
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "0.02"
|
||||
},
|
||||
"Flags": 1114112,
|
||||
"HighLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"value": "0"
|
||||
},
|
||||
"HighNode": "00000000000001E8",
|
||||
"LowLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K",
|
||||
"value": "1000000000"
|
||||
},
|
||||
"LowNode": "0000000000000000"
|
||||
},
|
||||
"LedgerEntryType": "RippleState",
|
||||
"LedgerIndex": "AAE13AF5192EFBFD49A8EEE5869595563FEB73228C0B38FED9CC3D20EE74F399",
|
||||
"PreviousFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "0.01"
|
||||
}
|
||||
},
|
||||
"PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD",
|
||||
"PreviousTxnLgrSeq": 10459364
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc",
|
||||
"Balance": "239555992",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 1,
|
||||
"Sequence": 38
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "E9A39B0BA8703D5FFD05D9EAD01EE6C0E7A15CF33C2C6B7269107BD2BD535818",
|
||||
"PreviousFields": {
|
||||
"Balance": "239567992",
|
||||
"Sequence": 37
|
||||
},
|
||||
"PreviousTxnID": "DC061E6F47B1B6E9A496A31B1AF87194B4CB24B2EBF8A59F35E31E12509238BD",
|
||||
"PreviousTxnLgrSeq": 10459364
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 2,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
|
||||
The meta above doesn’t make it obvious what the changes are to which accounts without first understanding what `HighNode`, `LowNode`, `...Limit` and the various other fields mean.
|
||||
|
||||
## The solution
|
||||
|
||||
We wrote the `ripple-lib-transactionparser` module to capture the logic needed to parse transaction meta like the above. We spent a lot of time discussing various edge cases and how to correctly parse the balance changes most intuitively. Using the `ripple-lib-transactionparser` the above meta will result in the following balance changes:
|
||||
|
||||
{
|
||||
rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc: [
|
||||
{
|
||||
value: '-0.01',
|
||||
currency: 'USD',
|
||||
counterparty: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q'
|
||||
},
|
||||
{
|
||||
value: '-0.012',
|
||||
currency: 'XRP',
|
||||
counterparty: ''
|
||||
}
|
||||
],
|
||||
rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q: [
|
||||
{
|
||||
counterparty: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc',
|
||||
currency: 'USD',
|
||||
value: '0.01'
|
||||
},
|
||||
{
|
||||
counterparty: 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K',
|
||||
currency: 'USD',
|
||||
value: '-0.01'
|
||||
}
|
||||
],
|
||||
rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K: [
|
||||
{
|
||||
counterparty: 'rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q',
|
||||
currency: 'USD',
|
||||
value: '0.01'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
The keys in the balance changes object (rLDY…) are the affected addresses and the values (in square brackets) are arrays of balance changes that happened on that address. Each balance change is specified with a counterparty, currency, and value, where `counterparty` refers to the address on the opposite end of the trustline. If you’re not a gateway, then the counterparty is typically the issuer of the currency (for a more detailed explanation see the section [Counterparty vs Issuer](#counterparty-vs-issuer) below).
|
||||
|
||||
Let’s look at how these balance changes are computed from the meta. The meta contains an array of AffectedNodes, where each AffectedNode object contains a CreatedNode, ModifiedNode, or DeletedNode, which each correspond to a change in the ledger. The JSON is structured this way, rather than just an array of nodes with the type as a property, so that the node objects can map directly to the corresponding C++ classes. Each node has a LedgerEntryType and there are two types that can indicate a balance change: AccountRoot - holds an account’s XRP balance, last transaction sequence number, and related information RippleState - tracks credit between two accounts and associated properties (corresponds to a trustline) So AccountRoot nodes appear when XRP balances change and RippleState nodes appear when trustline balances change.
|
||||
|
||||
Calculating XRP balance changes from AccountRoot nodes is straightforward; subtract the balance in the FinalFields from the balance in the PreviousFields and convert from drops to XRP (all XRP balances from rippled are specified in drops, where one XRP is equivalent to one million drops). If a new account is created, a CreatedNode will be found instead of a ModifiedNode, in which case PreviousFields won’t exist and FinalFields will be replaced with NewFields. DeletedNodes still have FinalFields and PreviousFields, just like ModifiedNodes.
|
||||
|
||||
Calculating the balance changes for a trustline is similar, but with an additional complication: the sign of the balances are oriented with respect to the `low node`, which is the node with the lower address of the two on the trustline (the address is treated as a numerical value, so for example `rKm…` is lower than `rMw…` since `K` comes before `M` in the [base58 dictionary](https://xrpl.org/base58-encodings.html)). This convention was established in rippled so that the hash of a trustline between A and B is the same as the hash of a trustline between B and A. Therefore, the balance is negated when generating the balance change for the high address. Two balance changes are created for each trustline balance change, one from the perspective of each address on the trustline, which are extracted from the HighLimit and LowLimit objects.
|
||||
|
||||
## Counterparty vs. Issuer
|
||||
|
||||
Through the Ripple products we sometimes use the concept of `issuer` instead of `counterparty`. Ripple-REST for example has been using the term `issuer` and it shows up throughout our documentation. The `issuer` is the party on a trustline that owes the other party, so the party on a trustline that has credited the party. The `counterparty` is the party on the other side of the trustline from the perspective of a given party, regardless of who owes who. For calculating balance changes, `counterparty` is more useful. If we were to use `issuer`, gateways would not be able to see balance changes that shift money between their trustlines because both changes would have the same issuer and would cancel out. A gateway may want to monitor such changes in order to mirror balances to an off-ledger accounting system.
|
||||
|
||||
It might seem like you would need the concept of `issuer` in order to keep track of who issued your USD because only the issuer will redeem it. But any USD that you have on a trustline with a gateway is automatically issued by that gateway. There’s no way to take USD from gateway A and move it to a trustline with gateway B because that would require increasing your balance with gateway B, which is something that only gateway B is allowed to do. If Alice sends SnapSwap-USD to Bob, it moves from Alice’s trustline with SnapSwap to Bob’s trustline with SnapSwap; SnapSwap-USDs never leave SnapSwap’s trustlines. Actually even calling them SnapSwap-USDs makes it sound like the currency itself is tied to SnapSwap, but the currency format doesn’t specify any address for the issuer, so SnapSwap-USDs are really just USDs on a SnapSwap trustline.
|
||||
|
||||
## Additional Notes on meta
|
||||
|
||||
### LowNode and HighNode
|
||||
|
||||
The LowNode and HighNode fields may be confusing. They are hints to make node deletion happen in constant time in rippled and should always be ignored outside of rippled. More precisely, LowNode is the integer number of the page in the low account's owner directory that contains the reference to the trust line.
|
||||
|
||||
### ACCOUNT\_ONE
|
||||
|
||||
The issuer listed in the balance fields is `rrrrrrrrrrrrrrrrrrrrBZbvji`, which is referred to as [ACCOUNT\_ONE](https://xrpl.org/accounts.html#special-addresses) and is the encoding that corresponds to the numerical value 1. This convention is used because the addresses on the trustline are already specified in the HighLimit and LowLimit objects, so specifying them here would be redundant.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Parsing balance changes from a transaction is hard, so we made a module that makes it easy. Find it on NPM: <https://www.npmjs.com/package/ripple-lib-transactionparser> And here's the github: <https://github.com/ripple/ripple-lib-extensions/tree/master/transactionparser> For any issues or questions, please use the github issues page.
|
||||
11
content/blog/2015/correction-to-ripple-white-paper.md
Normal file
11
content/blog/2015/correction-to-ripple-white-paper.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Correction to Ripple White Paper
|
||||
|
||||
We've recently been made aware of a [paper](http://www.ghassankarame.com/ripple.pdf) discussing the possibility of forks in the Ripple system. We agree with the authors' conclusion that a fork is not possible given that the UNL overlap is greater than 40%. Unfortunately, this is different than the 20% figure stated in our [2014 white paper](https://ripple.com/consensus-whitepaper/). We apologize for the oversight and are issuing a corrected version.
|
||||
|
||||
We'd like to point out that our validators are in fact configured with an overlap of 100%, which is also our current recommendation to partners. In other words, these new findings confirm our understanding that the live Ripple network has been and continues to be safe from forks.
|
||||
|
||||
In the paper the authors reference “several forks” in distributed ledger protocols, citing two sources. The first one is a blog post about [a fork in the Bitcoin network](https://bitcoinmagazine.com/3668/bitcoin-network-shaken-by-blockchain-fork/). The second is a blog post about [a fork in the Stellar network](https://www.stellar.org/blog/safety_liveness_and_fault_tolerance_consensus_choice/). We’d like to point out that neither of these systems use the Ripple consensus protocol in conjunction with our recommended configuration. There has been no such incident on the Ripple network after nearly three years of operation and more than 15 million ledger closes.
|
||||
|
||||
We're excited to see increased interest from the academic community in Ripple consensus. As we work to diversify validator sets, we are also increasing our own efforts to contribute to consensus research. Our 2014 white paper was the first formal description of Ripple-style consensus and there’s a lot more to explore. We're working to provide more detailed descriptions of Byzantine fault tolerance in Ripple and the safety of certain topologies of validators.
|
||||
|
||||
If you are currently in academia studying consensus or graph problems, please reach out to us at <research@ripple.com>.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Do You Have What It Takes to Be a Gateway?
|
||||
|
||||
We're proud to announce the first release of [our new Gateway Guide](https://developers.ripple.com/become-an-xrp-ledger-gateway.html), a comprehensive manual to operating a gateway in the Ripple network. Whether you're trying to understand [how a gateway makes revenue](https://developers.ripple.com/become-an-xrp-ledger-gateway.html#fees-and-revenue-sources), or how to use the [authorized accounts](https://developers.ripple.com/become-an-xrp-ledger-gateway.html#authorized-trust-lines) feature, or even just [what a warm wallet is](https://developers.ripple.com/issuing-and-operational-addresses.html), the gateway guide has you covered.
|
||||
|
||||
The guide comes with step-by-step, diagrammed explanations of typical gateway operations, a hefty [list of precautions](https://developers.ripple.com/become-an-xrp-ledger-gateway.html#precautions) to make your gateway safer, and concrete examples of all the API calls you need to perform in order to get your gateway accounts set up and secure.
|
||||
|
||||
We're proud of all the work we've done to make the business of running a gateway easier, but there's still more work to do. If you have any questions, comments, or ideas, please send feedback to <developers@ripple.com> - or post it on our forums. We'd love to hear from you!
|
||||
7
content/blog/2015/gatewayd-no-longer-available.md
Normal file
7
content/blog/2015/gatewayd-no-longer-available.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Gatewayd No Longer Available
|
||||
|
||||
In the interest of focusing our resources on other projects, starting today, Gatewayd will no longer be available on Github.
|
||||
|
||||
Gatewayd was an open-source project that began roughly one year ago, and it was broadly designed for the use-cases we discovered in the open-source community. Throughout the past year, we’ve explored these various use-cases and decided to narrow our focus. As such, we’re working on developing a more focused set of products to support our partners.
|
||||
|
||||
We would like to thank everyone who regularly used gatewayd. We appreciate your ongoing feedback, and we are continuously working on new products to better suit our customers’ needs. More details will be announced in the near future. Please contact us at <support@ripple.com> with any questions.
|
||||
46
content/blog/2015/introducing-the-data-api.md
Normal file
46
content/blog/2015/introducing-the-data-api.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Introducing the Data API
|
||||
|
||||
Ripple Labs data is moving under one roof, the Data API – a version two of the Historical Database API – and one infrastructure, Hadoop. We are building a much more robust and reliable way to import, parse, and house data that is not taxing on the Ripple network. Housing all of our endpoints together will allow us to scale much faster and with more confidence as the ledger and our data needs grow.
|
||||
|
||||
The Data API also includes a lot of new capabilities, including:
|
||||
|
||||
- historical normalization of currency values against XRP
|
||||
- added fields for exchanges that show the makers and takers of liquidity
|
||||
- more information on each account, including the funding source and date
|
||||
- a new endpoint to show account balance changes
|
||||
- new metrics in transaction stats
|
||||
|
||||
We are making these changes quickly and are focusing our resources on building out and testing new endpoints. Documentation for the Data API will soon be available on [ripple.com/build](http://ripple.com/build). The API will live at [data.ripple.com](http://data.ripple.com). In the meantime, you can look through the [Data API in GitHub](https://github.com/ripple/rippled-historical-database/blob/develop/README.md).
|
||||
|
||||
Because we are pulling all of the data endpoints under one API, we are shutting down some of our underutilized endpoints that are relying on infrastructure we are not moving forward with. The first shutdown will be CouchDB on August 19th, which we used early on for Ripple Charts. We are replacing some of the endpoints that hit CouchDB in the Data API, but other, less frequently used calls are being removed. In the next few months, we will also be shutting down the Ripple Charts API and replacing aspects of that by expanding the Data API. The Historical Database API – version one – will live in parallel to the Data API as we make this transition. It will be deprecated by the end of the year.
|
||||
|
||||
The following endpoints will be shut down:
|
||||
|
||||
- /api/totalNetworkValue
|
||||
- /api/totalValueSent
|
||||
- /api/valueSent
|
||||
- /api/offers
|
||||
- /api/currencyBalances
|
||||
- /api/accounttransactionstats
|
||||
- /api/accounttrust
|
||||
- /api/ledgersclosed
|
||||
|
||||
|
||||
The following endpoints will be replicated on the Data API:
|
||||
|
||||
| Current | Data API |
|
||||
|:----------------------------|:--------------------------------|
|
||||
| /api/offersExercised | /v2/exchanges |
|
||||
| /api/exchangeRates | /v2/exchange_rates |
|
||||
| /api/topMarkets | TBD |
|
||||
| /api/totalIssued | TBD |
|
||||
| /api/totalPaymentVolume | TBD |
|
||||
| /api/payments | TBD |
|
||||
| /api/accountsCreated | /v2/accounts |
|
||||
| /api/gateways | TBD |
|
||||
| /api/currencies | TBD |
|
||||
| /api/marketTraders | TBD |
|
||||
| /api/issuerCapitalization | TBD |
|
||||
| /api/accounttransactions | /v2/accounts/:account/payments |
|
||||
| /api/accountoffersexercised | /v2/accounts/:account/exchanges |
|
||||
| /api/transactionstats | /v2/stats |
|
||||
@@ -0,0 +1,22 @@
|
||||
# Ripple Charts Update: Payment Volume and Issued Value
|
||||
|
||||
Starting today, we are rolling out a small update to Ripple Charts that may seem minor but hopefully will have a big impact in the way we look at Ripple in the future.
|
||||
|
||||
On the Ripple Charts landing page we will be replacing two items:
|
||||
|
||||
## 1. "Transaction Volume" will be now changed to "Payment Volume"
|
||||
|
||||
We wanted to clearly distinguish that this number was specifically tracking payment transactions separate from any other transaction. Since the term transaction can mean many things in Ripple, we hope this adjustments makes things more clear.
|
||||
|
||||

|
||||
|
||||
## 2. "Total Network Value" will be broken down into two separate numbers, "XRP Capitalization" and "Issued Value"
|
||||
|
||||
As we spend more and more effort around building liquid assets on the Network, we didn't want to specifically tie real fiat currency assets with the volatility of XRP.
|
||||
|
||||
As many users pointed out in the forums, "Total Network Value" often ranged quite a bit due to the large influence of XRP price. We believe that this new addition will allow us to watch our issued value grow on the network while still being able to track the value of XRP going forward as well.
|
||||
|
||||

|
||||
|
||||
|
||||
We’d like to thank all of our passionate fans who regularly use Ripple Charts. We love and appreciate your ongoing feedback given our commitment to improving the charts experience—not only from the perspective of user experience but also in a way that best reflects the evolving direction of Ripple Labs.
|
||||
20
content/blog/2015/validator-registry.md
Normal file
20
content/blog/2015/validator-registry.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Validator Registry
|
||||
|
||||
At Ripple Labs, our goal is to expand the size and diversity of the Ripple consensus network by enabling people to easily run rippled validators and understand how those validators perform. We aim to create a network where validating participants are well known and respected by gathering and publicizing identity information of validators in addition to performance statistics.
|
||||
|
||||
Today, we are excited to announce the launch of the Ripple Validator Registry at [xrpcharts.ripple.com/#/validators](https://xrpcharts.ripple.com/#/validators). The Validator Registry gathers and publishes data for all network validators, enabling rippled operators to determine which validators to trust. An [http-based API](https://data.ripple.com/v2/network/validators) is also available for dynamically constructing rippled configurations.
|
||||
|
||||
|
||||
## Build a UNL
|
||||
Running rippled involves selecting a UNL - a unique node list of trusted validators. There are two primary factors in determining validator trustworthiness: performance and operator identity.
|
||||
|
||||
### Performance
|
||||
The Validator Registry provides several metrics for each validator based on uptime and network agreement:
|
||||
|
||||
- agreement - the percentage of ledgers that passed consensus that were validated by the validator
|
||||
- disagreement - the percentage of ledgers validated by the validator that did not pass consensus
|
||||
|
||||
### Identity
|
||||
Network participants are unlikely to trust validators without knowing who is operating them. To address this concern, validator operators can associate their validator with a web domain that they operate by following the steps on the [Ripple Dev Portal](https://ripple.com/build/rippled-apis/rippled-setup/#domain-verification). The Validator Registry verifies these domains and lists them with the validators.
|
||||
|
||||
As the consensus network continues to grow, we hope the Validator Registry plays a key role in decentralizing and strengthening the network through open data.
|
||||
Reference in New Issue
Block a user