Skip to content

Commit 77f77ad

Browse files
mvadariTapanito
andcommitted
Add XLS-17d: 0017 XLS-17d: XFL Developer-friendly representation of XRPL balances (XRPLF#319)
Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com>
1 parent dba89c1 commit 77f77ad

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

XLS-0017-xfl/README.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<pre>
2+
xls: 17
3+
title: XFL Developer-friendly representation of XRPL balances
4+
descrption: Introduces developer-friendly representation of XRPL balances.
5+
author: RichardAH (@RichardAH)
6+
created: 2021-03-19
7+
status: Final
8+
category: Protocol
9+
</pre>
10+
11+
# Background
12+
13+
The XRP ledger allows for two types of balances on accounts:
14+
15+
- Native `xrp` balances
16+
- IOU/Token `trustline` balances
17+
18+
Native balances are encoded and processed as signed 63bit integer values with an implicit decimal point at the 6th place from the right. A single unit is referred to as a drop. Thus the smallest possible value is `1 drop` represented logically as: `0.000001 xrp`.
19+
20+
Trustline balances are encoded and processed as a 63 bit decimal floating point number in the following format:
21+
{`sign bit` `8 bits of exponent` `54 bits of mantissa`}
22+
Note: This is not the IEEE floating point format (which is base 2.)
23+
24+
- The exponent is biased by 97. Thus an encoded exponent of `0b00000000` is `10^(-97)`.
25+
- The mantissa is normalised between `10^15` and `10^16 - 1`
26+
- The canonical zero of this format is all bits 0.
27+
28+
A 64th disambiguation bit is prepended (most significant bit) to both datatypes, which is `0` in the case of a native balance and `1` in the case of a trustline balance. [[1]](https://xrpl.org/serialization.html#amount-fields)
29+
30+
# Problem Definition
31+
32+
Performing computations between these two number formats can be difficult and error-prone for third party developers.
33+
34+
One existing partial solution is passing these amounts around as human-readable decimal numbers encoded as strings. This is computationally intensive and still does not allow numbers of one type to be mathematically operated on with numbers of the other type in a consistent and predictable way.
35+
36+
Internally the XRPL requires two independent types, however any xrp balance less than `10B` will translate without loss of precision into the trustline balance type. For amounts of xrp above `10B` (but less than `100B`) only 1 significant figure is lost from the least significant side. Thus in the worst possible case (moving >= `10B` XRP) `10` drops may be lost from an amount.
37+
38+
The benefits of representing xrp balances in the trustline balance format are:
39+
40+
- Much simpler developer experience (less mentally demanding, lower barrier to entry)
41+
- Can compute exchange rates between trustline balances and xrp
42+
- Can store all balance types in a single data type
43+
- A unified singular set of safe math routines which are much less likely to be used wrongly by developers
44+
45+
# XFL Format
46+
47+
The XFL format is designed for ease of use and maximum compatibility across a wide variety of processors and platforms.
48+
49+
For maximum ease of passing and returning from (pure) functions all XFL numbers are encoded into an `enclosing number` which is always a signed 64 bit integer, as follows:
50+
51+
Note: bit 63 is the most significant bit
52+
53+
- bit 63: `enclosing sign bit` always 0 for a valid XFL
54+
- bit 62: `internal sign bit` 0 = negative 1 = positive. note: this is not the int64_t's sign bit.
55+
- bit 61 - 53: `exponent` biased such that `0b000000000` = -97
56+
- bit 53 - 0: `mantissa` between `10^15` and `10^16 - 1`
57+
58+
Special case:
59+
60+
- Canonical zero: enclosing number = 0
61+
62+
Any XFL with a negative enclosing sign bit is `invalid`. This _DOES NOT_ refer to the internal sign bit inside the XFL format. It is definitely possible to have a negative value represented in an XFL, however these always exist with a _POSITIVE_ enclosing sign bit.
63+
64+
Invalid (negative enclosing sign bit) XFL values are reserved for propagation of error codes. If an invalid XFL is passed to an XFL processing function (for example `float_multiply`) it too should return an invalid XFL.
65+
66+
# Examples
67+
68+
| Number | Enclosing | To String |
69+
| ------ | ------------------- | ----------------------------- |
70+
| -1 | 1478180677777522688 | -1000000000000000 \* 10^(-15) |
71+
| 0 | 0000000000000000000 | [canonical zero] |
72+
| +1 | 6089866696204910592 | +1000000000000000 \* 10^(-15) |
73+
| +PI | 6092008288858500385 | +3141592653589793 \* 10^(-15) |
74+
| -PI | 1480322270431112481 | -3141592653589793 \* 10^(-15) |
75+
76+
# Reference Implementations
77+
78+
Javascript:
79+
80+
```js
81+
const minMantissa = 1000000000000000n;
82+
const maxMantissa = 9999999999999999n;
83+
const minExponent = -96;
84+
const maxExponent = 80;
85+
86+
function make_xfl(exponent, mantissa) {
87+
// convert types as needed
88+
if (typeof exponent != "bigint") exponent = BigInt(exponent);
89+
90+
if (typeof mantissa != "bigint") mantissa = BigInt(mantissa);
91+
92+
// canonical zero
93+
if (mantissa == 0n) return 0n;
94+
95+
// normalize
96+
let is_negative = mantissa < 0;
97+
if (is_negative) mantissa *= -1n;
98+
99+
while (mantissa > maxMantissa) {
100+
mantissa /= 10n;
101+
exponent++;
102+
}
103+
while (mantissa < minMantissa) {
104+
mantissa *= 10n;
105+
exponent--;
106+
}
107+
108+
// canonical zero on mantissa underflow
109+
if (mantissa == 0) return 0n;
110+
111+
// under and overflows
112+
if (exponent > maxExponent || exponent < minExponent) return -1; // note this is an "invalid" XFL used to propagate errors
113+
114+
exponent += 97n;
115+
116+
let xfl = !is_negative ? 1n : 0n;
117+
xfl <<= 8n;
118+
xfl |= BigInt(exponent);
119+
xfl <<= 54n;
120+
xfl |= BigInt(mantissa);
121+
122+
return xfl;
123+
}
124+
125+
function get_exponent(xfl) {
126+
if (xfl < 0n) throw "Invalid XFL";
127+
if (xfl == 0n) return 0n;
128+
return ((xfl >> 54n) & 0xffn) - 97n;
129+
}
130+
131+
function get_mantissa(xfl) {
132+
if (xfl < 0n) throw "Invalid XFL";
133+
if (xfl == 0n) return 0n;
134+
return xfl - ((xfl >> 54n) << 54n);
135+
}
136+
137+
function is_negative(xfl) {
138+
if (xfl < 0n) throw "Invalid XFL";
139+
if (xfl == 0n) return false;
140+
return ((xfl >> 62n) & 1n) == 0n;
141+
}
142+
143+
function to_string(xfl) {
144+
if (xfl < 0n) throw "Invalid XFL";
145+
if (xfl == 0n) return "<zero>";
146+
return (
147+
(is_negative(xfl) ? "-" : "+") +
148+
get_mantissa(xfl) +
149+
" * 10^(" +
150+
get_exponent(xfl) +
151+
")"
152+
);
153+
}
154+
```
155+
156+
C:
157+
158+
- See implementation in Hooks: [here](https://github.com/RichardAH/rippled-hooks/blob/6b132d6d1382e3ee61e6759cecad36f08b9e665f/src/ripple/app/tx/impl/applyHook.cpp#L86)

0 commit comments

Comments
 (0)