mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-18 02:35:49 +00:00
Serialization: clean up sample code, add CLI usage
This commit is contained in:
@@ -4,9 +4,11 @@
|
||||
# Author: rome@ripple.com
|
||||
# Copyright Ripple 2018
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import sys
|
||||
|
||||
from address import decode_address
|
||||
from xrpl_num import IssuedAmount
|
||||
@@ -15,6 +17,12 @@ logger = logging.getLogger(__name__)
|
||||
logger.addHandler(logging.StreamHandler())
|
||||
|
||||
def load_defs(fname="definitions.json"):
|
||||
"""
|
||||
Loads JSON from the definitions file and converts it to a preferred format.
|
||||
|
||||
(The definitions file should be drop-in compatible with the one from the
|
||||
ripple-binary-codec JavaScript package.)
|
||||
"""
|
||||
with open(fname) as definitions_file:
|
||||
definitions = json.load(definitions_file)
|
||||
return {
|
||||
@@ -53,33 +61,71 @@ def field_id(field_name):
|
||||
# high 4 bits is the type_code
|
||||
# low 4 bits is the field code
|
||||
combined_code = (type_code << 4) | field_code
|
||||
return bytes_from_uint(combined_code, 8)
|
||||
return uint8_to_bytes(combined_code)
|
||||
elif type_code >= 16 and field_code < 16:
|
||||
# first 4 bits are zeroes
|
||||
# next 4 bits is field code
|
||||
# next byte is type code
|
||||
byte1 = bytes_from_uint(field_code, 8)
|
||||
byte2 = bytes_from_uint(type_code, 8)
|
||||
byte1 = uint8_to_bytes(field_code)
|
||||
byte2 = uint8_to_bytes(type_code)
|
||||
return b''.join( (byte1, byte2) )
|
||||
elif type_code < 16 and field_code >= 16:
|
||||
# first 4 bits is type code
|
||||
# next 4 bits are zeroes
|
||||
# next byte is field code
|
||||
byte1 = bytes_from_uint(type_code << 4, 8)
|
||||
byte2 = bytes_from_uint(field_code, 8)
|
||||
byte1 = uint8_to_bytes(type_code << 4)
|
||||
byte2 = uint8_to_bytes(field_code)
|
||||
return b''.join( (byte1, byte2) )
|
||||
else: # both are >= 16
|
||||
# first byte is all zeroes
|
||||
# second byte is type
|
||||
# third byte is field code
|
||||
byte2 = bytes_from_uint(type_code, 8)
|
||||
byte3 = bytes_from_uint(field_code, 8)
|
||||
byte2 = uint8_to_bytes(type_code)
|
||||
byte3 = uint8_to_bytes(field_code)
|
||||
return b''.join( (bytes(1), byte2, byte3) )
|
||||
|
||||
def bytes_from_uint(i, bits):
|
||||
if bits % 8:
|
||||
raise ValueError("bytes_from_uint: bits must be divisible by 8")
|
||||
return i.to_bytes(bits // 8, byteorder="big", signed=False)
|
||||
def vl_encode(vl_contents):
|
||||
"""
|
||||
Helper function for variable-length fields including Blob types
|
||||
and some AccountID types.
|
||||
|
||||
Encodes arbitrary binary data with a length prefix. The length of the prefix
|
||||
is 1-3 bytes depending on the length of the contents:
|
||||
|
||||
Content length <= 192 bytes: prefix is 1 byte
|
||||
192 bytes < Content length <= 12480 bytes: prefix is 2 bytes
|
||||
12480 bytes < Content length <= 918744 bytes: prefix is 3 bytes
|
||||
"""
|
||||
|
||||
vl_len = len(vl_contents)
|
||||
if vl_len <= 192:
|
||||
len_byte = vl_len.to_bytes(1, byteorder="big", signed=False)
|
||||
return b''.join( (len_byte, vl_contents) )
|
||||
elif vl_len <= 12480:
|
||||
vl_len -= 193
|
||||
byte1 = ((vl_len >> 8) + 193).to_bytes(1, byteorder="big", signed=False)
|
||||
byte2 = (vl_len & 0xff).to_bytes(1, byteorder="big", signed=False)
|
||||
return b''.join( (byte1, byte2, vl_contents) )
|
||||
elif vl_len <= 918744:
|
||||
vl_len -= 12481
|
||||
byte1 = (241 + (vl_len >> 16)).to_bytes(1, byteorder="big", signed=False)
|
||||
byte2 = ((vl_len >> 8) & 0xff).to_bytes(1, byteorder="big", signed=False)
|
||||
byte3 = (vl_len & 0xff).to_bytes(1, byteorder="big", signed=False)
|
||||
return b''.join( (byte1, byte2, byte3, vl_contents) )
|
||||
|
||||
raise ValueError("VariableLength field must be <= 918744 bytes long")
|
||||
|
||||
|
||||
# Individual field type serialization routines ---------------------------------
|
||||
|
||||
def accountid_to_bytes(address):
|
||||
"""
|
||||
Serialize an AccountID field type. These are variable-length encoded.
|
||||
|
||||
Some fields contain nested non-VL-encoded AccountIDs directly; those call
|
||||
decode_address() instead of this function.
|
||||
"""
|
||||
return vl_encode(decode_address(address))
|
||||
|
||||
def amount_to_bytes(a):
|
||||
"""
|
||||
@@ -109,13 +155,38 @@ def amount_to_bytes(a):
|
||||
else:
|
||||
raise ValueError("amount must be XRP string or {currency, value, issuer}")
|
||||
|
||||
def issued_amount_as_bytes(strnum):
|
||||
num = Decimal(strnum)
|
||||
def array_to_bytes(array):
|
||||
"""
|
||||
Serialize an array of objects from decoded JSON.
|
||||
Each member object must have a type wrapper and an inner object.
|
||||
For example:
|
||||
[
|
||||
{
|
||||
// wrapper object
|
||||
"Memo": {
|
||||
// inner object
|
||||
"MemoType": "687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963",
|
||||
"MemoData": "72656e74"
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
members_as_bytes = []
|
||||
for el in array:
|
||||
wrapper_key = list(el.keys())[0]
|
||||
inner_obj = el[wrapper_key]
|
||||
members_as_bytes.append(field_to_bytes(field_name=wrapper_key, field_val=el))
|
||||
members_as_bytes.append(field_id("ArrayEndMarker"))
|
||||
return b''.join(members_as_bytes)
|
||||
|
||||
|
||||
def tx_type_to_bytes(txtype):
|
||||
type_uint = DEFINITIONS["TRANSACTION_TYPES"][txtype]
|
||||
return type_uint.to_bytes(2, byteorder="big", signed=False)
|
||||
def blob_to_bytes(field_val):
|
||||
"""
|
||||
Serializes a string of hex as binary data with a variable-length encoded
|
||||
length prefix. (The prefix is 1-3 bytes depending on the length of the
|
||||
contents.)
|
||||
"""
|
||||
vl_contents = bytes.fromhex(field_val)
|
||||
return vl_encode(vl_contents)
|
||||
|
||||
def currency_code_to_bytes(code_string, xrp_ok=False):
|
||||
if re.match(r"^[A-Za-z0-9?!@#$%^&*<>(){}\[\]|]{3}$", code_string):
|
||||
@@ -143,54 +214,43 @@ def currency_code_to_bytes(code_string, xrp_ok=False):
|
||||
else:
|
||||
raise ValueError("invalid currency code")
|
||||
|
||||
def vl_encode(vl_contents):
|
||||
vl_len = len(vl_contents)
|
||||
if vl_len <= 192:
|
||||
len_byte = vl_len.to_bytes(1, byteorder="big", signed=False)
|
||||
return b''.join( (len_byte, vl_contents) )
|
||||
elif vl_len <= 12480:
|
||||
vl_len -= 193
|
||||
byte1 = ((vl_len >> 8) + 193).to_bytes(1, byteorder="big", signed=False)
|
||||
byte2 = (vl_len & 0xff).to_bytes(1, byteorder="big", signed=False)
|
||||
return b''.join( (byte1, byte2, vl_contents) )
|
||||
elif vl_len <= 918744:
|
||||
vl_len -= 12481
|
||||
byte1 = (241 + (vl_len >> 16)).to_bytes(1, byteorder="big", signed=False)
|
||||
byte2 = ((vl_len >> 8) & 0xff).to_bytes(1, byteorder="big", signed=False)
|
||||
byte3 = (vl_len & 0xff).to_bytes(1, byteorder="big", signed=False)
|
||||
return b''.join( (byte1, byte2, byte3, vl_contents) )
|
||||
def hash128_to_bytes(contents):
|
||||
"""
|
||||
Serializes a hexadecimal string as binary and confirms that it's 128 bits
|
||||
"""
|
||||
b = hash_to_bytes(contents)
|
||||
if len(b) != 16: # 16 bytes = 128 bits
|
||||
raise ValueError("Hash128 is not 128 bits long")
|
||||
return b
|
||||
|
||||
raise ValueError("VariableLength field must be <= 918744 bytes long")
|
||||
def hash160_to_bytes(contents):
|
||||
b = hash_to_bytes(contents)
|
||||
if len(b) != 20: # 20 bytes = 160 bits
|
||||
raise ValueError("Hash160 is not 160 bits long")
|
||||
return b
|
||||
|
||||
def vl_to_bytes(field_val):
|
||||
vl_contents = bytes.fromhex(field_val)
|
||||
return vl_encode(vl_contents)
|
||||
def hash256_to_bytes(contents):
|
||||
b = hash_to_bytes(contents)
|
||||
if len(b) != 32: # 32 bytes = 256 bits
|
||||
raise ValueError("Hash256 is not 256 bits long")
|
||||
return b
|
||||
|
||||
def hash_to_bytes(contents):
|
||||
"""
|
||||
Helper function; serializes a hash value from a hexadecimal string
|
||||
of any length.
|
||||
"""
|
||||
return bytes.fromhex(field_val)
|
||||
|
||||
def accountid_to_bytes(address):
|
||||
return vl_encode(decode_address(address))
|
||||
|
||||
def array_to_bytes(array):
|
||||
"""
|
||||
Serialize an array of objects.
|
||||
Each member object must have a type wrapper and an inner object.
|
||||
"""
|
||||
members_as_bytes = []
|
||||
for el in array:
|
||||
wrapper_key = list(el.keys())[0]
|
||||
inner_obj = el[wrapper_key]
|
||||
members_as_bytes.append(field_to_bytes(field_name=wrapper_key, field_val=el))
|
||||
members_as_bytes.append(field_id("ArrayEndMarker"))
|
||||
return b''.join(members_as_bytes)
|
||||
|
||||
def object_to_bytes(obj):
|
||||
"""
|
||||
Serialize an object, assuming a type wrapper, for example:
|
||||
Serialize an object from decoded JSON.
|
||||
Each object must have a type wrapper and an inner object. For example:
|
||||
|
||||
{
|
||||
// type wrapper
|
||||
"SignerEntry": {
|
||||
// inner object
|
||||
"Account": "rUpy3eEg8rqjqfUoLeBnZkscbKbFsKXC3v",
|
||||
"SignerWeight": 1
|
||||
}
|
||||
@@ -260,11 +320,31 @@ def path_as_bytes(path):
|
||||
if "issuer" in step.keys():
|
||||
type_byte |= 0x20
|
||||
step_data.append(decode_address(step["issuer"]))
|
||||
step_data = [bytes_from_uint(type_byte, 8)] + step_data
|
||||
step_data = [uint8_to_bytes(type_byte)] + step_data
|
||||
path_contents += step_data
|
||||
|
||||
return b''.join(path_contents)
|
||||
|
||||
def tx_type_to_bytes(txtype):
|
||||
"""
|
||||
TransactionType field is a special case that is written in JSON
|
||||
as a string name but in binary as a UInt16.
|
||||
"""
|
||||
type_uint = DEFINITIONS["TRANSACTION_TYPES"][txtype]
|
||||
return uint16_to_bytes(type_uint)
|
||||
|
||||
|
||||
def uint8_to_bytes(i):
|
||||
return i.to_bytes(1, byteorder="big", signed=False)
|
||||
|
||||
def uint16_to_bytes(i):
|
||||
return i.to_bytes(2, byteorder="big", signed=False)
|
||||
|
||||
def uint32_to_bytes(i):
|
||||
return i.to_bytes(4, byteorder="big", signed=False)
|
||||
|
||||
|
||||
# Core serialization logic -----------------------------------------------------
|
||||
|
||||
def field_to_bytes(field_name, field_val):
|
||||
"""
|
||||
@@ -285,22 +365,51 @@ def field_to_bytes(field_name, field_val):
|
||||
# TypeName: function(field): bytes object
|
||||
"AccountID": accountid_to_bytes,
|
||||
"Amount": amount_to_bytes,
|
||||
"Blob": vl_to_bytes,
|
||||
"Hash128": hash_to_bytes,
|
||||
"Hash160": hash_to_bytes,
|
||||
"Hash256": hash_to_bytes,
|
||||
"Blob": blob_to_bytes,
|
||||
"Hash128": hash128_to_bytes,
|
||||
"Hash160": hash160_to_bytes,
|
||||
"Hash256": hash256_to_bytes,
|
||||
"PathSet": pathset_to_bytes,
|
||||
"STArray": array_to_bytes,
|
||||
"STObject": object_to_bytes,
|
||||
"UInt8" : lambda x:bytes_from_uint(x, 8),
|
||||
"UInt16": lambda x:bytes_from_uint(x, 16),
|
||||
"UInt32": lambda x:bytes_from_uint(x, 32),
|
||||
"UInt64": lambda x:bytes_from_uint(x, 64),
|
||||
"UInt8" : uint8_to_bytes,
|
||||
"UInt16": uint16_to_bytes,
|
||||
"UInt32": uint32_to_bytes,
|
||||
}
|
||||
field_binary = dispatch[field_type](field_val)
|
||||
return b''.join( (id_prefix, field_binary) )
|
||||
|
||||
def serialize_tx(tx):
|
||||
def serialize_tx(tx, for_signing=False):
|
||||
"""
|
||||
Takes a transaction as decoded JSON and returns a bytes object representing
|
||||
the transaction in binary format.
|
||||
|
||||
The input format should omit transaction metadata and the transaction
|
||||
should be formatted with the transaction instructions at the top level.
|
||||
("hash" can be included, but will be ignored)
|
||||
|
||||
If for_signing=True, then only signing fields are serialized, so you can use
|
||||
the output to sign the transaction.
|
||||
|
||||
SigningPubKey and TxnSignature are optional, but the transaction can't
|
||||
be submitted without them.
|
||||
|
||||
For example:
|
||||
|
||||
{
|
||||
"TransactionType" : "Payment",
|
||||
"Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
||||
"Destination" : "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"Amount" : {
|
||||
"currency" : "USD",
|
||||
"value" : "1",
|
||||
"issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
|
||||
},
|
||||
"Fee": "12",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 2
|
||||
}
|
||||
"""
|
||||
field_order = sorted(tx.keys(), key=field_sort_key)
|
||||
logger.debug("Canonical field order: %s" % field_order)
|
||||
|
||||
@@ -313,41 +422,39 @@ def serialize_tx(tx):
|
||||
fields_as_bytes.append(field_bytes)
|
||||
|
||||
all_serial = b''.join(fields_as_bytes)
|
||||
logger.info(all_serial.hex().upper())
|
||||
logger.debug(all_serial.hex().upper())
|
||||
return all_serial
|
||||
|
||||
# Startup stuff ----------------------------------------------------------------
|
||||
logger.setLevel(logging.WARNING)
|
||||
DEFINITIONS = load_defs()
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
# Commandline utility ----------------------------------------------------------
|
||||
# parses JSON from a file or commandline argument and prints the serialized
|
||||
# form of the transaction as hex
|
||||
if __name__ == "__main__":
|
||||
logger.setLevel(logging.DEBUG)
|
||||
DEFINITIONS = load_defs()
|
||||
p = argparse.ArgumentParser()
|
||||
txsource = p.add_mutually_exclusive_group()
|
||||
txsource.add_argument("-f", "--filename", default="test-cases/tx1.json",
|
||||
help="Read input transaction from a JSON file. (Uses test-cases/tx1.json by default)")
|
||||
txsource.add_argument("-j", "--json",
|
||||
help="Read input transaction JSON from the command line")
|
||||
txsource.add_argument("--stdin", action="store_true", default=False,
|
||||
help="Read input transaction JSON from standard input (stdin)")
|
||||
p.add_argument("-v", "--verbose", action="store_true", default=False,
|
||||
help="Display debug messages (such as individual field serializations)")
|
||||
args = p.parse_args()
|
||||
|
||||
# example_tx = {
|
||||
# "TransactionType" : "Payment",
|
||||
# "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
|
||||
# "Destination" : "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
# "Amount" : {
|
||||
# "currency" : "USD",
|
||||
# "value" : "1",
|
||||
# "issuer" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
|
||||
# },
|
||||
# "Fee": "12",
|
||||
# "Flags": 2147483648,
|
||||
# "Sequence": 2
|
||||
# }
|
||||
if args.verbose:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
with open("test-cases/tx3-nometa.json") as f:
|
||||
example_tx = json.load(f)
|
||||
# Determine source of JSON transaction:
|
||||
if args.json:
|
||||
example_tx = json.loads(args.json)
|
||||
elif args.stdin:
|
||||
example_tx = json.load(sys.stdin)
|
||||
else:
|
||||
with open(args.filename) as f:
|
||||
example_tx = json.load(f)
|
||||
|
||||
serialize_tx(example_tx)
|
||||
|
||||
# example rippled signature:
|
||||
# ./rippled sign masterpassphrase (the above JSON)
|
||||
# where "masterpassphrase" is the key behind rHb9...
|
||||
# snoPBrXtMeMyMHUVTgbuqAfg1SUTb in base58
|
||||
# "tx_blob" : "1200002280000000240000000261D4838D7EA4C6800000000000000000000000000055534400000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA968400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD0207446304402201FE0A74FC1BDB509C8F42B861EF747C43B92917706BB623F0A0D621891933AF402205206FBA8B0BF6733DB5B03AD76B5A76A2D46DF9093916A3BEC78897E58A3DF148114B5F762798A53D543A014CAF8B297CFF8F2F937E883143E9D4A2B8AA0780F682D136F7A56D6724EF53754",
|
||||
# "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
|
||||
# "TxnSignature" : "304402201FE0A74FC1BDB509C8F42B861EF747C43B92917706BB623F0A0D621891933AF402205206FBA8B0BF6733DB5B03AD76B5A76A2D46DF9093916A3BEC78897E58A3DF14",
|
||||
# "hash" : "8BA1509E4FB80CCF76CD9DE924B8B71597637C775BA2DC515F90C333DA534BF3"
|
||||
print(serialize_tx(example_tx).hex().upper())
|
||||
|
||||
52
content/_code-samples/tx-serialization/test-cases/README.md
Normal file
52
content/_code-samples/tx-serialization/test-cases/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Transaction Serialization Test Cases
|
||||
|
||||
This folder contains several transactions in their JSON and binary forms, which
|
||||
you can use to verify the behavior of transaction serialization code.
|
||||
|
||||
For example (starting from the `tx-serialization/` dir above this one):
|
||||
|
||||
```bash
|
||||
$ python3 serialize.py -f test-cases/tx2.json | \
|
||||
diff - test-cases/tx2-binary.txt
|
||||
```
|
||||
|
||||
The expected result is no output because the output of `serialize.py` matches
|
||||
the contents of `test-cases/tx2-binary.txt` exactly.
|
||||
|
||||
For an example of how the output is different if you change the `Fee` parameter of sample transaction 1, we can pipe a modified version of the file into the serializer:
|
||||
|
||||
```bash
|
||||
$ cat test-cases/tx1.json | \
|
||||
sed -e 's/"Fee": "10"/"Fee": "100"/' | \
|
||||
python3 serialize.py --stdin | \
|
||||
diff - test-cases/tx1-binary.txt --color
|
||||
```
|
||||
|
||||
The output shows that the two versions of the transaction binary are different (but because they're all on one line, it's not super clear _where_ within the line the difference is):
|
||||
|
||||
```text
|
||||
1c1
|
||||
< 120007220008000024001ABED82A2380BF2C2019001ABED764D55920AC93914000000000000000
|
||||
00000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D165400000037E
|
||||
11D600684000000000000064732103EE83BB432547885C219634A1BC407A9DB0474145D69737D09C
|
||||
CDC63E1DEE7FE3744630440220143759437C04F7B61F012563AFE90D8DAFC46E86035E1D965A9CED
|
||||
282C97D4CE02204CFD241E86F17E011298FC1A39B63386C74306A5DE047E213B0F29EFA4571C2C81
|
||||
14DD76483FACDEE26E60D8A586BB58D09F27045C46
|
||||
---
|
||||
> 120007220008000024001ABED82A2380BF2C2019001ABED764D55920AC93914000000000000000
|
||||
00000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D165400000037E
|
||||
11D60068400000000000000A732103EE83BB432547885C219634A1BC407A9DB0474145D69737D09C
|
||||
CDC63E1DEE7FE3744630440220143759437C04F7B61F012563AFE90D8DAFC46E86035E1D965A9CED
|
||||
282C97D4CE02204CFD241E86F17E011298FC1A39B63386C74306A5DE047E213B0F29EFA4571C2C81
|
||||
14DD76483FACDEE26E60D8A586BB58D09F27045C46
|
||||
```
|
||||
|
||||
(If you're curious, the difference appears in the third line of each blob in this example. The modified version of the transaction serializes the `Fee` amount ending in `64` (hex for 100) while the original version ended in `0A` (hex for 10).)
|
||||
|
||||
For a friendlier display, you could pipe the output of the serializer to a file and use a visual tool like [Meld](http://meldmerge.org/) that shows intra-line differences:
|
||||
|
||||
```bash
|
||||
$ cat test-cases/tx1.json | sed -e 's/"Fee": "10"/"Fee": "100"/' | python3 serialize.py --stdin > /tmp/tx1-modified.txt && meld /tmp/tx1-modified.txt test-cases/tx1-binary.txt
|
||||
```
|
||||
|
||||

|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -1,125 +0,0 @@
|
||||
{
|
||||
"Account": "rMBzp8CgpE441cp5PVyA9rpVV7oT8hP3ys",
|
||||
"Expiration": 595640108,
|
||||
"Fee": "10",
|
||||
"Flags": 524288,
|
||||
"OfferSequence": 1752791,
|
||||
"Sequence": 1752792,
|
||||
"SigningPubKey": "03EE83BB432547885C219634A1BC407A9DB0474145D69737D09CCDC63E1DEE7FE3",
|
||||
"TakerGets": "15000000000",
|
||||
"TakerPays": {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"value": "7072.8"
|
||||
},
|
||||
"TransactionType": "OfferCreate",
|
||||
"TxnSignature": "30440220143759437C04F7B61F012563AFE90D8DAFC46E86035E1D965A9CED282C97D4CE02204CFD241E86F17E011298FC1A39B63386C74306A5DE047E213B0F29EFA4571C2C",
|
||||
"hash": "73734B611DDA23D3F5F62E20A173B78AB8406AC5015094DA53F53D39B9EDB06C",
|
||||
"metaData": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"IndexNext": "0000000000000000",
|
||||
"IndexPrevious": "0000000000000000",
|
||||
"Owner": "rMBzp8CgpE441cp5PVyA9rpVV7oT8hP3ys",
|
||||
"RootIndex": "10AF5737F535F47CA9E8B6F82C4B7F4D998B1B7C44185C6078A22C751FD9FB7D"
|
||||
},
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "10AF5737F535F47CA9E8B6F82C4B7F4D998B1B7C44185C6078A22C751FD9FB7D"
|
||||
}
|
||||
},
|
||||
{
|
||||
"DeletedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rMBzp8CgpE441cp5PVyA9rpVV7oT8hP3ys",
|
||||
"BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E10BFD011CB2800",
|
||||
"BookNode": "0000000000000000",
|
||||
"Expiration": 595640096,
|
||||
"Flags": 131072,
|
||||
"OwnerNode": "0000000000000000",
|
||||
"PreviousTxnID": "50C2CBD3BEF831D80C2950D3001E67F1D257665569A9D77B1F0E0B8B4D178CEB",
|
||||
"PreviousTxnLgrSeq": 43010795,
|
||||
"Sequence": 1752791,
|
||||
"TakerGets": "15000000000",
|
||||
"TakerPays": {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"value": "7071.75"
|
||||
}
|
||||
},
|
||||
"LedgerEntryType": "Offer",
|
||||
"LedgerIndex": "233E9A034C083E895EF7B4F6A643291FEF1608D55C8C5783F71E9C5F82D3E7FB"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rMBzp8CgpE441cp5PVyA9rpVV7oT8hP3ys",
|
||||
"Balance": "37180946255",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 6,
|
||||
"Sequence": 1752793
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "9EB65374048F2AED1995A6725D4234545432083B0C5728627E06443A8E1F4C98",
|
||||
"PreviousFields": {
|
||||
"Balance": "37180946265",
|
||||
"Sequence": 1752792
|
||||
},
|
||||
"PreviousTxnID": "50C2CBD3BEF831D80C2950D3001E67F1D257665569A9D77B1F0E0B8B4D178CEB",
|
||||
"PreviousTxnLgrSeq": 43010795
|
||||
}
|
||||
},
|
||||
{
|
||||
"DeletedNode": {
|
||||
"FinalFields": {
|
||||
"ExchangeRate": "4E10BFD011CB2800",
|
||||
"Flags": 0,
|
||||
"RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E10BFD011CB2800",
|
||||
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
|
||||
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
|
||||
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
|
||||
"TakerPaysIssuer": "0A20B3C85F482532A9578DBB3950B85CA06594D1"
|
||||
},
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E10BFD011CB2800"
|
||||
}
|
||||
},
|
||||
{
|
||||
"CreatedNode": {
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E10C0730D0B8000",
|
||||
"NewFields": {
|
||||
"ExchangeRate": "4E10C0730D0B8000",
|
||||
"RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E10C0730D0B8000",
|
||||
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
|
||||
"TakerPaysIssuer": "0A20B3C85F482532A9578DBB3950B85CA06594D1"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"CreatedNode": {
|
||||
"LedgerEntryType": "Offer",
|
||||
"LedgerIndex": "FD6C2E2D72319FB0C22FC50B0AF993B2AF26717927EAEB1E8857971EE2C3CADD",
|
||||
"NewFields": {
|
||||
"Account": "rMBzp8CgpE441cp5PVyA9rpVV7oT8hP3ys",
|
||||
"BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4E10C0730D0B8000",
|
||||
"Expiration": 595640108,
|
||||
"Flags": 131072,
|
||||
"Sequence": 1752792,
|
||||
"TakerGets": "15000000000",
|
||||
"TakerPays": {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"value": "7072.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 0,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
{
|
||||
"hash": "C0B450C8601E19CB0BDED71C4B523B2A4AAB77415B08E7923D8DA3F831631702",
|
||||
"ledger_index": 36259236,
|
||||
"date": "2018-02-01T09:45:32+00:00",
|
||||
"tx": {
|
||||
"TransactionType": "EscrowFinish",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1,
|
||||
"OfferSequence": 11,
|
||||
"Fee": "10101",
|
||||
"SigningPubKey": "0268D79CD579D077750740FA18A2370B7C2018B2714ECE70BA65C38D223E79BC9C",
|
||||
"TxnSignature": "3045022100F06FB54049D6D50142E5CF2E2AC21946AF305A13E2A2D4BA881B36484DD01A540220311557EC8BEF536D729605A4CB4D4DC51B1E37C06C93434DD5B7651E1E2E28BF",
|
||||
"Account": "r3Y6vCE8XqfZmYBRngy22uFYkmz3y9eCRA",
|
||||
"Owner": "r9NpyVfLfUG8hatuCCHKzosyDtKnBdsEN3",
|
||||
"Memos": [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoData": "04C4D46544659A2D58525043686174"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 35,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"DeletedNode": {
|
||||
"LedgerEntryType": "Escrow",
|
||||
"LedgerIndex": "983EBDF89C1C30CECDB105B593E7DEBE602AF89012EDB4DD76D24ACEF92C89EF",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"PreviousTxnLgrSeq": 35059511,
|
||||
"FinishAfter": 570758400,
|
||||
"OwnerNode": "0000000000000000",
|
||||
"PreviousTxnID": "6F54E04D7B205CBE2FCAEF1C249E62A9759721C7FE1F6992FD800266C8E4814C",
|
||||
"Amount": "1000000000000000",
|
||||
"Account": "r9NpyVfLfUG8hatuCCHKzosyDtKnBdsEN3",
|
||||
"Destination": "r9NpyVfLfUG8hatuCCHKzosyDtKnBdsEN3"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "C05DC35EBDCA5D8697190FF41950EC5AFCBF7F61C1177DC73000CC17C2149886",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"RootIndex": "C05DC35EBDCA5D8697190FF41950EC5AFCBF7F61C1177DC73000CC17C2149886",
|
||||
"Owner": "r9NpyVfLfUG8hatuCCHKzosyDtKnBdsEN3"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 36230933,
|
||||
"PreviousTxnID": "9366F75CD350ACE0EEFC0A392ECBD5AC2B84C06E5DEC2DE895B76FFC7BD55553",
|
||||
"LedgerIndex": "C180CA555CE8820D8F1086CDCA756FF8D5813FEB2AE3FF002B9F48F870CA08A0",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1,
|
||||
"Balance": "26000000"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 2,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "25989899",
|
||||
"Account": "r3Y6vCE8XqfZmYBRngy22uFYkmz3y9eCRA"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 36253527,
|
||||
"PreviousTxnID": "F2AA5584005847C19F59A9C87E7BF3108F97F2567C5083159EEC40B08ED90F46",
|
||||
"LedgerIndex": "DCED5AAFE87AA8D00E651DBBFBA2F992927BC3DC5FFD905EF49019ED02824B3A",
|
||||
"PreviousFields": {
|
||||
"OwnerCount": 14,
|
||||
"Balance": "200923000"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 1048576,
|
||||
"Sequence": 18,
|
||||
"OwnerCount": 13,
|
||||
"Balance": "1000000200923000",
|
||||
"Account": "r9NpyVfLfUG8hatuCCHKzosyDtKnBdsEN3"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user