diff --git a/.cargo-husky/hooks/pre-commit b/.cargo-husky/hooks/pre-commit new file mode 100755 index 00000000..c2e5f7bb --- /dev/null +++ b/.cargo-husky/hooks/pre-commit @@ -0,0 +1,10 @@ +#!/bin/sh +set -e + +echo 'Running all pre-commit checks:' +cargo fmt +cargo test --no-default-features --features core,models,utils +cargo test --all-features +cargo clippy --fix --allow-staged +cargo doc --no-deps +#cargo audit diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 00000000..46535c6d --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,26 @@ +## Expected Behavior + + +## Actual Behavior + + +## Steps to Reproduce the Problem + + 1. + 1. + 1. + +## Minimal Reproducable Example + +```rust + +``` + +## Specifications + + - Version: + - Platform: + - Flags: + +## Detailed Description + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..f167cb98 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,50 @@ +## High Level Overview of Change + + + +### Context of Change + + + +### Type of Change + + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Refactor (non-breaking change that only restructures code) +- [ ] Tests (You added tests for code that already exists, or your new feature included in this PR) +- [ ] Documentation Updates +- [ ] Release + +## Before / After + + + +## Test Plan + + + + diff --git a/.github/workflows/audit_test.yml b/.github/workflows/audit_test.yml new file mode 100644 index 00000000..ed37267a --- /dev/null +++ b/.github/workflows/audit_test.yml @@ -0,0 +1,20 @@ +on: + push: + branches: + - develop + pull_request: + branches: + - main + schedule: + - cron: '0 0 * * *' + +name: Audit + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/quality_test.yml b/.github/workflows/quality_test.yml new file mode 100644 index 00000000..f553fea0 --- /dev/null +++ b/.github/workflows/quality_test.yml @@ -0,0 +1,20 @@ +on: + push: + branches: + - develop + pull_request: + branches: + - main + +name: Quality + +jobs: + clippy_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - run: rustup component add clippy + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-features diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml new file mode 100644 index 00000000..0d2e2857 --- /dev/null +++ b/.github/workflows/unit_test.yml @@ -0,0 +1,35 @@ +on: + push: + branches: + - develop + pull_request: + branches: + - main + +name: Unit + +jobs: + build_and_test: + name: xrpl-rust + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - uses: actions-rs/cargo@v1 + with: + command: build + args: --release --all-features + - uses: actions-rs/cargo@v1 + with: + command: build + args: --release --no-default-features --features core,models + - uses: actions-rs/cargo@v1 + with: + command: test + args: --all-features + - uses: actions-rs/cargo@v1 + with: + command: test + args: --no-default-features --features core,models diff --git a/.gitignore b/.gitignore index 088ba6ba..2fbef9f4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,12 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk + +# Added by cargo +/target + +# VSCode +.vscode + +# Additional +src/main.rs diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..40a484fe --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [[Incomplete]] +- Support for no_std environments +## [[Unreleased]] +### Modified +- Use the core hex library +### Added +- Initial Implementation diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..6d1bffed --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @sephynox diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..246e4dd7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,134 @@ +# Contributing + +## Setup Your Development Environment + +If you want to contribute code to `xrpl-rust`, the following sections describe +how to set up your developer environment. + +### Setup the Rust/Cargo Environment + +Getting started with Rust and `xrpl-rust` is easy. To install `rust` and +`cargo` follow these steps: + +* Install [`rust`](https://doc.rust-lang.org/cargo/getting-started/installation.html): + + curl https://sh.rustup.rs -sSf | sh + +* Update rust using `rustup` and install a few development dependencies: + + // Rustup + rustup update + rustup component add rustfmt + rustup component add clippy-preview + + // Cargo + cargo install cargo-audit + +### Git `pre-commit` Hooks + +To run linting and other checks, `xrpl-rust` uses +[`pre-commit`](https://pre-commit.com/). + +> This should already be setup thanks to +[`cargo-husky`](https://github.com/rhysd/cargo-husky) + +### Run the Formatter + +To run the linter: + +```bash +cargo fmt +``` + +> Note that the formatter will automatically run via pre-commit hook + +### Run the Linter + +To run the linter: + +```bash +cargo clippy +``` + +> Note that the linter will automatically run via pre-commit hook + +### Running Tests + +To run tests: + +```bash +# Test the core feature for no_std +cargo test --no-default-features --features core,models,utils +# Test all features enabled +cargo test --all-features +``` + +> Note that the tests will automatically run via pre-commit hook + +### Generate Documentation + +You can see the complete reference documentation at +[`xrpl-rust` docs](https://docs.rs/xrpl). + +You can also generate them locally using `cargo`: + +```bash +cargo doc +``` + +### Audit Crates + +To test dependencies for known security advisories, run: + +```bash +cargo audit +``` + +### Submitting Bugs + +Bug reports are welcome. Please create an issue using the default issue +template. Fill in *all* information including a minimal reproducible +code example. Every function in the library comes with such an example +and can adapted to look like the following for an issue report: + +```rust +// Required Dependencies +use xrpl::core::keypairs::derive_keypair; +use xrpl::core::keypairs::exceptions::XRPLKeypairsException; + +// Provided Variables +let seed: &str = "sn259rEFXrQrWyx3Q7XneWcwV6dfL"; +let validator: bool = false; + +// Expected Result +let tuple: (String, String) = ( + "ED60292139838CB86E719134F848F055057CA5BDA61F5A529729F1697502D53E1C".into(), + "ED009F66528611A0D400946A01FA01F8AF4FF4C1D0C744AE3F193317DCA77598F1".into(), +); + +// Operation +match derive_keypair(seed, validator) { + Ok(seed) => assert_eq!(tuple, seed), + Err(e) => match e { + XRPLKeypairsException::InvalidSignature => panic!("Fails unexpectedly"), + _ => (), + }, +}; +``` +> This format makes it easy for maintainers to replicate and test against. + +## Release Process + +### Editing the Code + +* Your changes should have unit and/or integration tests. +* New functionality should include a minimal reproducible sample. +* Your changes should pass the linter. +* Your code should pass all the actions on GitHub. +* Open a PR against `main` and ensure that all CI passes. +* Get a full code review from one of the maintainers. +* Merge your changes. + +### Release + +TODO diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..41817880 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "xrpl-rust" +version = "0.1.1" +edition = "2018" +authors = ["Tanveer Wahid "] +description = "A 100% Rust library to interact with the XRPL" +readme = "README.md" +license = "ISC" +repository = "https://github.com/589labs/xrpl-rust" + +keywords = ["xrpl", "no_std"] +categories = ["no-std"] + +[package.metadata.release] +no-dev-version = true +tag-name = "{{version}}" + +[lib] +name = "xrpl" +crate-type = ["lib"] + +[dependencies] +lazy_static = "1.4.0" +sha2 = "0.9.8" +rand_hc = "0.3.1" +ripemd160 = "0.9.1" +ed25519-dalek = "1.0.1" +indexmap = { version = "1.7.0", features = ["serde"] } +strum = { version = "0.22.0", default-features = false } +strum_macros = { version = "0.22.0", default-features = false } +regex = { version = "1.5.4", default-features = false } +num-bigint = { version = "0.4.2", default-features = false } +rust_decimal = { version = "1.17.0", default-features = false, features = ["serde"] } +chrono = { version = "0.4.19", default-features = false, features = ["alloc", "clock"] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +bs58 = { version = "0.4.0", default-features = false, features = ["check", "alloc"] } +serde = { version = "1.0.130", default-features = false, features = ["derive"] } +serde_json = { version = "1.0.68", default-features = false, features = ["alloc"] } +rand = { version = "0.8.4", default-features = false, features = ["getrandom"] } +secp256k1 = { version = "0.20.3", default-features = false, features = ["alloc"] } + +[dev-dependencies] +criterion = "0.3.5" +cargo-husky = { version = "1.5.0", default-features = false, features = ["user-hooks"] } + +[[bench]] +name = "benchmarks" +harness = false + +[features] +default = ["std", "core", "models", "utils"] +models = ["core"] +core = ["utils"] +utils = [] +std = [ + "rand/std", + "regex/std", + "chrono/std", + "num-bigint/std", + "rand/std_rng", + "hex/std", + "rust_decimal/std", + "bs58/std", + "serde/std", + "indexmap/std", + "secp256k1/std" +] diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..fc999a6c --- /dev/null +++ b/LICENSE @@ -0,0 +1,10 @@ +Copyright (c) 2021, Tanveer Wahid + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby +granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md index 4f0ddd50..6c9624ff 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,105 @@ -# xrpl-rust -A Rust library to interact with the XRPL . +# xrpl-rust ![Downloads](https://img.shields.io/crates/d/xrpl-rust) +[![latest]][crates.io] [![deps_status]][deps] [![audit_status]][audit] [![unit_status]][unit] + +[latest]: https://img.shields.io/crates/v/xrpl-rust.svg +[crates.io]: https://crates.io/crates/xrpl-rust + +[docs_status]: https://docs.rs/xrpl-rust/badge.svg +[docs]: https://docs.rs/xrpl-rust + +[deps_status]: https://deps.rs/repo/github/589labs/xrpl-rust/status.svg +[deps]: https://deps.rs/repo/github/589labs/xrpl-rust + +[audit_status]: https://github.com/589labs/xrpl-rust/actions/workflows/audit_test.yml/badge.svg +[audit]: https://github.com/589labs/xrpl-rust/actions/workflows/audit_test.yml + +[rustc]: https://img.shields.io/badge/rust-1.51.0%2B-orange.svg +[rust]: https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html + +[unit_status]: https://github.com/589labs/xrpl-rust/actions/workflows/unit_test.yml/badge.svg +[unit]: https://github.com/589labs/xrpl-rust/actions/workflows/unit_test.yml + +[contributors]: https://github.com/589labs/xrpl-rust/graphs/contributors +[contributors_status]: https://img.shields.io/github/contributors/589labs/xrpl-rust.svg + +[license]: https://opensource.org/licenses/ISC +[license_status]: https://img.shields.io/badge/License-ISC-blue.svg + +A Rust library to interact with the XRPL. +Based off of the [xrpl-py](https://github.com/XRPLF/xrpl-py) library. + +A pure Rust implementation for interacting with the XRP Ledger. The xrpl-rust +crate simplifies the hardest parts of XRP Ledger interaction including +serialization and transaction signing while providing idiomatic Rust +functionality for XRP Ledger transactions and core server API (rippled) +objects. + +Interactions with this crate occur using data structures from this crate or +core [alloc](https://doc.rust-lang.org/alloc) types with the exception of +serde for JSON handling and indexmap for dictionaries. The goal is to ensure +this library can be used on devices without the ability to use a +[std](hhttps://doc.rust-lang.org/std) environment. + +> WIP - Help Welcome + +# 🛠 Installation [![rustc]][rust] + +To install, add the following to your project's `Cargo.toml`: + +```toml +[dependencies.xrpl] +version = "0.1.1" +``` + +# 🕮 Documentation [![docs_status]][docs] + +Documentation is available [here](https://docs.rs/xrpl). + +## ⛮ Quickstart +TODO - Most core functionality is in place and working. + +In Progres: +* Models +* Asynchronous ledger interactions + * JSON RPC + * API + * Websocket +* Benchmarks +* Integration tests + +# ⚐ Flags + +By default, the `std` and `core` features are enabled. +To operate in a `#![no_std]` environment simply disable the defaults +and enable features manually: + +```toml +[dependencies.xrpl] +version = "0.1.1" +default-features = false +features = ["core", "models"] +``` + +## ‼ Exported Dependencies + +### Serde + +This project exports [serde](https://serde.rs) for handling JSON. + +### Indexmap + +This project exports [indexmap](https://docs.rs/crate/indexmap) as `HashMap` is +not supported in the `alloc` crate. TODO: Support both. + +## ⚙ #![no_std] + +This library aims to be `#![no_std]` compliant. + +# 🕊 Contributing [![contributors_status]][contributors] + +If you want to contribute to this project, see [CONTRIBUTING](CONTRIBUTING.md). + +# 🗎 License [![license_status]][license] + +The `xrpl-rust` library is licensed under the ISC License. +See [LICENSE](LICENSE) for more information. diff --git a/benches/benchmarks.rs b/benches/benchmarks.rs new file mode 100644 index 00000000..42e85079 --- /dev/null +++ b/benches/benchmarks.rs @@ -0,0 +1,18 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use xrpl::core::definitions::get_field_type_name; +use xrpl::utils::xrp_to_drops; + +pub fn bench_xrp_to_drops(c: &mut Criterion) { + c.bench_function("utils::xrp_to_drops", |b| { + b.iter(|| xrp_to_drops(black_box("100.000001"))) + }); +} + +pub fn bench_get_field_type_name(c: &mut Criterion) { + c.bench_function("core::definitions::definitions::get_field_type_name", |b| { + b.iter(|| get_field_type_name(black_box("HighLimit"))) + }); +} + +criterion_group!(benches, bench_xrp_to_drops, bench_get_field_type_name); +criterion_main!(benches); diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 00000000..77c5051d --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,30 @@ +//! Collection of public constants for XRPL. + +use alloc::string::String; +use alloc::string::ToString; +use strum_macros::EnumIter; + +/// Regular expression for determining ISO currency codes. +pub const ISO_CURRENCY_REGEX: &str = r"^[A-Z0-9]{3}$"; +/// Regular expression for determining hex currency codes. +pub const HEX_CURRENCY_REGEX: &str = r"^[A-F0-9]{40}$"; + +/// Length of an account id. +pub const ACCOUNT_ID_LENGTH: usize = 20; + +/// Represents the supported cryptography algorithms. +#[derive(Debug, PartialEq, Clone, EnumIter)] +pub enum CryptoAlgorithm { + ED25519, + SECP256K1, +} + +impl ToString for CryptoAlgorithm { + /// Return the String representation of an algorithm. + fn to_string(&self) -> String { + match *self { + CryptoAlgorithm::ED25519 => "ed25519".to_string(), + CryptoAlgorithm::SECP256K1 => "secp256k1".to_string(), + } + } +} diff --git a/src/core/addresscodec/exceptions.rs b/src/core/addresscodec/exceptions.rs new file mode 100644 index 00000000..e0a092a5 --- /dev/null +++ b/src/core/addresscodec/exceptions.rs @@ -0,0 +1,71 @@ +//! General XRPL Address Codec Exception. + +use crate::core::binarycodec::exceptions::XRPLBinaryCodecException; +use crate::utils::exceptions::ISOCodeException; + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum XRPLAddressCodecException { + InvalidXAddressPrefix, + InvalidXAddressZeroNoTag, + InvalidXAddressZeroRemain, + InvalidCAddressIdLength { length: usize }, + InvalidCAddressTag, + InvalidSeedPrefixEncodingType, + InvalidEncodingPrefixLength, + InvalidClassicAddressValue, + UnsupportedXAddress, + UnknownSeedEncoding, + UnexpectedPayloadLength { expected: usize, found: usize }, + Base58DecodeError(bs58::decode::Error), + HexError(hex::FromHexError), + XRPLBinaryCodecError(XRPLBinaryCodecException), + ISOError(ISOCodeException), + SerdeJsonError(serde_json::error::Category), + VecResizeError(alloc::vec::Vec), +} + +impl From for XRPLAddressCodecException { + fn from(err: ISOCodeException) -> Self { + XRPLAddressCodecException::ISOError(err) + } +} + +impl From for XRPLAddressCodecException { + fn from(err: XRPLBinaryCodecException) -> Self { + XRPLAddressCodecException::XRPLBinaryCodecError(err) + } +} + +impl From for XRPLAddressCodecException { + fn from(err: bs58::decode::Error) -> Self { + XRPLAddressCodecException::Base58DecodeError(err) + } +} + +impl From for XRPLAddressCodecException { + fn from(err: hex::FromHexError) -> Self { + XRPLAddressCodecException::HexError(err) + } +} + +impl From for XRPLAddressCodecException { + fn from(err: serde_json::Error) -> Self { + XRPLAddressCodecException::SerdeJsonError(err.classify()) + } +} + +impl From> for XRPLAddressCodecException { + fn from(err: alloc::vec::Vec) -> Self { + XRPLAddressCodecException::VecResizeError(err) + } +} + +impl core::fmt::Display for XRPLAddressCodecException { + fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + write!(f, "XRPLAddressCodecException: {:?}", self) + } +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLAddressCodecException {} diff --git a/src/core/addresscodec/mod.rs b/src/core/addresscodec/mod.rs new file mode 100644 index 00000000..8ad195fb --- /dev/null +++ b/src/core/addresscodec/mod.rs @@ -0,0 +1,739 @@ +//! This module contains commonly-used constants. +pub mod exceptions; +#[cfg(test)] +pub mod test_cases; +pub mod utils; + +use crate::constants::CryptoAlgorithm; +use crate::core::addresscodec::exceptions::XRPLAddressCodecException; +use crate::core::addresscodec::utils::*; +use crate::skip_err; +use alloc::string::String; +use alloc::vec::Vec; +use core::convert::TryInto; +use strum::IntoEnumIterator; + +/// Map the algorithm to the prefix. +fn _algorithm_to_prefix<'a>(algo: &CryptoAlgorithm) -> &'a [u8] { + match algo { + CryptoAlgorithm::ED25519 => &ED25519_SEED_PREFIX, + CryptoAlgorithm::SECP256K1 => &FAMILY_SEED_PREFIX, + } +} + +/// Returns whether a decoded X-Address is a test address. +fn _is_test_address(prefix: &[u8]) -> Result { + if ADDRESS_PREFIX_BYTES_MAIN == prefix { + Ok(false) + } else if ADDRESS_PREFIX_BYTES_TEST == prefix { + Ok(true) + } else { + Err(XRPLAddressCodecException::InvalidXAddressPrefix) + } +} + +/// Returns the destination tag extracted from the suffix +/// of the X-Address. +fn _get_tag_from_buffer(buffer: &[u8]) -> Result, XRPLAddressCodecException> { + let flag = &buffer[0]; + + if flag >= &2 { + Err(XRPLAddressCodecException::UnsupportedXAddress) + } else if flag == &1 { + // Little-endian to big-endian + Ok(Some( + buffer[1] as u64 + + buffer[2] as u64 * 0x100 + + buffer[3] as u64 * 0x10000 + + buffer[4] as u64 * 0x1000000, + )) + // inverse of what happens in encode + } else if flag != &0 { + Err(XRPLAddressCodecException::InvalidXAddressZeroNoTag) + } else if hex::decode("0000000000000000")? != buffer[1..9] { + Err(XRPLAddressCodecException::InvalidXAddressZeroRemain) + } else { + Ok(None) + } +} + +/// Returns an encoded seed. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::encode_seed; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// use xrpl::constants::CryptoAlgorithm; +/// use xrpl::core::addresscodec::utils::SEED_LENGTH; +/// +/// let entropy: [u8; SEED_LENGTH] = [ +/// 207, 45, 227, 120, 251, 221, 126, 46, 232, +/// 125, 72, 109, 251, 90, 123, 255 +/// ]; +/// let encoding_type: CryptoAlgorithm = CryptoAlgorithm::SECP256K1; +/// let seed: String = "sn259rEFXrQrWyx3Q7XneWcwV6dfL".into(); +/// +/// let encoding: Option = match encode_seed( +/// entropy, +/// encoding_type, +/// ) { +/// Ok(seed) => Some(seed), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnknownSeedEncoding => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(seed), encoding); +/// ``` +pub fn encode_seed( + entropy: [u8; SEED_LENGTH], + encoding_type: CryptoAlgorithm, +) -> Result { + encode_base58( + &entropy, + _algorithm_to_prefix(&encoding_type), + Some(SEED_LENGTH), + ) +} + +/// Returns an encoded seed. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::decode_seed; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// use xrpl::core::addresscodec::utils::SEED_LENGTH; +/// use xrpl::constants::CryptoAlgorithm; +/// extern crate alloc; +/// use alloc::vec; +/// +/// let seed: &str = "sn259rEFXrQrWyx3Q7XneWcwV6dfL"; +/// let tuple: ([u8; SEED_LENGTH], CryptoAlgorithm) = ( +/// [207, 45, 227, 120, 251, 221, 126, 46, 232, 125, 72, 109, 251, 90, 123, 255], +/// CryptoAlgorithm::SECP256K1, +/// ); +/// +/// let decoding: Option<([u8; SEED_LENGTH], CryptoAlgorithm)> = match decode_seed(seed) { +/// Ok((bytes, algorithm)) => Some((bytes, algorithm)), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnknownSeedEncoding => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(tuple), decoding); +/// ``` +pub fn decode_seed( + seed: &str, +) -> Result<([u8; SEED_LENGTH], CryptoAlgorithm), XRPLAddressCodecException> { + let mut result: Option, XRPLAddressCodecException>> = None; + let mut algo: Option = None; + + for a in CryptoAlgorithm::iter() { + let decode = decode_base58(seed, _algorithm_to_prefix(&a)); + result = Some(skip_err!(decode)); + algo = Some(a); + } + + match result { + Some(Ok(val)) => { + let decoded: [u8; SEED_LENGTH] = val.try_into()?; + Ok((decoded, algo.expect("decode_seed"))) + } + Some(Err(_)) | None => Err(XRPLAddressCodecException::UnknownSeedEncoding), + } +} + +/// Returns the X-Address representation of the data. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::classic_address_to_xaddress; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// +/// let classic_address: &str = "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59".into(); +/// let tag: Option = None; +/// let is_test_network: bool = false; +/// let xaddress: String = "X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ".into(); +/// +/// let conversion: Option = match classic_address_to_xaddress( +/// classic_address, +/// tag, +/// is_test_network +/// ) { +/// Ok(address) => Some(address), +/// Err(e) => match e { +/// XRPLAddressCodecException::InvalidXAddressPrefix => None, +/// XRPLAddressCodecException::UnsupportedXAddress => None, +/// XRPLAddressCodecException::InvalidXAddressZeroNoTag => None, +/// XRPLAddressCodecException::InvalidXAddressZeroRemain => None, +/// XRPLAddressCodecException::UnexpectedPayloadLength { +/// expected: _, +/// found: _, +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(xaddress), conversion); +/// ``` +pub fn classic_address_to_xaddress( + classic_address: &str, + tag: Option, + is_test_network: bool, +) -> Result { + let classic_address_bytes = decode_classic_address(classic_address)?; + let flag: bool = tag != None; + let tag_val: u64; + + if classic_address_bytes.len() != CLASSIC_ADDRESS_ID_LENGTH { + Err(XRPLAddressCodecException::InvalidCAddressIdLength { + length: CLASSIC_ADDRESS_ID_LENGTH, + }) + } else if tag != None && tag > Some(u32::max_value().into()) { + Err(XRPLAddressCodecException::InvalidCAddressTag) + } else { + if let Some(tval) = tag { + tag_val = tval; + } else { + tag_val = 0; + } + + let mut bytestring = match is_test_network { + true => ADDRESS_PREFIX_BYTES_TEST, + false => ADDRESS_PREFIX_BYTES_MAIN, + } + .to_vec(); + + bytestring.extend_from_slice(&classic_address_bytes); + + let encoded_tag = [ + flag as u8, + (tag_val & 0xFF) as u8, + (tag_val >> 8 & 0xFF) as u8, + (tag_val >> 16 & 0xFF) as u8, + (tag_val >> 24 & 0xFF) as u8, + 0, + 0, + 0, + 0, + ]; + + bytestring.extend_from_slice(&encoded_tag); + + Ok(bs58::encode(bytestring) + .with_alphabet(&XRPL_ALPHABET) + .with_check() + .into_string()) + } +} + +/// Returns a tuple containing the classic address, tag, +/// and whether the address is on a test network for an +/// X-Address. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::xaddress_to_classic_address; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// +/// let xaddress: &str = "X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ"; +/// let classic: (String, Option, bool) = ( +/// "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59".into(), +/// None, +/// false, +/// ); +/// +/// let conversion: Option<(String, Option, bool)> = match xaddress_to_classic_address(xaddress) { +/// Ok((address, tag, is_test_network)) => Some((address, tag, is_test_network)), +/// Err(e) => match e { +/// XRPLAddressCodecException::InvalidXAddressPrefix => None, +/// XRPLAddressCodecException::UnsupportedXAddress => None, +/// XRPLAddressCodecException::InvalidXAddressZeroNoTag => None, +/// XRPLAddressCodecException::InvalidXAddressZeroRemain => None, +/// XRPLAddressCodecException::UnexpectedPayloadLength { +/// expected: _, +/// found: _, +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(classic), conversion); +/// ``` +pub fn xaddress_to_classic_address( + xaddress: &str, +) -> Result<(String, Option, bool), XRPLAddressCodecException> { + // Convert b58 to bytes + let decoded = bs58::decode(xaddress) + .with_alphabet(&XRPL_ALPHABET) + .with_check(None) + .into_vec()?; + + let is_test_network = _is_test_address(&decoded[..2])?; + let classic_address_bytes = &decoded[2..22]; + // extracts the destination tag + let tag = _get_tag_from_buffer(&decoded[22..])?; + + let classic_address = encode_classic_address(classic_address_bytes)?; + Ok((classic_address, tag, is_test_network)) +} + +/// Returns the classic address encoding of these bytes +/// as a base58 string. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::encode_classic_address; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// +/// let bytes: &[u8] = &[ +/// 94, 123, 17, 37, 35, 246, 141, 47, 94, 135, 157, 180, +/// 234, 197, 28, 102, 152, 166, 147, 4 +/// ]; +/// let address: String = "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59".into(); +/// +/// let encoding: Option = match encode_classic_address(bytes) { +/// Ok(address) => Some(address), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnexpectedPayloadLength { +/// expected: _, +/// found: _, +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(address), encoding); +/// ``` +pub fn encode_classic_address(bytestring: &[u8]) -> Result { + encode_base58( + bytestring, + &CLASSIC_ADDRESS_PREFIX, + Some(CLASSIC_ADDRESS_LENGTH.into()), + ) +} + +/// Returns the decoded bytes of the classic address. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::decode_classic_address; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// extern crate alloc; +/// use alloc::vec; +/// +/// let key: &str = "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"; +/// let bytes: Vec = vec![ +/// 94, 123, 17, 37, 35, 246, 141, 47, 94, 135, 157, 180, +/// 234, 197, 28, 102, 152, 166, 147, 4 +/// ]; +/// +/// let decoding: Option> = match decode_classic_address(key) { +/// Ok(bytes) => Some(bytes), +/// Err(e) => match e { +/// XRPLAddressCodecException::InvalidEncodingPrefixLength => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(bytes), decoding); +/// ``` +pub fn decode_classic_address(classic_address: &str) -> Result, XRPLAddressCodecException> { + decode_base58(classic_address, &CLASSIC_ADDRESS_PREFIX) +} + +/// Returns the node public key encoding of these bytes +/// as a base58 string. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::encode_node_public_key; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// +/// let bytes: &[u8] = &[ +/// 3, 136, 229, 186, 135, 160, 0, 203, 128, 114, 64, 223, +/// 140, 132, 142, 176, 181, 255, 165, 200, 229, 165, 33, +/// 188, 142, 16, 92, 15, 10, 68, 33, 120, 40 +/// ]; +/// let key: String = "n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH".into(); +/// +/// let encoding: Option = match encode_node_public_key(bytes) { +/// Ok(key) => Some(key), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnexpectedPayloadLength { +/// expected: _, +/// found: _, +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(key), encoding); +/// ``` +pub fn encode_node_public_key(bytestring: &[u8]) -> Result { + encode_base58( + bytestring, + &NODE_PUBLIC_KEY_PREFIX, + Some(NODE_PUBLIC_KEY_LENGTH.into()), + ) +} + +/// Returns the decoded bytes of the node public key. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::decode_node_public_key; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// extern crate alloc; +/// use alloc::vec; +/// +/// let key: &str = "n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH"; +/// let bytes: Vec = vec![ +/// 3, 136, 229, 186, 135, 160, 0, 203, 128, 114, 64, 223, +/// 140, 132, 142, 176, 181, 255, 165, 200, 229, 165, 33, +/// 188, 142, 16, 92, 15, 10, 68, 33, 120, 40 +/// ]; +/// +/// let decoding: Option> = match decode_node_public_key(key) { +/// Ok(bytes) => Some(bytes), +/// Err(e) => match e { +/// XRPLAddressCodecException::InvalidEncodingPrefixLength => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(bytes), decoding); +/// ``` +pub fn decode_node_public_key(node_public_key: &str) -> Result, XRPLAddressCodecException> { + decode_base58(node_public_key, &NODE_PUBLIC_KEY_PREFIX) +} + +/// Returns the account public key encoding of these +/// bytes as a base58 string. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::encode_account_public_key; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// +/// let bytes: &[u8] = &[ +/// 2, 54, 147, 241, 89, 103, 174, 53, 125, 3, 39, 151, +/// 74, 212, 111, 227, 193, 39, 17, 59, 17, 16, 214, 4, +/// 79, 212, 30, 114, 54, 137, 248, 28, 198 +/// ]; +/// let key: String = "aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3".into(); +/// +/// let encoding: Option = match encode_account_public_key(bytes) { +/// Ok(key) => Some(key), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnexpectedPayloadLength { +/// expected: _, +/// found: _, +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(key), encoding); +/// ``` +pub fn encode_account_public_key(bytestring: &[u8]) -> Result { + encode_base58( + bytestring, + &ACCOUNT_PUBLIC_KEY_PREFIX, + Some(ACCOUNT_PUBLIC_KEY_LENGTH.into()), + ) +} + +/// Returns the decoded bytes of the node public key. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::decode_account_public_key; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// extern crate alloc; +/// use alloc::vec; +/// +/// let key: &str = "aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3"; +/// let bytes: Vec = vec![ +/// 2, 54, 147, 241, 89, 103, 174, 53, 125, 3, 39, 151, +/// 74, 212, 111, 227, 193, 39, 17, 59, 17, 16, 214, 4, +/// 79, 212, 30, 114, 54, 137, 248, 28, 198 +/// ]; +/// +/// let decoding: Option> = match decode_account_public_key(key) { +/// Ok(bytes) => Some(bytes), +/// Err(e) => match e { +/// XRPLAddressCodecException::InvalidEncodingPrefixLength => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(bytes), decoding); +/// ``` +pub fn decode_account_public_key( + account_public_key: &str, +) -> Result, XRPLAddressCodecException> { + decode_base58(account_public_key, &ACCOUNT_PUBLIC_KEY_PREFIX) +} + +/// Returns whether `classic_address` is a valid classic address. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::is_valid_classic_address; +/// +/// let address: &str = "rpGaCyHRYbgKhErgFih3RdjJqXDsYBouz3"; +/// +/// assert!(is_valid_classic_address(address)); +/// ``` +pub fn is_valid_classic_address(classic_address: &str) -> bool { + decode_base58(classic_address, &CLASSIC_ADDRESS_PREFIX).is_ok() +} + +/// Returns whether ``xaddress`` is a valid X-Address. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::is_valid_xaddress; +/// +/// let address: &str = "X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ"; +/// +/// assert!(is_valid_xaddress(address)); +/// ``` +pub fn is_valid_xaddress(xaddress: &str) -> bool { + xaddress_to_classic_address(xaddress).is_ok() +} + +#[cfg(test)] +mod test { + use super::*; + use crate::alloc::string::ToString; + use crate::core::addresscodec::test_cases::*; + + #[test] + fn test_algorithm_to_prefix() { + assert_eq!( + &FAMILY_SEED_PREFIX, + _algorithm_to_prefix(&CryptoAlgorithm::SECP256K1) + ); + assert_eq!( + &ED25519_SEED_PREFIX, + _algorithm_to_prefix(&CryptoAlgorithm::ED25519) + ); + } + + #[test] + fn test_encode_seed() { + let bytes: [u8; 16] = [ + 207, 45, 227, 120, 251, 221, 126, 46, 232, 125, 72, 109, 251, 90, 123, 255, + ]; + + assert_eq!( + Ok(SECP256K1_ENCODED_SEED_TEST.to_string()), + encode_seed(bytes, CryptoAlgorithm::SECP256K1) + ); + + let bytes: [u8; 16] = [ + 76, 58, 29, 33, 63, 189, 251, 20, 199, 194, 141, 96, 148, 105, 179, 65, + ]; + + assert_eq!( + Ok(ED25519_ENCODED_SEED_TEST.to_string()), + encode_seed(bytes, CryptoAlgorithm::ED25519) + ); + } + + #[test] + fn test_decode_seed() { + let hex_bytes = hex::decode(SECP256K1_HEX_TEST).expect(""); + let (decode_result, encoding_type) = decode_seed(SECP256K1_ENCODED_SEED_TEST).unwrap(); + + assert_eq!(hex_bytes, decode_result); + assert_eq!(CryptoAlgorithm::SECP256K1, encoding_type); + + let hex_bytes = hex::decode(ED25519_HEX_TEST).expect(""); + let (decode_result, encoding_type) = decode_seed(ED25519_ENCODED_SEED_TEST).unwrap(); + + assert_eq!(hex_bytes, decode_result); + assert_eq!(CryptoAlgorithm::ED25519, encoding_type); + } + + #[test] + fn test_classic_address_to_xaddress() { + for case in ADDRESS_TEST_CASES { + assert_eq!( + classic_address_to_xaddress(case.address, case.tag, true), + Ok(case.test_xaddress.to_string()), + ); + + assert_eq!( + classic_address_to_xaddress(case.address, case.tag, false), + Ok(case.main_xaddress.to_string()), + ); + } + } + + #[test] + fn test_xaddress_to_classic_address() { + for case in ADDRESS_TEST_CASES { + let (classic_address, tag, is_test) = + xaddress_to_classic_address(case.test_xaddress).unwrap(); + + assert_eq!(*case.address, classic_address); + assert_eq!(case.tag, tag); + assert!(is_test); + + let (classic_address, tag, is_test) = + xaddress_to_classic_address(case.main_xaddress).unwrap(); + + assert_eq!(*case.address, classic_address); + assert_eq!(case.tag, tag); + assert!(!is_test); + } + } + + #[test] + fn test_encode_node_public_key() { + let bytes = hex::decode(NODE_PUBLIC_KEY_HEX_TEST).expect(""); + assert_eq!( + encode_node_public_key(&bytes), + Ok(NODE_PUBLIC_KEY_TEST.to_string()), + ); + } + + #[test] + fn test_decode_node_public_key() { + assert_eq!( + decode_node_public_key(NODE_PUBLIC_KEY_TEST), + Ok(hex::decode(NODE_PUBLIC_KEY_HEX_TEST).expect("")), + ); + } + + #[test] + fn test_encode_account_public_key() { + assert_eq!( + encode_account_public_key(&hex::decode(ACCOUNT_PUBLIC_KEY_HEX_TEST).expect("")), + Ok(ACCOUNT_PUBLIC_KEY_TEST.to_string()), + ); + } + + #[test] + fn test_decode_account_public_key() { + assert_eq!( + decode_account_public_key(ACCOUNT_PUBLIC_KEY_TEST), + Ok(hex::decode(ACCOUNT_PUBLIC_KEY_HEX_TEST).expect("")), + ); + } + + #[test] + fn test_is_valid_classic_address() { + for case in ADDRESS_TEST_CASES { + assert!(is_valid_classic_address(case.address)) + } + } + + #[test] + fn test_is_valid_xaddress() { + for case in ADDRESS_TEST_CASES { + assert!(is_valid_xaddress(case.test_xaddress)) + } + } + + #[test] + fn accept_seed_encode_decode_secp256k1_low() { + let encoded_string = "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"; + let bytes: [u8; 16] = Default::default(); + let (decode_result, encoding_type) = decode_seed(encoded_string).unwrap(); + + assert_eq!( + encode_seed(bytes, CryptoAlgorithm::SECP256K1), + Ok(encoded_string.to_string()), + ); + + assert_eq!(decode_result, bytes); + assert_eq!(encoding_type, CryptoAlgorithm::SECP256K1); + } + + #[test] + fn accept_seed_encode_decode_secp256k1_high() { + let bytes: [u8; 16] = [255; 16]; + let encoded_string = "saGwBRReqUNKuWNLpUAq8i8NkXEPN"; + let (decode_result, encoding_type) = decode_seed(encoded_string).unwrap(); + + assert_eq!( + encode_seed(bytes, CryptoAlgorithm::SECP256K1), + Ok(encoded_string.to_string()), + ); + + assert_eq!(decode_result, bytes); + assert_eq!(encoding_type, CryptoAlgorithm::SECP256K1); + } + + #[test] + fn accept_seed_encode_decode_ed25519_low() { + let encoded_string = "sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE"; + let bytes: [u8; 16] = Default::default(); + let (decode_result, encoding_type) = decode_seed(encoded_string).unwrap(); + + assert_eq!( + encode_seed(bytes, CryptoAlgorithm::ED25519), + Ok(encoded_string.to_string()), + ); + + assert_eq!(decode_result, bytes); + assert_eq!(encoding_type, CryptoAlgorithm::ED25519); + } + + #[test] + fn accept_seed_encode_decode_ed25519_high() { + let bytes: [u8; 16] = [255; 16]; + let encoded_string = "sEdV19BLfeQeKdEXyYA4NhjPJe6XBfG"; + let (decode_result, encoding_type) = decode_seed(encoded_string).unwrap(); + + assert_eq!( + encode_seed(bytes, CryptoAlgorithm::ED25519), + Ok(encoded_string.to_string()), + ); + + assert_eq!(decode_result, bytes); + assert_eq!(encoding_type, CryptoAlgorithm::ED25519); + } +} diff --git a/src/core/addresscodec/test_cases.rs b/src/core/addresscodec/test_cases.rs new file mode 100644 index 00000000..0c53f533 --- /dev/null +++ b/src/core/addresscodec/test_cases.rs @@ -0,0 +1,153 @@ +pub struct TestCase<'a> { + pub address: &'a str, + pub tag: Option, + pub main_xaddress: &'a str, + pub test_xaddress: &'a str, +} + +pub const ADDRESS_TEST_CASES: [TestCase; 22] = [ + TestCase { + address: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + tag: None, + main_xaddress: "X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ", + test_xaddress: "T719a5UwUCnEs54UsxG9CJYYDhwmFCqkr7wxCcNcfZ6p5GZ", + }, + TestCase { + address: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + tag: Some(1), + main_xaddress: "X7AcgcsBL6XDcUb289X4mJ8djcdyKaGZMhc9YTE92ehJ2Fu", + test_xaddress: "T719a5UwUCnEs54UsxG9CJYYDhwmFCvbJNZbi37gBGkRkbE", + }, + TestCase { + address: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + tag: Some(14), + main_xaddress: "X7AcgcsBL6XDcUb289X4mJ8djcdyKaGo2K5VpXpmCqbV2gS", + test_xaddress: "T719a5UwUCnEs54UsxG9CJYYDhwmFCvqXVCALUGJGSbNV3x", + }, + TestCase { + address: "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + tag: Some(11747), + main_xaddress: "X7AcgcsBL6XDcUb289X4mJ8djcdyKaLFuhLRuNXPrDeJd9A", + test_xaddress: "T719a5UwUCnEs54UsxG9CJYYDhwmFCziiNHtUukubF2Mg6t", + }, + TestCase { + address: "rLczgQHxPhWtjkaQqn3Q6UM8AbRbbRvs5K", + tag: None, + main_xaddress: "XVZVpQj8YSVpNyiwXYSqvQoQqgBttTxAZwMcuJd4xteQHyt", + test_xaddress: "TVVrSWtmQQssgVcmoMBcFQZKKf56QscyWLKnUyiuZW8ALU4", + }, + TestCase { + address: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", + tag: None, + main_xaddress: "X7YenJqxv3L66CwhBSfd3N8RzGXxYqPopMGMsCcpho79rex", + test_xaddress: "T77wVQzA8ntj9wvCTNiQpNYLT5hmhRsFyXDoMLqYC4BzQtV", + }, + TestCase { + address: "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", + tag: Some(58), + main_xaddress: "X7YenJqxv3L66CwhBSfd3N8RzGXxYqV56ZkTCa9UCzgaao1", + test_xaddress: "T77wVQzA8ntj9wvCTNiQpNYLT5hmhR9kej6uxm4jGcQD7rZ", + }, + TestCase { + address: "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", + tag: Some(23480), + main_xaddress: "X7d3eHCXzwBeWrZec1yT24iZerQjYL8m8zCJ16ACxu1BrBY", + test_xaddress: "T7YChPFWifjCAXLEtg5N74c7fSAYsvSokwcmBPBUZWhxH5P", + }, + TestCase { + address: "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", + tag: Some(11747), + main_xaddress: "X7d3eHCXzwBeWrZec1yT24iZerQjYLo2CJf8oVC5CMWey5m", + test_xaddress: "T7YChPFWifjCAXLEtg5N74c7fSAYsvTcc7nEfwuEEvn5Q4w", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: None, + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQn49b3qD26PK7FcGSKE", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(0), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtV8AqEL4xcZj5whKbmc", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnSy8RHqGHoGJ59spi2", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(1), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtV8xvjGQTYPiAx6gwDC", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnSz1uDimDdPYXzSpyw", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(2), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtV8zpDURx7DzBCkrQE7", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnTryP9tG9TW8GeMBmd", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(32), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtVoYiC9UvKfjKar4LJe", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnT2oqaCDzMEuCDAj1j", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(276), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtVoKj3MnFGMXEFMnvJV", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnTMgJJYfAbsiPsc6Zg", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(65591), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtVozpjdhPQVdt3ghaWw", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQn7ryu2W6njw7mT1jmS", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(16781933), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtVqrDUk2vDpkTjPsY73", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnVsw45sDtGHhLi27Qa", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(4294967294), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnX8tDFQ53itLNqs6vU", + }, + TestCase { + address: "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf", + tag: Some(4294967295), + main_xaddress: "XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi", + test_xaddress: "TVE26TYGhfLC7tQDno7G8dGtxSkYQnXoy6kSDh6rZzApc69", + }, + TestCase { + address: "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY", + tag: None, + main_xaddress: "XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2gYsjNFQLKYW33DzBm", + test_xaddress: "TVd2rqMkYL2AyS97NdELcpeiprNBjwLZzuUG5rZnaewsahi", + }, + TestCase { + address: "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY", + tag: Some(0), + main_xaddress: "XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2m4Er6SnvjVLpMWPjR", + test_xaddress: "TVd2rqMkYL2AyS97NdELcpeiprNBjwRQUBetPbyrvXSTuxU", + }, + TestCase { + address: "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY", + tag: Some(13371337), + main_xaddress: "XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2qwGkhgc48zzcx6Gkr", + test_xaddress: "TVd2rqMkYL2AyS97NdELcpeiprNBjwVUDvp3vhpXbNhLwJi", + }, +]; + +pub const SECP256K1_ENCODED_SEED_TEST: &str = "sn259rEFXrQrWyx3Q7XneWcwV6dfL"; +pub const SECP256K1_HEX_TEST: &str = "CF2DE378FBDD7E2EE87D486DFB5A7BFF"; +pub const ED25519_ENCODED_SEED_TEST: &str = "sEdTM1uX8pu2do5XvTnutH6HsouMaM2"; +pub const ED25519_HEX_TEST: &str = "4C3A1D213FBDFB14C7C28D609469B341"; + +pub const NODE_PUBLIC_KEY_TEST: &str = "n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH"; +pub const NODE_PUBLIC_KEY_HEX_TEST: &str = + "0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828"; +pub const ACCOUNT_PUBLIC_KEY_TEST: &str = "aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3"; +pub const ACCOUNT_PUBLIC_KEY_HEX_TEST: &str = + "023693F15967AE357D0327974AD46FE3C127113B1110D6044FD41E723689F81CC6"; diff --git a/src/core/addresscodec/utils.rs b/src/core/addresscodec/utils.rs new file mode 100644 index 00000000..6ed2620a --- /dev/null +++ b/src/core/addresscodec/utils.rs @@ -0,0 +1,184 @@ +//! This module contains commonly-used utilities. + +use crate::core::addresscodec::exceptions::XRPLAddressCodecException; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use bs58::Alphabet; + +/// value is 33; Seed value (for secret keys) (16 bytes) +pub(crate) const FAMILY_SEED_PREFIX: [u8; 1] = [0x21]; +/// [1, 225, 75] +pub(crate) const ED25519_SEED_PREFIX: [u8; 3] = [0x01, 0xE1, 0x4B]; + +/// Bytes for the prefix of a mainnet address. +pub(crate) const ADDRESS_PREFIX_BYTES_MAIN: [u8; 2] = [0x05, 0x44]; +/// Bytes for the prefix of a testnet address. +pub(crate) const ADDRESS_PREFIX_BYTES_TEST: [u8; 2] = [0x04, 0x93]; + +/// Length of a classic address. +pub(crate) const CLASSIC_ADDRESS_ID_LENGTH: usize = 20; +/// Classic address length +pub(crate) const CLASSIC_ADDRESS_LENGTH: u8 = 20; +/// base58 encodings: https://xrpl.org/base58-encodings.html +/// Account address (20 bytes) +pub(crate) const CLASSIC_ADDRESS_PREFIX: [u8; 1] = [0x0]; + +/// value is 28; Validation public key (33 bytes) +pub(crate) const NODE_PUBLIC_KEY_PREFIX: [u8; 1] = [0x1C]; +/// Node public key length. +pub(crate) const NODE_PUBLIC_KEY_LENGTH: u8 = 33; +/// Value is 35; Account public key (33 bytes) +pub(crate) const ACCOUNT_PUBLIC_KEY_PREFIX: [u8; 1] = [0x23]; +/// Account public key length +pub(crate) const ACCOUNT_PUBLIC_KEY_LENGTH: u8 = 33; + +/// The dictionary used for XRPL base58 encodings +/// Sourced from the [`bs58`] crate. +/// +/// [`bs58`]: bs58::Alphabet +pub const XRPL_ALPHABET: Alphabet = *bs58::Alphabet::RIPPLE; + +/// Lenght of a seed value. +pub const SEED_LENGTH: usize = 16; + +/// Returns the byte decoding of the base58-encoded string. +/// +/// See [`bs58::encode`] +/// +/// [`bs58::encode`]: mod@bs58::encode() +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::utils::decode_base58; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// extern crate alloc; +/// use alloc::vec; +/// +/// let encoded: &str = "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"; +/// let prefix: &[u8] = &[0x0]; +/// let decoded: Vec = vec![ +/// 94, 123, 17, 37, 35, 246, 141, 47, 94, 135, 157, 180, +/// 234, 197, 28, 102, 152, 166, 147, 4, +/// ]; +/// +/// let result: Option> = match decode_base58( +/// encoded, +/// prefix +/// ) { +/// Ok(val) => Some(val), +/// Err(e) => match e { +/// XRPLAddressCodecException::InvalidEncodingPrefixLength => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(decoded), result); +/// ``` +pub fn decode_base58( + b58_string: &str, + prefix: &[u8], +) -> Result, XRPLAddressCodecException> { + let prefix_len = prefix.len(); + let decoded = bs58::decode(b58_string) + .with_alphabet(&XRPL_ALPHABET) + .with_check(None) + .into_vec()?; + + if &decoded[..prefix_len] != prefix { + Err(XRPLAddressCodecException::InvalidEncodingPrefixLength) + } else { + Ok(decoded[prefix_len..].to_vec()) + } +} + +/// Returns the base58 encoding of the bytestring, with the +/// given data prefix (which indicates type) and while +/// ensuring the bytestring is the expected length. +/// +/// See [`bs58::encode`] +/// +/// [`bs58::encode`]: mod@bs58::encode() +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::addresscodec::utils::encode_base58; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// +/// let decoded: &[u8] = &[ +/// 94, 123, 17, 37, 35, 246, 141, 47, 94, 135, 157, 180, +/// 234, 197, 28, 102, 152, 166, 147, 4, +/// ]; +/// let prefix: &[u8] = &[0x0]; +/// let expected_length: Option = Some(20); +/// let encoded: String = "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59".to_string(); +/// +/// let result: Option = match encode_base58( +/// decoded, +/// prefix, +/// expected_length +/// ) { +/// Ok(val) => Some(val), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnexpectedPayloadLength { +/// expected: _, +/// found: _, +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(encoded), result); +/// ``` +pub fn encode_base58( + bytestring: &[u8], + prefix: &[u8], + expected_length: Option, +) -> Result { + if expected_length != Some(bytestring.len()) { + Err(XRPLAddressCodecException::UnexpectedPayloadLength { + expected: expected_length.expect(""), + found: bytestring.len(), + }) + } else { + let mut payload = vec![]; + + payload.extend_from_slice(prefix); + payload.extend_from_slice(bytestring); + + Ok(bs58::encode(payload) + .with_alphabet(&XRPL_ALPHABET) + .with_check() + .into_string()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::alloc::string::ToString; + + const ENCODED: &str = "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"; + const DECODED: &[u8] = &[ + 94, 123, 17, 37, 35, 246, 141, 47, 94, 135, 157, 180, 234, 197, 28, 102, 152, 166, 147, 4, + ]; + + #[test] + fn test_decode_base58() { + assert_eq!(decode_base58(ENCODED, &[0x0]), Ok(DECODED.to_vec())); + } + + #[test] + fn test_encode_base58() { + assert_eq!( + encode_base58(DECODED, &[0x0], Some(20)), + Ok(ENCODED.to_string()) + ); + } +} diff --git a/src/core/binarycodec/exceptions.rs b/src/core/binarycodec/exceptions.rs new file mode 100644 index 00000000..fb7595e6 --- /dev/null +++ b/src/core/binarycodec/exceptions.rs @@ -0,0 +1,90 @@ +//! General XRPL Binary Codec Exceptions. + +use crate::utils::exceptions::ISOCodeException; +use crate::utils::exceptions::XRPRangeException; + +#[derive(Debug, Clone, PartialEq)] +pub enum XRPLBinaryCodecException { + UnexpectedParserSkipOverflow { max: usize, found: usize }, + UnexpectedLengthPrefixRange { min: usize, max: usize }, + UnexpectedTypeCodeRange { min: usize, max: usize }, + UnexpectedFieldCodeRange { min: usize, max: usize }, + UnexpectedFieldIdByteRange { min: usize, max: usize }, + UnknownFieldName, + InvalidReadFromBytesValue, + InvalidVariableLengthTooLarge { max: usize }, + InvalidHashLength { expected: usize, found: usize }, + InvalidPathSetFromValue, + TryFromSliceError, + TryFromIntError, + FromUtf8Error, + ParseIntError, + FromHexError, + XRPRangeError(XRPRangeException), + SerdeJsonError(serde_json::error::Category), + DecimalError(rust_decimal::Error), + ISOCodeError(ISOCodeException), +} + +impl From for XRPLBinaryCodecException { + fn from(err: XRPRangeException) -> Self { + XRPLBinaryCodecException::XRPRangeError(err) + } +} + +impl From for XRPLBinaryCodecException { + fn from(err: ISOCodeException) -> Self { + XRPLBinaryCodecException::ISOCodeError(err) + } +} + +impl From for XRPLBinaryCodecException { + fn from(err: rust_decimal::Error) -> Self { + XRPLBinaryCodecException::DecimalError(err) + } +} + +impl From for XRPLBinaryCodecException { + fn from(_: hex::FromHexError) -> Self { + XRPLBinaryCodecException::FromHexError + } +} + +impl From for XRPLBinaryCodecException { + fn from(err: serde_json::Error) -> Self { + XRPLBinaryCodecException::SerdeJsonError(err.classify()) + } +} + +impl From for XRPLBinaryCodecException { + fn from(_: core::num::TryFromIntError) -> Self { + XRPLBinaryCodecException::TryFromIntError + } +} + +impl From for XRPLBinaryCodecException { + fn from(_: core::array::TryFromSliceError) -> Self { + XRPLBinaryCodecException::TryFromSliceError + } +} + +impl From for XRPLBinaryCodecException { + fn from(_: core::num::ParseIntError) -> Self { + XRPLBinaryCodecException::ParseIntError + } +} + +impl From for XRPLBinaryCodecException { + fn from(_: alloc::string::FromUtf8Error) -> Self { + XRPLBinaryCodecException::FromUtf8Error + } +} + +impl core::fmt::Display for XRPLBinaryCodecException { + fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + write!(f, "XRPLBinaryCodecException: {:?}", self) + } +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLBinaryCodecException {} diff --git a/src/core/binarycodec/mod.rs b/src/core/binarycodec/mod.rs new file mode 100644 index 00000000..ff4c02d3 --- /dev/null +++ b/src/core/binarycodec/mod.rs @@ -0,0 +1,802 @@ +//! Functions for encoding objects into the XRP Ledger's +//! canonical binary format and decoding them. +pub mod exceptions; +pub(crate) mod test_cases; +pub mod utils; + +use crate::core::binarycodec::exceptions::XRPLBinaryCodecException; +use crate::core::binarycodec::utils::*; +use crate::core::definitions::*; +use crate::core::types::TryFromParser; +use crate::utils::ToBytes; +use alloc::borrow::ToOwned; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::convert::TryInto; + +/// Serializes JSON to XRPL binary format. +pub type BinarySerializer = Vec; + +/// Deserializes from hex-encoded XRPL binary format to +/// serde JSON fields and values. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::binarycodec::BinaryParser; +/// use xrpl::core::Parser; +/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; +/// +/// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; +/// let binary_parser: BinaryParser = BinaryParser::from(test_bytes); +/// +/// assert_eq!(binary_parser, test_bytes[..]); +/// ``` +#[derive(Debug, Clone)] +pub struct BinaryParser(Vec); + +/// Helper function for length-prefixed fields including +/// Blob types and some AccountID types. Calculates the +/// prefix of variable length bytes. +/// +/// 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 +/// +/// See Length Prefixing: +/// `` +fn _encode_variable_length_prefix(length: &usize) -> Result, XRPLBinaryCodecException> { + if length <= &MAX_SINGLE_BYTE_LENGTH { + Ok([*length as u8].to_vec()) + } else if length < &MAX_DOUBLE_BYTE_LENGTH { + let mut bytes = vec![]; + let b_length = *length - (MAX_SINGLE_BYTE_LENGTH + 1); + let val_a: u8 = ((b_length >> 8) + (MAX_SINGLE_BYTE_LENGTH + 1)).try_into()?; + let val_b: u8 = (b_length & 0xFF).try_into()?; + + bytes.extend_from_slice(&[val_a]); + bytes.extend_from_slice(&[val_b]); + + Ok(bytes) + } else if length <= &MAX_LENGTH_VALUE { + let mut bytes = vec![]; + let b_length = *length - MAX_DOUBLE_BYTE_LENGTH; + let val_a: u8 = ((MAX_SECOND_BYTE_VALUE + 1) + (b_length >> 16)).try_into()?; + let val_b: u8 = ((b_length >> 8) & 0xFF).try_into()?; + let val_c: u8 = (b_length & 0xFF).try_into()?; + + bytes.extend_from_slice(&[val_a]); + bytes.extend_from_slice(&[val_b]); + bytes.extend_from_slice(&[val_c]); + + Ok(bytes) + } else { + Err(XRPLBinaryCodecException::InvalidVariableLengthTooLarge { + max: MAX_LENGTH_VALUE, + }) + } +} + +pub trait Parser { + /// Peek the first byte of the BinaryParser. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// + /// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; + /// let binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// let first_byte: Option<[u8; 1]> = binary_parser.peek(); + /// + /// assert_eq!(Some([test_bytes[0]; 1]), first_byte); + /// ``` + fn peek(&self) -> Option<[u8; 1]>; + + /// Consume the first n bytes of the BinaryParser. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// + /// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; + /// let mut binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// + /// match binary_parser.skip_bytes(4) { + /// Ok(parser) => assert_eq!(*parser, test_bytes[4..]), + /// Err(e) => match e { + /// XRPLBinaryCodecException::UnexpectedParserSkipOverflow { + /// max: _, + /// found: _, + /// } => assert!(false), + /// _ => assert!(false) + /// } + /// } + /// ``` + fn skip_bytes(&mut self, n: usize) -> Result<&Self, XRPLBinaryCodecException>; + + /// Consume and return the first n bytes of the BinaryParser. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// + /// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; + /// let mut binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// + /// match binary_parser.read(5) { + /// Ok(data) => assert_eq!(test_bytes[..5], data), + /// Err(e) => match e { + /// XRPLBinaryCodecException::UnexpectedParserSkipOverflow { + /// max: _, + /// found: _, + /// } => assert!(false), + /// _ => assert!(false) + /// } + /// } + /// ``` + fn read(&mut self, n: usize) -> Result, XRPLBinaryCodecException>; + + /// Read 1 byte from parser and return as unsigned int. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// + /// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; + /// let mut binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// + /// match binary_parser.read_uint8() { + /// Ok(data) => assert_eq!(0, data), + /// Err(e) => match e { + /// XRPLBinaryCodecException::UnexpectedParserSkipOverflow { + /// max: _, + /// found: _, + /// } => assert!(false), + /// _ => assert!(false) + /// } + /// } + /// ``` + fn read_uint8(&mut self) -> Result; + + /// Read 2 bytes from parser and return as unsigned int. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// + /// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; + /// let mut binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// + /// match binary_parser.read_uint16() { + /// Ok(data) => assert_eq!(17, data), + /// Err(e) => match e { + /// XRPLBinaryCodecException::UnexpectedParserSkipOverflow { + /// max: _, + /// found: _, + /// } => assert!(false), + /// _ => assert!(false) + /// } + /// } + /// ``` + fn read_uint16(&mut self) -> Result; + + /// Read 4 bytes from parser and return as unsigned int. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// + /// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; + /// let mut binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// + /// match binary_parser.read_uint32() { + /// Ok(data) => assert_eq!(1122867, data), + /// Err(e) => match e { + /// XRPLBinaryCodecException::UnexpectedParserSkipOverflow { + /// max: _, + /// found: _, + /// } => assert!(false), + /// _ => assert!(false) + /// } + /// } + /// ``` + fn read_uint32(&mut self) -> Result; + + /// Returns whether the binary parser has finished + /// parsing (e.g. there is nothing left in the buffer + /// that needs to be processed). + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// extern crate alloc; + /// use alloc::vec; + /// + /// let empty: &[u8] = &[]; + /// let mut buffer: Vec = vec![]; + /// let test_bytes: &[u8] = &[0, 17, 34, 51, 68, 85, 102]; + /// let mut binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// + /// while !binary_parser.is_end(None) { + /// match binary_parser.read(1) { + /// Ok(data) => buffer.extend_from_slice(&data), + /// Err(e) => match e { + /// XRPLBinaryCodecException::UnexpectedParserSkipOverflow { + /// max: _, + /// found: _, + /// } => assert!(false), + /// _ => assert!(false) + /// } + /// } + /// } + /// + /// assert_eq!(test_bytes, &buffer[..]); + /// // The BinaryParser is emptied as it is read. + /// assert_eq!(binary_parser, empty[..]); + /// + /// ``` + fn is_end(&self, custom_end: Option) -> bool; + + /// Reads a variable length encoding prefix and returns + /// the encoded length. The formula for decoding a length + /// prefix is described in Length Prefixing. + /// + /// See Length Prefixing: + /// `` + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::Parser; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// + /// let test_bytes: &[u8] = &[6, 17, 34, 51, 68, 85, 102]; + /// let mut binary_parser: BinaryParser = BinaryParser::from(test_bytes); + /// + /// match binary_parser.read_length_prefix() { + /// Ok(data) => assert_eq!(6, data), + /// Err(e) => match e { + /// XRPLBinaryCodecException::UnexpectedLengthPrefixRange { + /// min: _, max: _ + /// } => assert!(false), + /// _ => assert!(false) + /// } + /// } + fn read_length_prefix(&mut self) -> Result; + + /// Reads field ID from BinaryParser and returns as + /// a FieldHeader object. + fn read_field_header(&mut self) -> Result; + + /// Read the field ordinal at the head of the + /// BinaryParser and return a FieldInstance object + /// representing information about the field + /// containedin the following bytes. + fn read_field(&mut self) -> Result; + + /// Read next bytes from BinaryParser as the given type. + fn read_type(&mut self) -> Result; + + /// Read value of the type specified by field from + /// the BinaryParser. + fn read_field_value(&mut self, field: &FieldInstance) -> Result + where + T::Error: From; +} + +pub trait Serialization { + /// Write given bytes to this BinarySerializer. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinarySerializer; + /// use xrpl::core::binarycodec::Serialization; + /// + /// let mut test_bytes: Vec = [0, 17, 34, 51, 68, 85, 102].to_vec(); + /// let mut serializer: BinarySerializer = BinarySerializer::new(); + /// + /// serializer.append(&mut test_bytes.to_owned()); + /// assert_eq!(test_bytes, serializer); + /// ``` + fn append(&mut self, bytes: &[u8]) -> &Self; + + /// Write a variable length encoded value to + /// the BinarySerializer. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinarySerializer; + /// use xrpl::core::binarycodec::Serialization; + /// + /// let expected: Vec = [3, 0, 17, 34].to_vec(); + /// let mut test_bytes: Vec = [0, 17, 34].to_vec(); + /// let mut serializer: BinarySerializer = BinarySerializer::new(); + /// + /// serializer.write_length_encoded(&mut test_bytes); + /// assert_eq!(expected, serializer); + /// ``` + fn write_length_encoded(&mut self, value: &[u8]) -> &Self; + + /// Write field and value to the buffer. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::binarycodec::BinarySerializer; + /// use xrpl::core::binarycodec::Serialization; + /// use xrpl::core::definitions::FieldInstance; + /// use xrpl::core::definitions::FieldInfo; + /// use xrpl::core::definitions::FieldHeader; + /// + /// let field_header: FieldHeader = FieldHeader { + /// type_code: -2, + /// field_code: 0, + /// }; + /// + /// let field_info: FieldInfo = FieldInfo { + /// nth: 0, + /// is_vl_encoded: false, + /// is_serialized: false, + /// is_signing_field: false, + /// r#type: "Unknown".to_string(), + /// }; + /// + /// let field_instance = FieldInstance::new(&field_info, "Generic", field_header); + /// let expected: Vec = [255, 224, 0, 17, 34].to_vec(); + /// let test_bytes: Vec = [0, 17, 34].to_vec(); + /// let mut serializer: BinarySerializer = BinarySerializer::new(); + /// + /// serializer.write_field_and_value(field_instance, &test_bytes); + /// assert_eq!(expected, serializer); + /// ``` + fn write_field_and_value(&mut self, field: FieldInstance, value: &[u8]) -> &Self; +} + +impl Serialization for BinarySerializer { + fn append(&mut self, bytes: &[u8]) -> &Self { + self.extend_from_slice(bytes); + self + } + + fn write_length_encoded(&mut self, value: &[u8]) -> &Self { + let length_prefix = _encode_variable_length_prefix(&value.len()); + + // TODO Handle unwrap better + self.extend_from_slice(&length_prefix.unwrap()); + self.extend_from_slice(value); + + self + } + + fn write_field_and_value(&mut self, field: FieldInstance, value: &[u8]) -> &Self { + self.extend_from_slice(&field.header.to_bytes()); + + if field.is_vl_encoded { + self.write_length_encoded(value); + } else { + self.extend_from_slice(value); + } + + self + } +} + +/// Peek the first byte of the BinaryParser. +impl Parser for BinaryParser { + fn peek(&self) -> Option<[u8; 1]> { + if !self.0.is_empty() { + Some(self.0[0].to_be_bytes()) + } else { + None + } + } + + fn skip_bytes(&mut self, n: usize) -> Result<&Self, XRPLBinaryCodecException> { + if n > self.0.len() { + Err(XRPLBinaryCodecException::UnexpectedParserSkipOverflow { + max: self.0.len(), + found: n, + }) + } else { + self.0 = self.0[n..].to_vec(); + Ok(self) + } + } + + fn read(&mut self, n: usize) -> Result, XRPLBinaryCodecException> { + let first_n_bytes = self.0[..n].to_owned(); + + self.skip_bytes(n)?; + Ok(first_n_bytes) + } + + fn read_uint8(&mut self) -> Result { + let result = self.read(1)?; + Ok(u8::from_be_bytes(result.try_into().or(Err( + XRPLBinaryCodecException::InvalidReadFromBytesValue, + ))?)) + } + + fn read_uint16(&mut self) -> Result { + let result = self.read(2)?; + Ok(u16::from_be_bytes(result.try_into().or(Err( + XRPLBinaryCodecException::InvalidReadFromBytesValue, + ))?)) + } + + fn read_uint32(&mut self) -> Result { + let result = self.read(4)?; + Ok(u32::from_be_bytes(result.try_into().or(Err( + XRPLBinaryCodecException::InvalidReadFromBytesValue, + ))?)) + } + + fn is_end(&self, custom_end: Option) -> bool { + if let Some(end) = custom_end { + self.0.len() <= end + } else { + self.0.is_empty() + } + } + + fn read_length_prefix(&mut self) -> Result { + let byte1: usize = self.read_uint8()? as usize; + + match byte1 { + // If the field contains 0 to 192 bytes of data, + // the first byte defines the length of the contents. + x if x <= MAX_SINGLE_BYTE_LENGTH => Ok(byte1), + // If the field contains 193 to 12480 bytes of data, + // the first two bytes indicate the length of the + // field with the following formula: + // 193 + ((byte1 - 193) * 256) + byte2 + x if x <= MAX_SECOND_BYTE_VALUE => { + let byte2: usize = self.read_uint8()? as usize; + Ok((MAX_SINGLE_BYTE_LENGTH + 1) + + ((byte1 - (MAX_SINGLE_BYTE_LENGTH + 1)) * MAX_BYTE_VALUE) + + byte2) + } + // If the field contains 12481 to 918744 bytes of data, + // the first three bytes indicate the length of the + // field with the following formula: + // 12481 + ((byte1 - 241) * 65536) + (byte2 * 256) + byte3 + x if x <= 254 => { + let byte2: usize = self.read_uint8()? as usize; + let byte3: usize = self.read_uint8()? as usize; + + Ok(MAX_DOUBLE_BYTE_LENGTH + + ((byte1 - (MAX_SECOND_BYTE_VALUE + 1)) * MAX_DOUBLE_BYTE_VALUE) + + (byte2 * MAX_BYTE_VALUE) + + byte3) + } + _ => Err(XRPLBinaryCodecException::UnexpectedLengthPrefixRange { min: 1, max: 3 }), + } + } + + fn read_field_header(&mut self) -> Result { + let mut type_code: i16 = self.read_uint8()? as i16; + let mut field_code: i16 = type_code & 15; + + type_code >>= 4; + + if type_code == 0 { + type_code = self.read_uint8()? as i16; + + if type_code == 0 || type_code < 16 { + return Err(XRPLBinaryCodecException::UnexpectedTypeCodeRange { min: 1, max: 16 }); + }; + }; + + if field_code == 0 { + field_code = self.read_uint8()? as i16; + + if field_code == 0 || field_code < 16 { + return Err(XRPLBinaryCodecException::UnexpectedFieldCodeRange { min: 1, max: 16 }); + }; + }; + + Ok(FieldHeader { + type_code, + field_code, + }) + } + + fn read_field(&mut self) -> Result { + let field_header = self.read_field_header()?; + let field_name = get_field_name_from_header(&field_header); + + if let Some(name) = field_name { + if let Some(instance) = get_field_instance(name) { + return Ok(instance); + }; + }; + + Err(XRPLBinaryCodecException::UnknownFieldName) + } + + fn read_type(&mut self) -> Result { + T::from_parser(self, None) + } + + fn read_field_value(&mut self, field: &FieldInstance) -> Result + where + T::Error: From, + { + if field.is_vl_encoded { + let length = self.read_length_prefix()?; + T::from_parser(self, Some(length)) + } else { + T::from_parser(self, None) + } + } +} + +impl From<&[u8]> for BinaryParser { + fn from(hex_bytes: &[u8]) -> Self { + BinaryParser(hex_bytes.to_vec()) + } +} + +impl From> for BinaryParser { + fn from(hex_bytes: Vec) -> Self { + BinaryParser(hex_bytes) + } +} + +impl TryFrom<&str> for BinaryParser { + type Error = XRPLBinaryCodecException; + + fn try_from(hex_bytes: &str) -> Result { + Ok(BinaryParser(hex::decode(hex_bytes)?)) + } +} + +impl PartialEq<[u8]> for BinaryParser { + fn eq(&self, bytes: &[u8]) -> bool { + self.0 == bytes + } +} + +impl PartialEq> for BinaryParser { + fn eq(&self, bytes: &Vec) -> bool { + &self.0 == bytes + } +} + +impl ExactSizeIterator for BinaryParser { + fn len(&self) -> usize { + self.0.len() + } +} + +impl Iterator for BinaryParser { + type Item = u8; + + fn next(&mut self) -> Option { + if self.is_end(None) { + None + } else { + Some(self.read_uint8().expect("BinaryParser::next")) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::alloc::string::ToString; + use alloc::string::String; + + const TEST_HEX: &str = "00112233445566"; + + #[test] + fn test_binaryparser_from() { + let test_bytes: Vec = hex::decode(TEST_HEX).expect(""); + let ref_bytes: &[u8] = test_bytes.as_ref(); + let slice_parser = BinaryParser::from(ref_bytes); + let vec_parser = BinaryParser::from(test_bytes.to_owned()); + + assert_eq!(slice_parser, test_bytes[..]); + assert_eq!(vec_parser, test_bytes[..]); + } + + #[test] + fn test_binaryparser_try_from() { + let test_bytes: Vec = hex::decode(TEST_HEX).expect(""); + let string_parser = BinaryParser::try_from(TEST_HEX).unwrap(); + + assert_eq!(string_parser, test_bytes[..]); + } + + #[test] + fn test_peek() { + let test_bytes: Vec = hex::decode(TEST_HEX).expect(""); + let binary_parser = BinaryParser::from(test_bytes.as_ref()); + + assert_eq!(binary_parser.peek(), Some([test_bytes[0]; 1])); + } + + #[test] + fn test_skip_bytes() { + let test_bytes: Vec = hex::decode(TEST_HEX).expect(""); + let mut binary_parser = BinaryParser::from(test_bytes.as_ref()); + + assert!(binary_parser.skip_bytes(4).is_ok()); + assert_eq!(binary_parser, test_bytes[4..]); + } + + #[test] + fn test_read() { + let test_bytes: Vec = hex::decode(TEST_HEX).expect(""); + let mut binary_parser = BinaryParser::from(test_bytes.as_ref()); + let result = binary_parser.read(5); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), test_bytes[..5]); + } + + #[test] + fn test_read_uint8() { + let test_hex: &str = "01000200000003"; + let test_bytes: Vec = hex::decode(test_hex).expect(""); + let mut binary_parser = BinaryParser::from(test_bytes.as_ref()); + let result = binary_parser.read_uint8(); + + assert!(result.is_ok()); + assert_eq!(result, Ok(1)); + } + + #[test] + fn test_read_uint16() { + let test_hex: &str = "000200000003"; + let test_bytes: Vec = hex::decode(test_hex).expect(""); + let mut binary_parser = BinaryParser::from(test_bytes.as_ref()); + let result = binary_parser.read_uint16(); + + assert!(result.is_ok()); + assert_eq!(result, Ok(2)); + } + + #[test] + fn test_read_uint32() { + let test_hex: &str = "00000003"; + let test_bytes: Vec = hex::decode(test_hex).expect(""); + let mut binary_parser = BinaryParser::from(test_bytes.as_ref()); + let result = binary_parser.read_uint32(); + + assert!(result.is_ok()); + assert_eq!(result, Ok(3)); + } + + #[test] + fn test_read_length_prefix() { + let test_bytes: Vec = hex::decode(TEST_HEX).expect(""); + let mut binary_parser = BinaryParser::from(test_bytes.as_ref()); + let result = binary_parser.read_length_prefix(); + + assert!(result.is_ok()); + assert_eq!(result, Ok(0)); + } + + // TODO Finish tests + #[test] + fn test_read_field_header() {} + + #[test] + fn test_read_field_value() {} + + #[test] + fn test_read_field_and_value() {} + + #[test] + fn test_read_type() {} + + #[test] + fn accept_peek_skip_read() { + let test_bytes: Vec = hex::decode(TEST_HEX).expect(""); + let mut binary_parser = BinaryParser::from(test_bytes.as_ref()); + + assert_eq!(binary_parser.peek(), Some([test_bytes[0]; 1])); + assert!(binary_parser.skip_bytes(3).is_ok()); + assert_eq!(binary_parser, test_bytes[3..]); + + let result = binary_parser.read(2); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), test_bytes[3..5]); + } + + #[test] + fn test_binaryserializer_write_field_and_value() { + let field_header = FieldHeader { + type_code: -2, + field_code: 0, + }; + + let field_info = FieldInfo { + nth: 0, + is_vl_encoded: false, + is_serialized: false, + is_signing_field: false, + r#type: "Unknown".to_string(), + }; + + let field_instance = FieldInstance::new(&field_info, "Generic", field_header); + let expected: Vec = [255, 224, 0, 17, 34].to_vec(); + let test_bytes: Vec = [0, 17, 34].to_vec(); + let mut serializer: BinarySerializer = BinarySerializer::new(); + + serializer.write_field_and_value(field_instance, &test_bytes); + assert_eq!(expected, serializer); + } + + /// This is currently a sanity check for private + /// [`_encode_variable_length_prefix`], which is called by + /// BinarySerializer.write_length_encoded. + #[test] + fn test_encode_variable_length_prefix() { + for case in [100_usize, 1000, 20_000] { + let blob = (0..case).map(|_| "A2").collect::(); + let mut binary_serializer: BinarySerializer = BinarySerializer::new(); + + binary_serializer.write_length_encoded(&hex::decode(blob).expect("")); + + let mut binary_parser: BinaryParser = BinaryParser::from(binary_serializer.as_ref()); + let decoded_length = binary_parser.read_length_prefix(); + + assert!(decoded_length.is_ok()); + assert_eq!(decoded_length, Ok(case)); + } + } +} diff --git a/src/core/binarycodec/test_cases.rs b/src/core/binarycodec/test_cases.rs new file mode 100644 index 00000000..fd9f0ea7 --- /dev/null +++ b/src/core/binarycodec/test_cases.rs @@ -0,0 +1,83 @@ +//! Test cases + +use alloc::string::String; +use alloc::vec::Vec; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use serde_json::value::Value; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Type { + pub name: String, + pub ordinal: i16, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct FieldTest { + pub type_name: String, + pub name: String, + pub nth_of_type: i16, + pub r#type: i16, + pub expected_hex: String, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct WholeObject {} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ValueTest { + pub test_json: Value, + pub r#type: String, + pub is_negative: Option, + pub is_native: Option, + pub type_id: Option, + pub expected_hex: Option, + pub mantissa: Option, + pub significant_digits: Option, + pub exponent: Option, + pub error: Option, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct TestDefinitions { + pub types: Vec, + pub fields_tests: Vec, + pub whole_objects: Vec, + pub values_tests: Vec, +} + +fn _load_tests() -> &'static Option { + pub const DATA_DRIVEN_TESTS: &str = include_str!("../test_data/data-driven-tests.json"); + pub const CODEC_TEST_FIXTURES: &str = include_str!("../test_data/codec-fixtures.json"); + pub const X_CODEC_TEST_FIXTURES: &str = include_str!("../test_data/x-codec-fixtures.json"); + + lazy_static! { + static ref TEST_CASES: Option = + Some(serde_json::from_str(DATA_DRIVEN_TESTS).expect("_load_tests")); + } + + &TEST_CASES +} + +/// Retrieve the field tests. +pub fn load_field_tests() -> &'static Vec { + let defintions = _load_tests().as_ref().expect("load_field_tests"); + &defintions.fields_tests +} + +/// Retrieve the field tests. +pub fn load_data_tests(test_type: Option<&str>) -> Vec { + let defintions = _load_tests().as_ref().expect("load_data_tests"); + + if let Some(test) = test_type { + defintions + .values_tests + .clone() + .into_iter() + .filter(|vt| vt.r#type == test) + .collect::>() + .to_vec() + } else { + defintions.values_tests.clone() + } +} diff --git a/src/core/binarycodec/utils.rs b/src/core/binarycodec/utils.rs new file mode 100644 index 00000000..5afacc9d --- /dev/null +++ b/src/core/binarycodec/utils.rs @@ -0,0 +1,253 @@ +//! Utilities for binarycodec crate. + +use crate::core::binarycodec::exceptions::XRPLBinaryCodecException; +use crate::core::definitions::load_definition_map; +use crate::core::definitions::DefinitionHandler; +use crate::core::definitions::FieldHeader; +use crate::core::definitions::CODE_MAX_VALUE; +use crate::core::definitions::CODE_MIN_VALUE; +use alloc::vec; +use alloc::vec::Vec; + +/// Max length that can be represented in a single byte +/// per XRPL serialization encoding. +pub const MAX_SINGLE_BYTE_LENGTH: usize = 192; +/// Max length that can be represented in 2 bytes per +/// XRPL serialization encoding. +pub const MAX_DOUBLE_BYTE_LENGTH: usize = 12481; +/// Max value that can be used in the second byte of a +/// length field. +pub const MAX_SECOND_BYTE_VALUE: usize = 240; +/// Max value that can be represented in using two +/// 8-bit bytes (2^16) +pub const MAX_DOUBLE_BYTE_VALUE: usize = 65536; +/// Maximum length that can be encoded in a length +/// prefix per XRPL serialization encoding. +pub const MAX_LENGTH_VALUE: usize = 918744; +/// Max value that can be represented using one 8-bit +/// byte (2^8) +pub const MAX_BYTE_VALUE: usize = 256; + +/// See: `` +fn _encode_field_id(field_header: &FieldHeader) -> Result, XRPLBinaryCodecException> { + let type_code = field_header.type_code; + let field_code = field_header.field_code; + let range = CODE_MIN_VALUE..CODE_MAX_VALUE; + + if !range.contains(&field_code) { + Err(XRPLBinaryCodecException::UnexpectedFieldCodeRange { + min: CODE_MIN_VALUE as usize, + max: CODE_MAX_VALUE as usize, + }) + } else if !range.contains(&type_code) { + Err(XRPLBinaryCodecException::UnexpectedTypeCodeRange { + min: CODE_MIN_VALUE as usize, + max: CODE_MAX_VALUE as usize, + }) + } else if type_code < 16 && field_code < 16 { + // high 4 bits is the type_code + // low 4 bits is the field code + let combined_code = (type_code << 4) | field_code; + Ok([combined_code as u8].to_vec()) + } else if type_code >= 16 && field_code < 16 { + // first 4 bits are zeroes + // next 4 bits is field code + // next byte is type code + let mut result = vec![]; + let byte1 = [field_code as u8]; + let byte2 = [type_code as u8]; + + result.extend_from_slice(&byte1); + result.extend_from_slice(&byte2); + + Ok(result) + } else if type_code < 16 && field_code >= 16 { + // first 4 bits is type code + // next 4 bits are zeroes + // next byte is field code + let mut result = vec![]; + let byte1 = [(type_code << 4) as u8]; + let byte2 = [field_code as u8]; + + result.extend_from_slice(&byte1); + result.extend_from_slice(&byte2); + + Ok(result) + } else { + // both are >= 16 + // first byte is all zeroes + // second byte is type code + // third byte is field code + let mut result = vec![]; + let byte2 = [type_code as u8]; + let byte3 = [field_code as u8]; + + result.extend_from_slice(&[0]); + result.extend_from_slice(&byte2); + result.extend_from_slice(&byte3); + + Ok(result) + } +} + +/// See: `` +fn _decode_field_id(field_id: &str) -> Result { + let bytes = hex::decode(field_id)?; + + match bytes.len() { + 1 => { + let type_code = (bytes[0] >> 4) as i16; + let field_code = (bytes[0] & 0x0F) as i16; + + Ok(FieldHeader { + type_code, + field_code, + }) + } + 2 => { + let first_byte = bytes[0]; + let second_byte = bytes[1]; + let first_byte_high_bits = first_byte >> 4; + let first_byte_low_bits = first_byte & 0x0F; + + if first_byte_high_bits == 0 { + // Next 4 bits are field code, second byte + // is type code. + let type_code = second_byte as i16; + let field_code = first_byte_low_bits as i16; + + Ok(FieldHeader { + type_code, + field_code, + }) + } else { + // Otherwise, next 4 bits are type code, + // second byte is field code. + let type_code = first_byte_high_bits as i16; + let field_code = second_byte as i16; + + Ok(FieldHeader { + type_code, + field_code, + }) + } + } + 3 => { + let type_code = bytes[1] as i16; + let field_code = bytes[2] as i16; + + Ok(FieldHeader { + type_code, + field_code, + }) + } + _ => Err(XRPLBinaryCodecException::UnexpectedFieldIdByteRange { min: 1, max: 3 }), + } +} + +/// Returns the unique field ID for a given field name. +/// This field ID consists of the type code and field +/// code, in 1 to 3 bytes depending on whether those +/// values are "common" (<16) or "uncommon" (>=16) +/// +/// See Field Ids: +/// `` +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::binarycodec::utils::encode_field_name; +/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; +/// extern crate alloc; +/// use alloc::vec; +/// +/// let field_name: &str = "LedgerSequence"; +/// let bytes: Vec = vec![38]; +/// +/// let encoding: Option> = match encode_field_name(field_name) { +/// Ok(bytes) => Some(bytes), +/// Err(e) => match e { +/// XRPLBinaryCodecException::UnknownFieldName => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(bytes), encoding); +/// ``` +pub fn encode_field_name(field_name: &str) -> Result, XRPLBinaryCodecException> { + let definitions = load_definition_map(); + let field_header = definitions.get_field_header_from_name(field_name); + + if let Some(header) = field_header { + _encode_field_id(&header) + } else { + Err(XRPLBinaryCodecException::UnknownFieldName) + } +} + +/// Returns the field name represented by the given field ID. +/// +/// See Field Ids: +/// `` +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::binarycodec::utils::decode_field_name; +/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; +/// +/// let field_id: &str = "26"; +/// let field_name: &str = "LedgerSequence"; +/// +/// let decoding: Option<&str> = match decode_field_name(field_id) { +/// Ok(field_name) => Some(field_name), +/// Err(e) => match e { +/// XRPLBinaryCodecException::UnexpectedFieldIdByteRange { +/// min: _, +/// max: _ +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(field_name), decoding); +/// ``` +pub fn decode_field_name(field_id: &str) -> Result<&str, XRPLBinaryCodecException> { + let definitions = load_definition_map(); + let field_header = _decode_field_id(field_id)?; + let field_name = definitions.get_field_name_from_header(&field_header); + + if let Some(name) = field_name { + Ok(name) + } else { + Err(XRPLBinaryCodecException::UnknownFieldName) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::core::binarycodec::test_cases::load_field_tests; + + #[test] + fn test_encode_field_name() { + for test in load_field_tests() { + let result = hex::encode_upper(encode_field_name(&test.name).expect("")); + assert_eq!(test.expected_hex, result) + } + } + + #[test] + fn test_decode_field_name() { + for test in load_field_tests() { + assert_eq!( + decode_field_name(&test.expected_hex), + Ok(test.name.as_ref()) + ) + } + } +} diff --git a/src/core/definitions/definitions.json b/src/core/definitions/definitions.json new file mode 100644 index 00000000..9a4f4453 --- /dev/null +++ b/src/core/definitions/definitions.json @@ -0,0 +1,1762 @@ +{ + "TYPES": { + "Validation": 10003, + "Done": -1, + "Hash128": 4, + "Blob": 7, + "AccountID": 8, + "Amount": 6, + "Hash256": 5, + "UInt8": 16, + "Vector256": 19, + "SerializedDict": 14, + "Unknown": -2, + "Transaction": 10001, + "Hash160": 17, + "PathSet": 18, + "LedgerEntry": 10002, + "UInt16": 1, + "NotPresent": 0, + "UInt64": 3, + "UInt32": 2, + "SerializedList": 15 + }, + "LEDGER_ENTRY_TYPES": { + "Any": -3, + "Child": -2, + "Invalid": -1, + "AccountRoot": 97, + "DirectoryNode": 100, + "RippleState": 114, + "Ticket": 84, + "SignerList": 83, + "Offer": 111, + "LedgerHashes": 104, + "Amendments": 102, + "FeeSettings": 115, + "Escrow": 117, + "PayChannel": 120, + "DepositPreauth": 112, + "Check": 67, + "Nickname": 110, + "Contract": 99, + "GeneratorMap": 103, + "NegativeUNL": 78 + }, + "FIELDS": [ + [ + "Generic", + { + "nth": 0, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Unknown" + } + ], + [ + "Invalid", + { + "nth": -1, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Unknown" + } + ], + [ + "LedgerEntryType", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt16" + } + ], + [ + "TransactionType", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt16" + } + ], + [ + "SignerWeight", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt16" + } + ], + [ + "Flags", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "SourceTag", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "Sequence", + { + "nth": 4, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "PreviousTxnLgrSeq", + { + "nth": 5, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "LedgerSequence", + { + "nth": 6, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "CloseTime", + { + "nth": 7, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "ParentCloseTime", + { + "nth": 8, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "SigningTime", + { + "nth": 9, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "Expiration", + { + "nth": 10, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "TransferRate", + { + "nth": 11, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "WalletSize", + { + "nth": 12, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "OwnerCount", + { + "nth": 13, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "DestinationTag", + { + "nth": 14, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "HighQualityIn", + { + "nth": 16, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "HighQualityOut", + { + "nth": 17, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "LowQualityIn", + { + "nth": 18, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "LowQualityOut", + { + "nth": 19, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "QualityIn", + { + "nth": 20, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "QualityOut", + { + "nth": 21, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "StampEscrow", + { + "nth": 22, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "BondAmount", + { + "nth": 23, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "LoadFee", + { + "nth": 24, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "OfferSequence", + { + "nth": 25, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "FirstLedgerSequence", + { + "nth": 26, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "LastLedgerSequence", + { + "nth": 27, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "TransactionIndex", + { + "nth": 28, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "OperationLimit", + { + "nth": 29, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "ReferenceFeeUnits", + { + "nth": 30, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "ReserveBase", + { + "nth": 31, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "ReserveIncrement", + { + "nth": 32, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "SetFlag", + { + "nth": 33, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "ClearFlag", + { + "nth": 34, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "SignerQuorum", + { + "nth": 35, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "CancelAfter", + { + "nth": 36, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "FinishAfter", + { + "nth": 37, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "IndexNext", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "IndexPrevious", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "BookNode", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "OwnerNode", + { + "nth": 4, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "BaseFee", + { + "nth": 5, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "ExchangeRate", + { + "nth": 6, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "LowNode", + { + "nth": 7, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "HighNode", + { + "nth": 8, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ], + [ + "EmailHash", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash128" + } + ], + [ + "LedgerHash", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "ParentHash", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "TransactionHash", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "AccountHash", + { + "nth": 4, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "PreviousTxnID", + { + "nth": 5, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "LedgerIndex", + { + "nth": 6, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "WalletLocator", + { + "nth": 7, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "RootIndex", + { + "nth": 8, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "AccountTxnID", + { + "nth": 9, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "BookDirectory", + { + "nth": 16, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "InvoiceID", + { + "nth": 17, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "Nickname", + { + "nth": 18, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "Amendment", + { + "nth": 19, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "TicketID", + { + "nth": 20, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "Digest", + { + "nth": 21, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "hash", + { + "nth": 257, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Hash256" + } + ], + [ + "index", + { + "nth": 258, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Hash256" + } + ], + [ + "Amount", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "Balance", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "LimitAmount", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "TakerPays", + { + "nth": 4, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "TakerGets", + { + "nth": 5, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "LowLimit", + { + "nth": 6, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "HighLimit", + { + "nth": 7, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "Fee", + { + "nth": 8, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "SendMax", + { + "nth": 9, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "DeliverMin", + { + "nth": 10, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "MinimumOffer", + { + "nth": 16, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "RippleEscrow", + { + "nth": 17, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "DeliveredAmount", + { + "nth": 18, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Amount" + } + ], + [ + "taker_gets_funded", + { + "nth": 258, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Amount" + } + ], + [ + "taker_pays_funded", + { + "nth": 259, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Amount" + } + ], + [ + "PublicKey", + { + "nth": 1, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "MessageKey", + { + "nth": 2, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "SigningPubKey", + { + "nth": 3, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "TxnSignature", + { + "nth": 4, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": false, + "type": "Blob" + } + ], + [ + "Generator", + { + "nth": 5, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "Signature", + { + "nth": 6, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": false, + "type": "Blob" + } + ], + [ + "Domain", + { + "nth": 7, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "FundCode", + { + "nth": 8, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "RemoveCode", + { + "nth": 9, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "ExpireCode", + { + "nth": 10, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "CreateCode", + { + "nth": 11, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "MemoType", + { + "nth": 12, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "MemoData", + { + "nth": 13, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "MemoFormat", + { + "nth": 14, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "Fulfillment", + { + "nth": 16, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "Condition", + { + "nth": 17, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "MasterSignature", + { + "nth": 18, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": false, + "type": "Blob" + } + ], + [ + "UNLModifyValidator", + { + "nth": 19, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "ValidatorToDisable", + { + "nth": 20, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "ValidatorToReEnable", + { + "nth": 20, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Blob" + } + ], + [ + "Account", + { + "nth": 1, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "Owner", + { + "nth": 2, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "Destination", + { + "nth": 3, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "Issuer", + { + "nth": 4, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "Authorize", + { + "nth": 5, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "Unauthorize", + { + "nth": 6, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "Target", + { + "nth": 7, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "RegularKey", + { + "nth": 8, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "AccountID" + } + ], + [ + "ObjectEndMarker", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "TransactionMetaData", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "CreatedNode", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "DeletedNode", + { + "nth": 4, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "ModifiedNode", + { + "nth": 5, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "PreviousFields", + { + "nth": 6, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "FinalFields", + { + "nth": 7, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "NewFields", + { + "nth": 8, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "TemplateEntry", + { + "nth": 9, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "Memo", + { + "nth": 10, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "SignerEntry", + { + "nth": 11, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "Signer", + { + "nth": 16, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "Majority", + { + "nth": 18, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "DisabledValidator", + { + "nth": 19, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedDict" + } + ], + [ + "ArrayEndMarker", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "Signers", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": false, + "type": "SerializedList" + } + ], + [ + "SignerEntries", + { + "nth": 4, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "Template", + { + "nth": 5, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "Necessary", + { + "nth": 6, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "Sufficient", + { + "nth": 7, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "AffectedNodes", + { + "nth": 8, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "Memos", + { + "nth": 9, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "Majorities", + { + "nth": 16, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "DisabledValidators", + { + "nth": 17, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "SerializedList" + } + ], + [ + "CloseResolution", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt8" + } + ], + [ + "Method", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt8" + } + ], + [ + "TransactionResult", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt8" + } + ], + [ + "TakerPaysCurrency", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash160" + } + ], + [ + "TakerPaysIssuer", + { + "nth": 2, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash160" + } + ], + [ + "TakerGetsCurrency", + { + "nth": 3, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash160" + } + ], + [ + "TakerGetsIssuer", + { + "nth": 4, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash160" + } + ], + [ + "Paths", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "PathSet" + } + ], + [ + "Indexes", + { + "nth": 1, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Vector256" + } + ], + [ + "Hashes", + { + "nth": 2, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Vector256" + } + ], + [ + "Amendments", + { + "nth": 3, + "isVLEncoded": true, + "isSerialized": true, + "isSigningField": true, + "type": "Vector256" + } + ], + [ + "Transaction", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Transaction" + } + ], + [ + "LedgerEntry", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "LedgerEntry" + } + ], + [ + "Validation", + { + "nth": 1, + "isVLEncoded": false, + "isSerialized": false, + "isSigningField": false, + "type": "Validation" + } + ], + [ + "SignerListID", + { + "nth": 38, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "SettleDelay", + { + "nth": 39, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "BeginLedgerSeq", + { + "nth": 40, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt32" + } + ], + [ + "Channel", + { + "nth": 22, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "ConsensusHash", + { + "nth": 23, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "CheckID", + { + "nth": 24, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "Hash256" + } + ], + [ + "TickSize", + { + "nth": 16, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt8" + } + ], + [ + "UNLModifyDisabling", + { + "nth": 17, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt8" + } + ], + [ + "DestinationNode", + { + "nth": 9, + "isVLEncoded": false, + "isSerialized": true, + "isSigningField": true, + "type": "UInt64" + } + ] + ], + "TRANSACTION_RESULTS": { + "telLOCAL_ERROR": -399, + "telBAD_DOMAIN": -398, + "telBAD_PATH_COUNT": -397, + "telBAD_PUBLIC_KEY": -396, + "telFAILED_PROCESSING": -395, + "telINSUF_FEE_P": -394, + "telNO_DST_PARTIAL": -393, + "telCAN_NOT_QUEUE": -392, + "telCAN_NOT_QUEUE_BALANCE": -391, + "telCAN_NOT_QUEUE_BLOCKS": -390, + "telCAN_NOT_QUEUE_BLOCKED": -389, + "telCAN_NOT_QUEUE_FEE": -388, + "telCAN_NOT_QUEUE_FULL": -387, + + "temMALFORMED": -299, + "temBAD_AMOUNT": -298, + "temBAD_CURRENCY": -297, + "temBAD_EXPIRATION": -296, + "temBAD_FEE": -295, + "temBAD_ISSUER": -294, + "temBAD_LIMIT": -293, + "temBAD_OFFER": -292, + "temBAD_PATH": -291, + "temBAD_PATH_LOOP": -290, + "temBAD_REGKEY": -289, + "temBAD_SEND_XRP_LIMIT": -288, + "temBAD_SEND_XRP_MAX": -287, + "temBAD_SEND_XRP_NO_DIRECT": -286, + "temBAD_SEND_XRP_PARTIAL": -285, + "temBAD_SEND_XRP_PATHS": -284, + "temBAD_SEQUENCE": -283, + "temBAD_SIGNATURE": -282, + "temBAD_SRC_ACCOUNT": -281, + "temBAD_TRANSFER_RATE": -280, + "temDST_IS_SRC": -279, + "temDST_NEEDED": -278, + "temINVALID": -277, + "temINVALID_FLAG": -276, + "temREDUNDANT": -275, + "temRIPPLE_EMPTY": -274, + "temDISABLED": -273, + "temBAD_SIGNER": -272, + "temBAD_QUORUM": -271, + "temBAD_WEIGHT": -270, + "temBAD_TICK_SIZE": -269, + "temINVALID_ACCOUNT_ID": -268, + "temCANNOT_PREAUTH_SELF": -267, + "temUNCERTAIN": -266, + "temUNKNOWN": -265, + + "tefFAILURE": -199, + "tefALREADY": -198, + "tefBAD_ADD_AUTH": -197, + "tefBAD_AUTH": -196, + "tefBAD_LEDGER": -195, + "tefCREATED": -194, + "tefEXCEPTION": -193, + "tefINTERNAL": -192, + "tefNO_AUTH_REQUIRED": -191, + "tefPAST_SEQ": -190, + "tefWRONG_PRIOR": -189, + "tefMASTER_DISABLED": -188, + "tefMAX_LEDGER": -187, + "tefBAD_SIGNATURE": -186, + "tefBAD_QUORUM": -185, + "tefNOT_MULTI_SIGNING": -184, + "tefBAD_AUTH_MASTER": -183, + "tefINVARIANT_FAILED": -182, + "tefTOO_BIG": -181, + + "terRETRY": -99, + "terFUNDS_SPENT": -98, + "terINSUF_FEE_B": -97, + "terNO_ACCOUNT": -96, + "terNO_AUTH": -95, + "terNO_LINE": -94, + "terOWNERS": -93, + "terPRE_SEQ": -92, + "terLAST": -91, + "terNO_RIPPLE": -90, + "terQUEUED": -89, + + "tesSUCCESS": 0, + + "tecCLAIM": 100, + "tecPATH_PARTIAL": 101, + "tecUNFUNDED_ADD": 102, + "tecUNFUNDED_OFFER": 103, + "tecUNFUNDED_PAYMENT": 104, + "tecFAILED_PROCESSING": 105, + "tecDIR_FULL": 121, + "tecINSUF_RESERVE_LINE": 122, + "tecINSUF_RESERVE_OFFER": 123, + "tecNO_DST": 124, + "tecNO_DST_INSUF_XRP": 125, + "tecNO_LINE_INSUF_RESERVE": 126, + "tecNO_LINE_REDUNDANT": 127, + "tecPATH_DRY": 128, + "tecUNFUNDED": 129, + "tecNO_ALTERNATIVE_KEY": 130, + "tecNO_REGULAR_KEY": 131, + "tecOWNERS": 132, + "tecNO_ISSUER": 133, + "tecNO_AUTH": 134, + "tecNO_LINE": 135, + "tecINSUFF_FEE": 136, + "tecFROZEN": 137, + "tecNO_TARGET": 138, + "tecNO_PERMISSION": 139, + "tecNO_ENTRY": 140, + "tecINSUFFICIENT_RESERVE": 141, + "tecNEED_MASTER_KEY": 142, + "tecDST_TAG_NEEDED": 143, + "tecINTERNAL": 144, + "tecOVERSIZE": 145, + "tecCRYPTOCONDITION_ERROR": 146, + "tecINVARIANT_FAILED": 147, + "tecEXPIRED": 148, + "tecDUPLICATE": 149, + "tecKILLED": 150, + "tecHAS_OBLIGATIONS": 151, + "tecTOO_SOON": 152 + }, + "TRANSACTION_TYPES": { + "Invalid": -1, + + "Payment": 0, + "EscrowCreate": 1, + "EscrowFinish": 2, + "AccountSet": 3, + "EscrowCancel": 4, + "SetRegularKey": 5, + "NickNameSet": 6, + "OfferCreate": 7, + "OfferCancel": 8, + "Contract": 9, + "TicketCreate": 10, + "TicketCancel": 11, + "SignerListSet": 12, + "PaymentChannelCreate": 13, + "PaymentChannelFund": 14, + "PaymentChannelClaim": 15, + "CheckCreate": 16, + "CheckCash": 17, + "CheckCancel": 18, + "DepositPreauth": 19, + "TrustSet": 20, + "AccountDelete": 21, + + "EnableAmendment": 100, + "SetFee": 101, + "UNLModify": 102 + } +} diff --git a/src/core/definitions/mod.rs b/src/core/definitions/mod.rs new file mode 100644 index 00000000..12dfe556 --- /dev/null +++ b/src/core/definitions/mod.rs @@ -0,0 +1,168 @@ +//! Functions for encoding objects into the XRP Ledger's +//! canonical binary format and decoding them. + +pub mod types; + +pub use self::types::*; + +use crate::utils::ToBytes; +use alloc::format; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use serde::{Deserialize, Serialize}; + +pub const CODE_MIN_VALUE: i16 = 1; +pub const CODE_MAX_VALUE: i16 = u8::MAX as i16; + +/// A container class for simultaneous storage of a field's +/// type code and field code. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::definitions::FieldHeader; +/// +/// let field_header = FieldHeader { +/// type_code: -2, +/// field_code: 0, +/// }; +/// ``` +#[derive(Debug, Clone)] +pub struct FieldHeader { + pub type_code: i16, + pub field_code: i16, +} + +/// A collection of serialization information about +/// a specific field type. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::definitions::FieldInfo; +/// use xrpl::core::definitions::FieldHeader; +/// use xrpl::core::definitions::FieldInstance; +/// +/// let field_header: FieldHeader = FieldHeader { +/// type_code: -2, +/// field_code: 0, +/// }; +/// +/// let field_info: FieldInfo = FieldInfo { +/// nth: 0, +/// is_vl_encoded: false, +/// is_serialized: false, +/// is_signing_field: false, +/// r#type: "Unknown".to_string(), +/// }; +/// +/// let field_instance: FieldInstance = +/// FieldInstance::new(&field_info, "Generic", field_header); +/// ``` +#[derive(Debug)] +pub struct FieldInstance { + pub nth: i16, + pub is_vl_encoded: bool, + pub is_serialized: bool, + pub is_signing: bool, + pub associated_type: String, + pub name: String, + pub header: FieldHeader, + pub ordinal: i32, +} + +///Model object for field info metadata from the +/// "fields" section of definitions.json. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::definitions::FieldInfo; +/// +/// let field_info = FieldInfo { +/// nth: 0, +/// is_vl_encoded: false, +/// is_serialized: false, +/// is_signing_field: false, +/// r#type: "Unknown".to_string(), +/// }; +/// ``` +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct FieldInfo { + /// The field code -- sort order position for + /// fields of the same type. + pub nth: i16, + /// Whether the serialized length of this + /// field varies. + #[serde(rename = "isVLEncoded")] + pub is_vl_encoded: bool, + /// If the field is presented in binary + /// serialized representation. + pub is_serialized: bool, + /// If the field should be included in signed + /// transactions. + pub is_signing_field: bool, + /// The name of this field's serialization type, + /// e.g. UInt32, AccountID, etc. + pub r#type: String, +} + +impl FieldInstance { + /// Create a new FieldInstance. + pub fn new(field_info: &FieldInfo, field_name: &str, field_header: FieldHeader) -> Self { + FieldInstance { + nth: field_info.nth, + is_vl_encoded: field_info.is_vl_encoded, + is_serialized: field_info.is_serialized, + is_signing: field_info.is_signing_field, + name: field_name.to_string(), + ordinal: &(field_header.type_code as i32) << 16 | field_info.nth as i32, + header: field_header, + associated_type: field_info.r#type.to_string(), + } + } +} + +impl ToString for FieldHeader { + /// Convert the FieldHeader to a String. + fn to_string(&self) -> String { + format!("{}_{}", self.type_code, self.field_code) + } +} + +impl ToBytes for FieldHeader { + /// Convert the FieldHeader to a Vec. + fn to_bytes(&self) -> Vec { + let mut header_bytes = vec![]; + + if self.type_code < 16 { + if self.field_code < 16 { + let shift = self.type_code << 4 | self.field_code; + header_bytes.extend_from_slice(&shift.to_be_bytes()); + } else { + let shift = self.type_code << 4; + + header_bytes.extend_from_slice(&shift.to_be_bytes()); + header_bytes.extend_from_slice(&self.field_code.to_be_bytes()); + } + } else if self.field_code < 16 { + header_bytes.extend_from_slice(&self.field_code.to_be_bytes()); + header_bytes.extend_from_slice(&self.type_code.to_be_bytes()); + } else { + header_bytes.extend_from_slice(&[0]); + header_bytes.extend_from_slice(&self.type_code.to_be_bytes()); + header_bytes.extend_from_slice(&self.field_code.to_be_bytes()); + } + + header_bytes + } +} diff --git a/src/core/definitions/types.rs b/src/core/definitions/types.rs new file mode 100644 index 00000000..5d419ee0 --- /dev/null +++ b/src/core/definitions/types.rs @@ -0,0 +1,898 @@ +//! Maps and helpers providing serialization-related +//! information about fields. + +use crate::core::definitions::FieldHeader; +use crate::core::definitions::FieldInfo; +use crate::core::definitions::FieldInstance; +use alloc::borrow::ToOwned; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; +use indexmap::IndexMap; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; + +type FieldInfoMap = IndexMap; +type TypeValueMap = IndexMap; +type TypeNameMap = IndexMap; +type FieldHeaderNameMap = IndexMap; +type TransactionTypeValueMap = IndexMap; +type TransactionTypeNameMap = IndexMap; +type TransactionResultValueMap = IndexMap; +type TransactionResultNameMap = IndexMap; +type LedgerEntryTypeValueMap = IndexMap; +type LedgerEntryTypeNameMap = IndexMap; + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "PascalCase")] +pub struct Types { + pub validation: i16, + pub done: i16, + pub hash_128: i16, + pub blob: i16, + #[serde(rename = "AccountID")] + pub account_id: i16, + pub amount: i16, + pub hash_256: i16, + pub u_int_8: i16, + pub vector_256: i16, + pub serialized_dict: i16, + pub unknown: i16, + pub transaction: i16, + pub hash_160: i16, + pub path_set: i16, + pub ledger_entry: i16, + pub u_int_16: i16, + pub not_present: i16, + pub u_int_64: i16, + pub u_int_32: i16, + pub serialized_list: i16, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "PascalCase")] +pub struct LedgerEntryTypes { + pub any: i16, + pub child: i16, + pub invalid: i16, + pub account_root: i16, + pub directory_node: i16, + pub ripple_state: i16, + pub ticket: i16, + pub signer_list: i16, + pub offer: i16, + pub ledger_hashes: i16, + pub amendments: i16, + pub fee_settings: i16, + pub escrow: i16, + pub pay_channel: i16, + pub deposit_preauth: i16, + pub check: i16, + pub nickname: i16, + pub contract: i16, + pub generator_map: i16, + #[serde(rename = "NegativeUNL")] + pub negative_unl: i16, +} + +/// =( +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct TransactionResults { + #[serde(rename = "telLOCAL_ERROR")] + pub tel_local_error: i16, + #[serde(rename = "telBAD_DOMAIN")] + pub tel_bad_domain: i16, + #[serde(rename = "telBAD_PATH_COUNT")] + pub tel_bad_path_count: i16, + #[serde(rename = "telBAD_PUBLIC_KEY")] + pub tel_bad_public_keyt: i16, + #[serde(rename = "telFAILED_PROCESSING")] + pub tel_failed_processing: i16, + #[serde(rename = "telINSUF_FEE_P")] + pub tel_insuf_fee_p: i16, + #[serde(rename = "telNO_DST_PARTIAL")] + pub tel_no_dst_partial: i16, + #[serde(rename = "telCAN_NOT_QUEUE")] + pub tel_can_not_queue: i16, + #[serde(rename = "telCAN_NOT_QUEUE_BALANCE")] + pub tel_can_not_queue_balance: i16, + #[serde(rename = "telCAN_NOT_QUEUE_BLOCKS")] + pub tel_can_not_blocks: i16, + #[serde(rename = "telCAN_NOT_QUEUE_BLOCKED")] + pub tel_can_not_blocked: i16, + #[serde(rename = "telCAN_NOT_QUEUE_FEE")] + pub tel_can_not_queue_fee: i16, + #[serde(rename = "telCAN_NOT_QUEUE_FULL")] + pub tel_can_not_queue_null: i16, + + #[serde(rename = "temMALFORMED")] + pub tem_malformed: i16, + #[serde(rename = "temBAD_AMOUNT")] + pub tem_bad_amount: i16, + #[serde(rename = "temBAD_CURRENCY")] + pub tem_bad_currency: i16, + #[serde(rename = "temBAD_EXPIRATION")] + pub tem_bad_expiration: i16, + #[serde(rename = "temBAD_FEE")] + pub tem_bad_fee: i16, + #[serde(rename = "temBAD_ISSUER")] + pub tem_bad_issuer: i16, + #[serde(rename = "temBAD_LIMIT")] + pub tem_bad_limit: i16, + #[serde(rename = "temBAD_OFFER")] + pub tem_bad_offer: i16, + #[serde(rename = "temBAD_PATH")] + pub tem_bad_path: i16, + #[serde(rename = "temBAD_PATH_LOOP")] + pub tem_bad_path_loop: i16, + #[serde(rename = "temBAD_REGKEY")] + pub tem_bad_regkey: i16, + #[serde(rename = "temBAD_SEND_XRP_LIMIT")] + pub tem_bad_send_xrp_limit: i16, + #[serde(rename = "temBAD_SEND_XRP_MAX")] + pub tem_bad_send_xrp_max: i16, + #[serde(rename = "temBAD_SEND_XRP_NO_DIRECT")] + pub tem_bad_send_xrp_no_direct: i16, + #[serde(rename = "temBAD_SEND_XRP_PARTIAL")] + pub tem_bad_send_xrp_partial: i16, + #[serde(rename = "temBAD_SEND_XRP_PATHS")] + pub tem_bad_send_xrp_paths: i16, + #[serde(rename = "temBAD_SEQUENCE")] + pub tem_bad_sequence: i16, + #[serde(rename = "temBAD_SIGNATURE")] + pub tem_bad_signature: i16, + #[serde(rename = "temBAD_SRC_ACCOUNT")] + pub tem_bad_src_account: i16, + #[serde(rename = "temBAD_TRANSFER_RATE")] + pub tem_bad_transfer_rate: i16, + #[serde(rename = "temDST_IS_SRC")] + pub tem_dst_is_src: i16, + #[serde(rename = "temDST_NEEDED")] + pub tem_dst_needed: i16, + #[serde(rename = "temINVALID")] + pub tem_invalid: i16, + #[serde(rename = "temINVALID_FLAG")] + pub tem_invalid_flag: i16, + #[serde(rename = "temREDUNDANT")] + pub tem_redundant: i16, + #[serde(rename = "temRIPPLE_EMPTY")] + pub tem_ripple_empty: i16, + #[serde(rename = "temDISABLED")] + pub tem_disabled: i16, + #[serde(rename = "temBAD_SIGNER")] + pub tem_bad_signer: i16, + #[serde(rename = "temBAD_QUORUM")] + pub tem_bad_quorum: i16, + #[serde(rename = "temBAD_WEIGHT")] + pub tem_bad_weight: i16, + #[serde(rename = "temBAD_TICK_SIZE")] + pub tem_bad_tick_size: i16, + #[serde(rename = "temINVALID_ACCOUNT_ID")] + pub tem_invalid_account_id: i16, + #[serde(rename = "temCANNOT_PREAUTH_SELF")] + pub tem_cannot_preauth_self: i16, + #[serde(rename = "temUNCERTAIN")] + pub tem_uncertain: i16, + #[serde(rename = "temUNKNOWN")] + pub tem_unknown: i16, + + #[serde(rename = "tefFAILURE")] + pub tef_failure: i16, + #[serde(rename = "tefALREADY")] + pub tef_already: i16, + #[serde(rename = "tefBAD_ADD_AUTH")] + pub tef_bad_add_auth: i16, + #[serde(rename = "tefBAD_AUTH")] + pub tef_bad_auth: i16, + #[serde(rename = "tefBAD_LEDGER")] + pub tef_bad_ledger: i16, + #[serde(rename = "tefCREATED")] + pub tef_created: i16, + #[serde(rename = "tefEXCEPTION")] + pub tef_exception: i16, + #[serde(rename = "tefINTERNAL")] + pub tef_internal: i16, + #[serde(rename = "tefNO_AUTH_REQUIRED")] + pub tef_no_auth_required: i16, + #[serde(rename = "tefPAST_SEQ")] + pub tef_past_seq: i16, + #[serde(rename = "tefWRONG_PRIOR")] + pub tef_wrong_prior: i16, + #[serde(rename = "tefMASTER_DISABLED")] + pub tef_master_disabled: i16, + #[serde(rename = "tefMAX_LEDGER")] + pub tef_max_ledger: i16, + #[serde(rename = "tefBAD_SIGNATURE")] + pub tef_bad_signature: i16, + #[serde(rename = "tefBAD_QUORUM")] + pub tef_bad_quorum: i16, + #[serde(rename = "tefNOT_MULTI_SIGNING")] + pub tef_not_multi_signing: i16, + #[serde(rename = "tefBAD_AUTH_MASTER")] + pub tef_bad_auth_master: i16, + #[serde(rename = "tefINVARIANT_FAILED")] + pub tef_invariant_failed: i16, + #[serde(rename = "tefTOO_BIG")] + pub tef_too_big: i16, + + #[serde(rename = "terRETRY")] + pub ter_retry: i16, + #[serde(rename = "terFUNDS_SPENT")] + pub ter_funds_spent: i16, + #[serde(rename = "terINSUF_FEE_B")] + pub ter_insuf_fee_b: i16, + #[serde(rename = "terNO_ACCOUNT")] + pub ter_no_account: i16, + #[serde(rename = "terNO_AUTH")] + pub ter_no_auth: i16, + #[serde(rename = "terNO_LINE")] + pub ter_no_line: i16, + #[serde(rename = "terOWNERS")] + pub ter_owners: i16, + #[serde(rename = "terPRE_SEQ")] + pub ter_pre_seq: i16, + #[serde(rename = "terLAST")] + pub ter_last: i16, + #[serde(rename = "terNO_RIPPLE")] + pub ter_no_ripple: i16, + #[serde(rename = "terQUEUED")] + pub ter_queued: i16, + + #[serde(rename = "tesSUCCESS")] + pub tes_success: i16, + + #[serde(rename = "tecCLAIM")] + pub tec_claim: i16, + #[serde(rename = "tecPATH_PARTIAL")] + pub tec_path_partial: i16, + #[serde(rename = "tecUNFUNDED_ADD")] + pub tec_unfunded_add: i16, + #[serde(rename = "tecUNFUNDED_OFFER")] + pub tec_unfunded_offer: i16, + #[serde(rename = "tecUNFUNDED_PAYMENT")] + pub tec_unfunded_payment: i16, + #[serde(rename = "tecFAILED_PROCESSING")] + pub tec_failed_processing: i16, + #[serde(rename = "tecDIR_FULL")] + pub tec_dir_full: i16, + #[serde(rename = "tecINSUF_RESERVE_LINE")] + pub tec_insuf_reserve_line: i16, + #[serde(rename = "tecINSUF_RESERVE_OFFER")] + pub tec_insuf_reserve_offer: i16, + #[serde(rename = "tecNO_DST")] + pub tec_no_dst: i16, + #[serde(rename = "tecNO_DST_INSUF_XRP")] + pub tec_no_dst_insuf_xrp: i16, + #[serde(rename = "tecNO_LINE_INSUF_RESERVE")] + pub tec_no_line_insuf_reserve: i16, + #[serde(rename = "tecNO_LINE_REDUNDANT")] + pub tec_no_line_redundant: i16, + #[serde(rename = "tecPATH_DRY")] + pub tec_path_dry: i16, + #[serde(rename = "tecUNFUNDED")] + pub tec_unfunded: i16, + #[serde(rename = "tecNO_ALTERNATIVE_KEY")] + pub tec_no_alternative_key: i16, + #[serde(rename = "tecNO_REGULAR_KEY")] + pub tec_no_regular_key: i16, + #[serde(rename = "tecOWNERS")] + pub tec_owners: i16, + #[serde(rename = "tecNO_ISSUER")] + pub tec_no_issuer: i16, + #[serde(rename = "tecNO_AUTH")] + pub tec_no_auth: i16, + #[serde(rename = "tecNO_LINE")] + pub tec_no_line: i16, + #[serde(rename = "tecINSUFF_FEE")] + pub tec_insuff_fee: i16, + #[serde(rename = "tecFROZEN")] + pub tec_frozen: i16, + #[serde(rename = "tecNO_TARGET")] + pub tec_no_target: i16, + #[serde(rename = "tecNO_PERMISSION")] + pub tec_no_permission: i16, + #[serde(rename = "tecNO_ENTRY")] + pub tec_no_entry: i16, + #[serde(rename = "tecINSUFFICIENT_RESERVE")] + pub tec_insufficient_reserve: i16, + #[serde(rename = "tecNEED_MASTER_KEY")] + pub tec_need_master_key: i16, + #[serde(rename = "tecDST_TAG_NEEDED")] + pub tec_dst_tag_needed: i16, + #[serde(rename = "tecINTERNAL")] + pub tec_internal: i16, + #[serde(rename = "tecOVERSIZE")] + pub tec_oversize: i16, + #[serde(rename = "tecCRYPTOCONDITION_ERROR")] + pub tec_cryptocondition_error: i16, + #[serde(rename = "tecINVARIANT_FAILED")] + pub tec_invariant_failed: i16, + #[serde(rename = "tecEXPIRED")] + pub tec_expired: i16, + #[serde(rename = "tecDUPLICATE")] + pub tec_duplicate: i16, + #[serde(rename = "tecKILLED")] + pub tec_killed: i16, + #[serde(rename = "tecHAS_OBLIGATIONS")] + pub tec_has_obligations: i16, + #[serde(rename = "tecTOO_SOON")] + pub tec_too_soon: i16, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "PascalCase")] +pub struct TransactionTypes { + pub invalid: i16, + + pub payment: i16, + pub escrow_create: i16, + pub escrow_finish: i16, + pub account_set: i16, + pub escrow_cancel: i16, + pub set_regular_key: i16, + pub nick_name_set: i16, + pub offer_create: i16, + pub offer_cancel: i16, + pub contract: i16, + pub ticket_create: i16, + pub ticket_cancel: i16, + pub signer_list_set: i16, + pub payment_channel_create: i16, + pub payment_channel_fund: i16, + pub payment_channel_claim: i16, + pub check_create: i16, + pub check_cash: i16, + pub check_cancel: i16, + pub deposit_preauth: i16, + pub trust_set: i16, + pub account_delete: i16, + + pub enable_amendment: i16, + pub set_fee: i16, + #[serde(rename = "UNLModify")] + pub unl_modify: i16, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Field(pub String, pub FieldInfo); + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "UPPERCASE")] +pub struct Definitions { + pub types: Types, + pub ledger_entry_types: LedgerEntryTypes, + pub fields: Vec, + pub transaction_results: TransactionResults, + pub transaction_types: TransactionTypes, +} + +/// Loads JSON from the definitions file and converts +/// it to a preferred format. The definitions file contains +/// information required for the XRP Ledger's canonical +/// binary serialization format. +/// +/// Serialization: +/// `` +#[derive(Debug, Clone)] +pub struct DefinitionMap { + field_info_map: FieldInfoMap, + type_value_map: TypeValueMap, + type_name_map: TypeNameMap, + field_header_name_map: FieldHeaderNameMap, + transaction_type_value_map: TransactionTypeValueMap, + transaction_type_name_map: TransactionTypeNameMap, + transaction_result_value_map: TransactionResultValueMap, + transaction_result_name_map: TransactionResultNameMap, + ledger_entry_type_value_map: LedgerEntryTypeValueMap, + ledger_entry_type_name_map: LedgerEntryTypeNameMap, +} + +pub trait DefinitionHandler { + /// Create a new instance of a definition handler using + /// a Definitions object. + fn new(definitions: &Definitions) -> Self; + /// Get a FieldInfo object from a field name. + fn get_field_info(&self, key: &str) -> Option<&FieldInfo>; + /// Returns the serialization data type for the given + /// field name. + /// + /// Serialization Type List: + /// `` + fn get_field_type_name(&self, field_name: &str) -> Option<&String>; + /// Returns the type code associated with the given field. + /// + /// Serialization Type Codes: + /// `` + fn get_field_type_code(&self, field_name: &str) -> Option<&i16>; + /// Returns the field code associated with the given + /// field. + /// + /// Serialization Field Codes: + /// `` + fn get_field_code(&self, field_name: &str) -> Option; + /// Returns a FieldHeader object for a field of the given + /// field name. + fn get_field_header_from_name(&self, field_name: &str) -> Option; + /// Returns the field name described by the given + /// FieldHeader object. + fn get_field_name_from_header(&self, field_header: &FieldHeader) -> Option<&String>; + /// Return a FieldInstance object for the given field name. + fn get_field_instance(&self, field_name: &str) -> Option; + /// Return an integer representing the given + /// transaction type string in an enum. + fn get_transaction_type_code(&self, transaction_type: &str) -> Option<&i16>; + /// Return string representing the given transaction + /// type from the enum. + fn get_transaction_type_name(&self, transaction_type: &i16) -> Option<&String>; + /// Return an integer representing the given transaction + /// result string in an enum. + fn get_transaction_result_code(&self, transaction_result: &str) -> Option<&i16>; + /// Return string representing the given transaction result + /// type from the enum. + fn get_transaction_result_name(&self, transaction_result: &i16) -> Option<&String>; + /// Return an integer representing the given ledger entry + /// type string in an enum. + fn get_ledger_entry_type_code(&self, ledger_entry_type: &str) -> Option<&i16>; + /// Return string representing the given ledger entry type + /// from the enum. + fn get_ledger_entry_type_name(&self, ledger_entry_type: &i16) -> Option<&String>; +} + +trait DefinitionMaker { + fn _make_type_maps(types: &Types) -> (TypeValueMap, TypeNameMap); + fn _make_field_info_map( + fields: &[Field], + types: &TypeValueMap, + ) -> (FieldInfoMap, FieldHeaderNameMap); + fn _make_transaction_type_maps( + transaction_types: &TransactionTypes, + ) -> (TransactionTypeValueMap, TransactionTypeNameMap); + fn _make_transaction_result_maps( + transaction_types: &TransactionResults, + ) -> (TransactionResultValueMap, TransactionResultNameMap); + fn _make_ledger_entry_type_maps( + types: &LedgerEntryTypes, + ) -> (LedgerEntryTypeValueMap, LedgerEntryTypeNameMap); +} + +impl DefinitionMaker for DefinitionMap { + fn _make_type_maps(types: &Types) -> (TypeValueMap, TypeNameMap) { + let json = serde_json::to_string(&types).expect("_make_type_maps_a"); + let v_map: TypeValueMap = serde_json::from_str(&json).expect("_make_type_maps_b"); + let mut n_map: TypeNameMap = TypeNameMap::default(); + + for (key, value) in &v_map { + n_map.insert(*value, key.to_owned()); + } + + (v_map, n_map) + } + + fn _make_field_info_map( + fields: &[Field], + types: &TypeValueMap, + ) -> (FieldInfoMap, FieldHeaderNameMap) { + let mut field_info_map = FieldInfoMap::default(); + let mut field_header_name_map = FieldHeaderNameMap::default(); + + for field in fields { + let field_name: &str = &(field.0); + let field_info: FieldInfo = (field.1).to_owned(); + let field_header = FieldHeader { + type_code: *types.get(&field_info.r#type).expect("_make_field_info_map"), + field_code: field_info.nth, + }; + + field_info_map.insert(field_name.to_owned(), field_info); + field_header_name_map.insert(field_header.to_string(), field_name.to_owned()); + } + + (field_info_map, field_header_name_map) + } + + fn _make_transaction_type_maps( + transaction_types: &TransactionTypes, + ) -> (TransactionTypeValueMap, TransactionTypeNameMap) { + let json = + serde_json::to_string(&transaction_types).expect("_make_transaction_type_maps_a"); + let v_map: TransactionTypeValueMap = + serde_json::from_str(&json).expect("_make_transaction_type_maps_b"); + let mut n_map: TransactionTypeNameMap = TypeNameMap::default(); + + for (key, value) in &v_map { + n_map.insert(*value, key.to_owned()); + } + + (v_map, n_map) + } + + fn _make_transaction_result_maps( + transaction_types: &TransactionResults, + ) -> (TransactionResultValueMap, TransactionResultNameMap) { + let json = + serde_json::to_string(&transaction_types).expect("_make_transaction_result_maps_a"); + let v_map: TransactionResultValueMap = + serde_json::from_str(&json).expect("_make_transaction_result_maps_b"); + let mut n_map: TransactionResultNameMap = TypeNameMap::default(); + + for (key, value) in &v_map { + n_map.insert(*value, key.to_owned()); + } + + (v_map, n_map) + } + + fn _make_ledger_entry_type_maps( + types: &LedgerEntryTypes, + ) -> (LedgerEntryTypeValueMap, LedgerEntryTypeNameMap) { + let json = serde_json::to_string(&types).expect("_make_ledger_entry_type_maps_a"); + let v_map: TypeValueMap = + serde_json::from_str(&json).expect("_make_ledger_entry_type_maps_b"); + let mut n_map: TypeNameMap = TypeNameMap::default(); + + for (key, value) in &v_map { + n_map.insert(*value, key.to_owned()); + } + + (v_map, n_map) + } +} + +impl DefinitionHandler for DefinitionMap { + fn new(definitions: &Definitions) -> Self { + let (type_value_map, type_name_map) = DefinitionMap::_make_type_maps(&definitions.types); + let (field_info_map, field_header_name_map) = + DefinitionMap::_make_field_info_map(&definitions.fields, &type_value_map); + let (transaction_type_value_map, transaction_type_name_map) = + DefinitionMap::_make_transaction_type_maps(&definitions.transaction_types); + let (transaction_result_value_map, transaction_result_name_map) = + DefinitionMap::_make_transaction_result_maps(&definitions.transaction_results); + let (ledger_entry_type_value_map, ledger_entry_type_name_map) = + DefinitionMap::_make_ledger_entry_type_maps(&definitions.ledger_entry_types); + + DefinitionMap { + field_info_map, + field_header_name_map, + type_value_map, + type_name_map, + transaction_type_value_map, + transaction_type_name_map, + transaction_result_value_map, + transaction_result_name_map, + ledger_entry_type_value_map, + ledger_entry_type_name_map, + } + } + + fn get_field_info(&self, key: &str) -> Option<&FieldInfo> { + self.field_info_map.get(key) + } + + fn get_field_type_name(&self, field_name: &str) -> Option<&String> { + let result = self.field_info_map.get(field_name); + + if let Some(value) = result { + Some(&value.r#type) + } else { + None + } + } + + fn get_field_type_code(&self, field_name: &str) -> Option<&i16> { + let result = self.get_field_type_name(field_name); + + if let Some(value) = result { + self.type_value_map.get(value) + } else { + None + } + } + + fn get_field_code(&self, field_name: &str) -> Option { + let result = self.get_field_info(field_name); + result.map(|value| value.nth) + } + + fn get_field_header_from_name(&self, field_name: &str) -> Option { + let type_code_wrap: Option<&i16> = self.get_field_type_code(field_name); + let field_code_wrap: Option = self.get_field_code(field_name); + + match (type_code_wrap, field_code_wrap) { + (Some(type_code), Some(field_code)) => Some(FieldHeader { + type_code: *type_code, + field_code, + }), + _ => None, + } + } + + fn get_field_name_from_header(&self, field_header: &FieldHeader) -> Option<&String> { + self.field_header_name_map.get(&field_header.to_string()) + } + + fn get_field_instance<'a>(&self, field_name: &str) -> Option { + let field_info_wrap = self.field_info_map.get(field_name); + let field_header_wrap = self.get_field_header_from_name(field_name); + + match (field_info_wrap, field_header_wrap) { + (Some(field_info), Some(field_header)) => { + Some(FieldInstance::new(field_info, field_name, field_header)) + } + _ => None, + } + } + + fn get_transaction_type_code(&self, transaction_type: &str) -> Option<&i16> { + self.transaction_type_value_map.get(transaction_type) + } + + fn get_transaction_type_name(&self, transaction_type: &i16) -> Option<&String> { + self.transaction_type_name_map.get(transaction_type) + } + + fn get_transaction_result_code(&self, transaction_result: &str) -> Option<&i16> { + self.transaction_result_value_map.get(transaction_result) + } + + fn get_transaction_result_name(&self, transaction_result: &i16) -> Option<&String> { + self.transaction_result_name_map.get(transaction_result) + } + + fn get_ledger_entry_type_code(&self, ledger_entry_type: &str) -> Option<&i16> { + self.ledger_entry_type_value_map.get(ledger_entry_type) + } + + fn get_ledger_entry_type_name(&self, ledger_entry_type: &i16) -> Option<&String> { + self.ledger_entry_type_name_map.get(ledger_entry_type) + } +} + +fn _load_definitions() -> &'static Option<(Definitions, DefinitionMap)> { + static JSON: &str = include_str!("definitions.json"); + + lazy_static! { + static ref DEFINITIONS: Option<(Definitions, DefinitionMap)> = { + let definitions: Definitions = serde_json::from_str(JSON).expect("_load_definitions"); + let definition_map: DefinitionMap = DefinitionMap::new(&definitions); + + Some((definitions, definition_map)) + }; + } + + &DEFINITIONS +} + +/// Retrieve the definition map. +pub fn load_definition_map() -> &'static DefinitionMap { + let (_, map) = _load_definitions().as_ref().expect("load_definition_map"); + map +} + +/// Returns the serialization data type for the +/// given field name. +/// +/// Serialization Type List: +/// `` +pub fn get_field_type_name(field_name: &str) -> Option<&String> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_field_type_name(field_name) +} + +/// Returns the type code associated with the +/// given field. +/// +/// Serialization Type Codes: +/// `` +pub fn get_field_type_code(field_name: &str) -> Option<&i16> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_field_type_code(field_name) +} + +/// Returns the field code associated with the +/// given field. +/// +/// Serialization Field Codes: +/// `` +pub fn get_field_code(field_name: &str) -> Option { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_field_code(field_name) +} + +/// Returns a FieldHeader object for a field of +/// the given field name. +pub fn get_field_header_from_name(field_name: &str) -> Option { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_field_header_from_name(field_name) +} + +/// Returns the field name described by the +/// given FieldHeader object. +pub fn get_field_name_from_header(field_header: &FieldHeader) -> Option<&String> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_field_name_from_header(field_header) +} + +/// Return a FieldInstance object for the given +/// field name. +pub fn get_field_instance(field_name: &str) -> Option { + let definition_map: &DefinitionMap = load_definition_map(); + + definition_map.get_field_instance(field_name) +} + +/// Return an integer representing the given +/// transaction type string in an enum. +pub fn get_transaction_type_code(transaction_type: &str) -> Option<&i16> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_transaction_type_code(transaction_type) +} + +/// Return an integer representing the given +/// transaction type string in an enum. +pub fn get_transaction_type_name(transaction_type: &i16) -> Option<&String> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_transaction_type_name(transaction_type) +} + +/// Return an integer representing the given +/// transaction result string in an enum. +pub fn get_transaction_result_code(transaction_result_type: &str) -> Option<&i16> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_transaction_result_code(transaction_result_type) +} + +/// Return string representing the given transaction +/// result type from the enum. +pub fn get_transaction_result_name(transaction_result_type: &i16) -> Option<&String> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_transaction_result_name(transaction_result_type) +} + +/// Return an integer representing the given ledger +/// entry type string in an enum. +pub fn get_ledger_entry_type_code(ledger_entry_type: &str) -> Option<&i16> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_ledger_entry_type_code(ledger_entry_type) +} + +/// Return an integer representing the given ledger +/// entry type string in an enum. +pub fn get_ledger_entry_type_name(ledger_entry_type: &i16) -> Option<&String> { + let definition_map: &DefinitionMap = load_definition_map(); + definition_map.get_ledger_entry_type_name(ledger_entry_type) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_load_definitions() { + assert!(!_load_definitions().is_none()); + } + + #[test] + fn test_get_field_type_name() { + assert_eq!( + get_field_type_name("HighLimit"), + Some(&"Amount".to_string()) + ); + } + + #[test] + fn test_get_field_type_code() { + assert_eq!(get_field_type_code("HighLimit"), Some(&6)); + assert_eq!(get_field_type_code("Generic"), Some(&-2)); + } + + #[test] + fn test_get_field_code() { + assert_eq!(get_field_code("HighLimit"), Some(7)); + assert_eq!(get_field_code("Generic"), Some(0)); + assert_eq!(get_field_code("Invalid"), Some(-1)); + assert!(get_field_code("Nonexistent").is_none()); + } + + #[test] + fn test_get_field_header_from_name() { + let field_header = get_field_header_from_name("Generic").unwrap(); + + assert_eq!(-2, field_header.type_code); + assert_eq!(0, field_header.field_code); + } + + #[test] + fn test_get_field_name_from_header() { + let field_header = FieldHeader { + type_code: -2, + field_code: 0, + }; + + assert_eq!( + get_field_name_from_header(&field_header), + Some(&"Generic".to_string()) + ); + } + + #[test] + fn test_get_field_instance() { + let field_header = FieldHeader { + type_code: -2, + field_code: 0, + }; + + let field_info = FieldInfo { + nth: 0, + is_vl_encoded: false, + is_serialized: false, + is_signing_field: false, + r#type: "Unknown".to_string(), + }; + + let field_instance = FieldInstance::new(&field_info, "Generic", field_header); + let test_field_instance = get_field_instance("Generic"); + + assert!(!test_field_instance.is_none()); + + let test_field_instance = test_field_instance.unwrap(); + + assert_eq!( + field_instance.header.type_code, + test_field_instance.header.type_code + ); + } + + #[test] + fn test_get_transaction_type_code() { + assert_eq!(get_transaction_type_code("Invalid"), Some(&-1)); + assert_eq!(get_transaction_type_code("OfferCancel"), Some(&8)); + assert!(get_transaction_type_code("Nonexistent").is_none()); + } + + #[test] + fn test_get_transaction_type_name() { + assert_eq!(get_transaction_type_name(&-1), Some(&"Invalid".to_string())); + assert_eq!(get_transaction_type_name(&0), Some(&"Payment".to_string())); + assert!(get_transaction_type_name(&9000).is_none()); + } + + #[test] + fn test_get_transaction_result_code() { + assert_eq!(get_transaction_result_code("telLOCAL_ERROR"), Some(&-399)); + assert_eq!( + get_transaction_result_code("temCANNOT_PREAUTH_SELF"), + Some(&-267) + ); + assert!(get_transaction_result_code("Nonexistent").is_none()); + } + + #[test] + fn test_get_transaction_result_name() { + assert_eq!( + get_transaction_result_name(&-399), + Some(&"telLOCAL_ERROR".to_string()) + ); + assert_eq!( + get_transaction_result_name(&-267), + Some(&"temCANNOT_PREAUTH_SELF".to_string()), + ); + assert!(get_transaction_result_name(&9000).is_none()); + } + + #[test] + fn test_get_ledger_entry_type_code() { + assert_eq!(get_ledger_entry_type_code("Any"), Some(&-3)); + assert_eq!(get_ledger_entry_type_code("DepositPreauth"), Some(&112)); + assert!(get_ledger_entry_type_code("Nonexistent").is_none()); + } + + #[test] + fn test_get_ledger_entry_type_name() { + assert_eq!(get_ledger_entry_type_name(&-3), Some(&"Any".to_string())); + assert_eq!( + get_ledger_entry_type_name(&112), + Some(&"DepositPreauth".to_string()) + ); + assert!(get_ledger_entry_type_name(&9000).is_none()); + } +} diff --git a/src/core/keypairs/algorithms.rs b/src/core/keypairs/algorithms.rs new file mode 100644 index 00000000..ec3ca39b --- /dev/null +++ b/src/core/keypairs/algorithms.rs @@ -0,0 +1,535 @@ +//! Ed25519 elliptic curve cryptography interface. +//! SECP256K1 elliptic curve cryptography interface. +//! +//! Note: The process for using SECP256k1 is complex and +//! more involved than ED25519. +//! +//! See SECP256K1 Key Derivation: +//! `` + +use crate::constants::CryptoAlgorithm; +use crate::core::keypairs::exceptions::XRPLKeypairsException; +use crate::core::keypairs::utils::*; +use crate::core::keypairs::CryptoImplementation; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; +use core::convert::TryInto; +use core::str::FromStr; +use ed25519_dalek::Verifier; +use num_bigint::BigUint; +use rust_decimal::prelude::One; + +/// Methods for using the ECDSA cryptographic system with +/// the SECP256K1 elliptic curve. +pub struct Secp256k1; + +/// Methods for using the ED25519 cryptographic system. +pub struct Ed25519; + +impl Secp256k1 { + /// Hex encode the private key. + fn _private_key_to_str(key: secp256k1::SecretKey) -> String { + hex::encode_upper(key.as_ref()) + } + + /// Hex encode the public key. + fn _public_key_to_str(key: secp256k1::PublicKey) -> String { + hex::encode_upper(key.serialize()) + } + + /// Format a provided key. + fn _format_key(keystr: &str) -> String { + format!("{:0>pad$}", keystr, pad = SECP256K1_KEY_LENGTH) + } + + /// Format the public and private keys. + fn _format_keys( + public: secp256k1::PublicKey, + private: secp256k1::SecretKey, + ) -> (String, String) { + ( + Secp256k1::_format_key(&Secp256k1::_public_key_to_str(public)), + Secp256k1::_format_key(&Secp256k1::_private_key_to_str(private)), + ) + } + + /// Hash the message to prevent insecure signing. + fn _get_message(message: &[u8]) -> Result { + secp256k1::Message::from_slice(&sha512_first_half(message)) + } + + /// Determing if the provided secret key is valid. + fn _is_secret_valid(key: &[u8]) -> bool { + let key_bytes = BigUint::from_bytes_be(key); + key_bytes >= BigUint::one() + && key_bytes <= BigUint::from_bytes_be(&secp256k1::constants::CURVE_ORDER) + } + + /// Concat candidate key. + fn _candidate_merger(input: &[u8], candidate: &[u8], phase: &Secp256k1Phase) -> Vec { + if phase == &Secp256k1Phase::Root { + [input, candidate].concat() + } else { + [input, &SECP256K1_INTERMEDIATE_KEYPAIR_PADDING, candidate].concat() + } + } + + /// Given bytes_input determine public/private keypair + /// for a given phase of this algorithm. The difference + /// between generating the root and intermediate keypairs + /// is just what bytes are input by the caller and that + /// the intermediate keypair needs to inject + /// SECP256K1_INTERMEDIATE_KEYPAIR_PADDING into the value + /// to hash to get the raw private key. + fn _derive_part( + bytes: &[u8], + phase: Secp256k1Phase, + ) -> Result<(secp256k1::PublicKey, secp256k1::SecretKey), XRPLKeypairsException> { + let raw_private = Self::_get_secret(bytes, &phase)?; + let secp = secp256k1::Secp256k1::new(); + let wrapped_private = secp256k1::SecretKey::from_slice(&raw_private)?; + let wrapped_public = secp256k1::PublicKey::from_secret_key(&secp, &wrapped_private); + + Ok((wrapped_public, wrapped_private)) + } + + /// Derive the final public/private keys. + fn _derive_final( + root_public: secp256k1::PublicKey, + root_private: secp256k1::SecretKey, + mid_public: secp256k1::PublicKey, + mid_private: secp256k1::SecretKey, + ) -> Result<(secp256k1::PublicKey, secp256k1::SecretKey), XRPLKeypairsException> { + let mut wrapped_private = root_private; + let wrapped_public = root_public.combine(&mid_public)?; + + wrapped_private.add_assign(mid_private.as_ref())?; + Ok((wrapped_public, wrapped_private)) + } + + /// Given a function `candidate_merger` that knows how + /// to prepare a sequence candidate bytestring into a + /// possible full candidate secret, returns the first + /// sequence value that is valid. If none are valid, + /// raises; however this should be so exceedingly rare + /// as to ignore. + fn _get_secret( + input: &[u8], + phase: &Secp256k1Phase, + ) -> Result<[u8; SHA512_HASH_LENGTH], XRPLKeypairsException> { + for raw_root in 0..SECP256K1_SEQUENCE_MAX { + let root = (raw_root as u32).to_be_bytes(); + let candidate = sha512_first_half(&Self::_candidate_merger(input, &root, phase)); + + if Self::_is_secret_valid(&candidate) { + return Ok(candidate); + } else { + continue; + } + } + + Err(XRPLKeypairsException::InvalidSecret) + } +} + +impl Ed25519 { + /// Hex encode the private key. + fn _private_key_to_str(key: ed25519_dalek::SecretKey) -> String { + hex::encode(key) + } + + /// Hex encode the public key. + fn _public_key_to_str(key: ed25519_dalek::PublicKey) -> String { + hex::encode(key.as_ref()) + } + + /// Format a provided key. + fn _format_key(keystr: &str) -> String { + format!("{}{}", ED25519_PREFIX, keystr.to_uppercase()) + } + + /// Format the public and private keys. + fn _format_keys( + public: ed25519_dalek::PublicKey, + private: ed25519_dalek::SecretKey, + ) -> (String, String) { + ( + Ed25519::_format_key(&Ed25519::_public_key_to_str(public)), + Ed25519::_format_key(&Ed25519::_private_key_to_str(private)), + ) + } +} + +impl CryptoImplementation for Secp256k1 { + /// Derives a key pair for use with the XRP Ledger + /// from a seed value. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::keypairs::Secp256k1; + /// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; + /// use xrpl::core::keypairs::CryptoImplementation; + /// + /// let decoded_seed: &[u8] = &[ + /// 207, 45, 227, 120, 251, 221, 126, 46, + /// 232, 125, 72, 109, 251, 90, 123, 255 + /// ]; + /// let validator: bool = false; + /// let tuple: (String, String) = ( + /// "0203F2D90BC50012EC7CB20B07A1B818D6863636FB1E945D17449092CFB5495E1E".into(), + /// "0048D93A3B5948E5F9B323BF654BFAD6E8FF75B5FCAB03C5A55AD30CB2515B461F".into(), + /// ); + /// + /// let derivation: Option<(String, String)> = match Secp256k1.derive_keypair( + /// decoded_seed, + /// validator, + /// ) { + /// Ok((public, private)) => Some((public, private)), + /// Err(e) => match e { + /// XRPLKeypairsException::InvalidSignature => None, + /// XRPLKeypairsException::InvalidSecret => None, + /// XRPLKeypairsException::SECP256K1Error => None, + /// _ => None, + /// }, + /// }; + /// + /// assert_eq!(Some(tuple), derivation); + /// ``` + fn derive_keypair( + &self, + decoded_seed: &[u8], + is_validator: bool, + ) -> Result<(String, String), XRPLKeypairsException> { + let (root_public, root_secret) = Self::_derive_part(decoded_seed, Secp256k1Phase::Root)?; + if is_validator { + Ok(Secp256k1::_format_keys(root_public, root_secret)) + } else { + let (mid_public, mid_secret) = + Self::_derive_part(&root_public.serialize(), Secp256k1Phase::Mid)?; + let (final_public, final_secret) = + Self::_derive_final(root_public, root_secret, mid_public, mid_secret)?; + + Ok(Secp256k1::_format_keys(final_public, final_secret)) + } + } + + /// Signs a message using a given private key. + /// * `message` - Text about foo. + /// * `private_key` - Text about bar. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::keypairs::Secp256k1; + /// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; + /// use xrpl::core::keypairs::CryptoImplementation; + /// + /// let message: &[u8] = "test message".as_bytes(); + /// let private_key: &str = "00D78B9735C3F26501C7337B8A5727FD5\ + /// 3A6EFDBC6AA55984F098488561F985E23"; + /// let signature: Vec = vec![ + /// 48, 68, 2, 32, 88, 58, 145, 201, 94, 84, 230, 166, 81, 196, + /// 123, 236, 34, 116, 78, 11, 16, 30, 44, 64, 96, 231, 176, 143, + /// 99, 65, 101, 125, 173, 155, 195, 238, 2, 32, 125, 20, 137, + /// 199, 57, 93, 176, 24, 141, 58, 86, 169, 119, 236, 186, 84, + /// 179, 111, 169, 55, 27, 64, 49, 150, 85, 177, 180, 66, 158, + /// 51, 239, 45, + /// ]; + /// + /// let signing: Option> = match Secp256k1.sign( + /// message, + /// private_key, + /// ) { + /// Ok(signature) => Some(signature), + /// Err(e) => match e { + /// XRPLKeypairsException::SECP256K1Error => None, + /// _ => None, + /// }, + /// }; + /// + /// assert_eq!(Some(signature), signing); + /// ``` + fn sign( + &self, + message_bytes: &[u8], + private_key: &str, + ) -> Result, XRPLKeypairsException> { + let secp = secp256k1::Secp256k1::::signing_only(); + let message = Self::_get_message(message_bytes)?; + let trimmed_key = private_key.trim_start_matches(SECP256K1_PREFIX); + let private = secp256k1::SecretKey::from_str(trimmed_key)?; + let signature = secp.sign(&message, &private); + + Ok(signature.serialize_der().to_vec()) + } + + /// Verifies the signature on a given message. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::keypairs::Secp256k1; + /// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; + /// use xrpl::core::keypairs::CryptoImplementation; + /// + /// let message: &[u8] = "test message".as_bytes(); + /// let signature: &str = "30440220583A91C95E54E6A651C47BEC\ + /// 22744E0B101E2C4060E7B08F6341657D\ + /// AD9BC3EE02207D1489C7395DB0188D3A\ + /// 56A977ECBA54B36FA9371B40319655B1\ + /// B4429E33EF2D"; + /// let public_key: &str = "030D58EB48B4420B1F7B9DF55087E0E\ + /// 29FEF0E8468F9A6825B01CA2C361042D435"; + /// + /// assert!(Secp256k1.is_valid_message( + /// message, + /// signature, + /// public_key, + /// )); + /// ``` + fn is_valid_message(&self, message_bytes: &[u8], signature: &str, public_key: &str) -> bool { + let secp = secp256k1::Secp256k1::::verification_only(); + let msg = Self::_get_message(message_bytes); + + if let Ok(value) = hex::decode(signature) { + let sig = secp256k1::Signature::from_der(&value); + let public = secp256k1::PublicKey::from_str(public_key); + + if let (&Ok(m), &Ok(s), &Ok(p)) = (&msg.as_ref(), &sig.as_ref(), &public.as_ref()) { + secp.verify(m, s, p).is_ok() + } else { + false + } + } else { + false + } + } +} + +impl CryptoImplementation for Ed25519 { + /// Derives a key pair for use with the XRP Ledger + /// from a seed value. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::keypairs::Ed25519; + /// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; + /// use xrpl::core::keypairs::CryptoImplementation; + /// + /// let decoded_seed: &[u8] = &[ + /// 207, 45, 227, 120, 251, 221, 126, 46, + /// 232, 125, 72, 109, 251, 90, 123, 255 + /// ]; + /// let validator: bool = false; + /// let tuple: (String, String) = ( + /// "ED60292139838CB86E719134F848F055057CA5BDA61F5A529729F1697502D53E1C".into(), + /// "ED009F66528611A0D400946A01FA01F8AF4FF4C1D0C744AE3F193317DCA77598F1".into(), + /// ); + /// + /// let derivation: Option<(String, String)> = match Ed25519.derive_keypair( + /// decoded_seed, + /// validator, + /// ) { + /// Ok((public, private)) => Some((public, private)), + /// Err(e) => match e { + /// XRPLKeypairsException::InvalidSignature => None, + /// XRPLKeypairsException::ED25519Error => None, + /// XRPLKeypairsException::UnsupportedValidatorAlgorithm { expected: _ } => None, + /// _ => None, + /// }, + /// }; + /// + /// assert_eq!(Some(tuple), derivation); + /// ``` + fn derive_keypair( + &self, + decoded_seed: &[u8], + is_validator: bool, + ) -> Result<(String, String), XRPLKeypairsException> { + if is_validator { + Err(XRPLKeypairsException::UnsupportedValidatorAlgorithm { + expected: CryptoAlgorithm::ED25519, + }) + } else { + let raw_private = sha512_first_half(decoded_seed); + let private = ed25519_dalek::SecretKey::from_bytes(&raw_private)?; + let public = ed25519_dalek::PublicKey::from(&private); + + Ok(Ed25519::_format_keys(public, private)) + } + } + + /// Signs a message using a given private key. + /// * `message` - Text about foo. + /// * `private_key` - Text about bar. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::keypairs::Ed25519; + /// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; + /// use xrpl::core::keypairs::CryptoImplementation; + /// + /// let message: &[u8] = "test message".as_bytes(); + /// let private_key: &str = "EDB4C4E046826BD26190D09715FC31F4E\ + /// 6A728204EADD112905B08B14B7F15C4F3"; + /// let signature: Vec = vec![ + /// 203, 25, 158, 27, 253, 78, 61, 170, 16, 94, 72, 50, 238, 223, + /// 163, 100, 19, 225, 244, 66, 5,228, 239, 185, 226, 126, 130, 96, + /// 68, 194, 30, 62, 46, 132, 139, 188, 129, 149, 232, 149, 155, + /// 173, 248, 135, 89, 155, 115, 16, 173, 27, 112, 71, 239, 17, + /// 182, 130, 224, 208, 104, 247, 55,73, 117, 14, + /// ]; + /// + /// let signing: Option> = match Ed25519.sign( + /// message, + /// private_key, + /// ) { + /// Ok(signature) => Some(signature), + /// Err(e) => match e { + /// XRPLKeypairsException::ED25519Error => None, + /// _ => None, + /// }, + /// }; + /// + /// assert_eq!(Some(signature), signing); + /// ``` + fn sign(&self, message: &[u8], private_key: &str) -> Result, XRPLKeypairsException> { + let raw_private = hex::decode(&private_key[ED25519_PREFIX.len()..])?; + let private = ed25519_dalek::SecretKey::from_bytes(&raw_private)?; + let expanded_private = ed25519_dalek::ExpandedSecretKey::from(&private); + let public = ed25519_dalek::PublicKey::from(&private); + let signature: ed25519_dalek::Signature = expanded_private.sign(message, &public); + + Ok(signature.to_bytes().to_vec()) + } + + /// Verifies the signature on a given message. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::keypairs::Ed25519; + /// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; + /// use xrpl::core::keypairs::CryptoImplementation; + /// + /// let message: &[u8] = "test message".as_bytes(); + /// let signature: &str = "CB199E1BFD4E3DAA105E4832EEDFA3641\ + /// 3E1F44205E4EFB9E27E826044C21E3E2E\ + /// 848BBC8195E8959BADF887599B7310AD1\ + /// B7047EF11B682E0D068F73749750E"; + /// let public_key: &str = "ED01FA53FA5A7E77798F882ECE20B1AB\ + /// C00BB358A9E55A202D0D0676BD0CE37A63"; + /// + /// assert!(Ed25519.is_valid_message( + /// message, + /// signature, + /// public_key, + /// )); + /// ``` + fn is_valid_message(&self, message: &[u8], signature: &str, public_key: &str) -> bool { + let raw_public = hex::decode(&public_key[ED25519_PREFIX.len()..]); + let decoded_sig = hex::decode(signature); + + if raw_public.is_err() || decoded_sig.is_err() { + return false; + }; + + if let (Ok(rpub), Ok(dsig)) = (raw_public, decoded_sig) { + let public = ed25519_dalek::PublicKey::from_bytes(&rpub); + + if dsig.len() != ED25519_SIGNATURE_LENGTH { + return false; + }; + + if let Ok(value) = public { + let sig: [u8; ED25519_SIGNATURE_LENGTH] = + dsig.try_into().expect("is_valid_message"); + let converted = &ed25519_dalek::Signature::from(sig); + + value.verify(message, converted).is_ok() + } else { + false + } + } else { + false + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::core::keypairs::test_cases::*; + + #[test] + fn test_secp256k1_derive_keypair() { + let seed: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + let validator = Secp256k1.derive_keypair(seed, true); + let (public, private) = Secp256k1.derive_keypair(seed, false).unwrap(); + + assert!(validator.is_ok()); + assert_eq!(PRIVATE_SECP256K1, private); + assert_eq!(PUBLIC_SECP256K1, public); + } + + #[test] + fn test_secp256k1_sign() { + let success = Secp256k1.sign(TEST_MESSAGE.as_bytes(), PRIVATE_SECP256K1); + let error = Secp256k1.sign(TEST_MESSAGE.as_bytes(), "abc123"); + + assert!(success.is_ok()); + assert!(error.is_err()); + } + + #[test] + fn test_secp256k1_is_valid_message() { + let signature: &str = &hex::encode_upper(SIGNATURE_SECP256K1); + let message: &[u8] = TEST_MESSAGE.as_bytes(); + + assert!(Secp256k1.is_valid_message(message, signature, PUBLIC_SECP256K1)); + } + + #[test] + fn test_ed25519_derive_keypair() { + let seed: &[u8] = SEED_ED25519.as_bytes(); + let validator = Ed25519.derive_keypair(seed, true); + let (public, private) = Ed25519.derive_keypair(seed, false).unwrap(); + + assert!(validator.is_err()); + assert_eq!(RAW_PRIVATE_ED25519, public); + assert_eq!(RAW_PUBLIC_ED25519, private); + } + + #[test] + fn test_ed25519_sign() { + let success = Ed25519.sign(TEST_MESSAGE.as_bytes(), RAW_PRIVATE_ED25519); + let error = Ed25519.sign(TEST_MESSAGE.as_bytes(), "abc123"); + + assert!(success.is_ok()); + assert!(error.is_err()); + } + + #[test] + fn test_ed25519_is_valid_message() { + let signature: &str = &hex::encode_upper(SIGNATURE_ED25519); + let message: &[u8] = TEST_MESSAGE.as_bytes(); + + assert!(Ed25519.is_valid_message(message, signature, PUBLIC_ED25519)); + } +} diff --git a/src/core/keypairs/exceptions.rs b/src/core/keypairs/exceptions.rs new file mode 100644 index 00000000..60a39249 --- /dev/null +++ b/src/core/keypairs/exceptions.rs @@ -0,0 +1,49 @@ +//! XRPL keypair codec exceptions. + +use crate::constants::CryptoAlgorithm; +use crate::core::addresscodec::exceptions::XRPLAddressCodecException; + +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum XRPLKeypairsException { + InvalidSignature, + InvalidSecret, + UnsupportedValidatorAlgorithm { expected: CryptoAlgorithm }, + ED25519Error, + SECP256K1Error, + FromHexError, + AddressCodecException(XRPLAddressCodecException), +} + +impl From for XRPLKeypairsException { + fn from(err: XRPLAddressCodecException) -> Self { + XRPLKeypairsException::AddressCodecException(err) + } +} + +impl From for XRPLKeypairsException { + fn from(_: ed25519_dalek::ed25519::Error) -> Self { + XRPLKeypairsException::ED25519Error + } +} + +impl From for XRPLKeypairsException { + fn from(_: secp256k1::Error) -> Self { + XRPLKeypairsException::SECP256K1Error + } +} + +impl From for XRPLKeypairsException { + fn from(_: hex::FromHexError) -> Self { + XRPLKeypairsException::FromHexError + } +} + +impl core::fmt::Display for XRPLKeypairsException { + fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + write!(f, "XRPLKeypairsException: {:?}", self) + } +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLKeypairsException {} diff --git a/src/core/keypairs/mod.rs b/src/core/keypairs/mod.rs new file mode 100644 index 00000000..19815b32 --- /dev/null +++ b/src/core/keypairs/mod.rs @@ -0,0 +1,350 @@ +//! Core codec functions for interacting with the XRPL. + +pub mod algorithms; +pub mod exceptions; +#[cfg(test)] +pub(crate) mod test_cases; +pub mod utils; + +pub use self::algorithms::Ed25519; +pub use self::algorithms::Secp256k1; + +use crate::constants::CryptoAlgorithm; +use crate::core::addresscodec::exceptions::XRPLAddressCodecException; +use crate::core::addresscodec::utils::SEED_LENGTH; +use crate::core::addresscodec::*; +use crate::core::keypairs::exceptions::XRPLKeypairsException; +use crate::core::keypairs::utils::*; +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use rand::Rng; +use rand::SeedableRng; + +/// Return the signature length for an algorithm. +const fn _get_algorithm_sig_length(algo: CryptoAlgorithm) -> usize { + match algo { + CryptoAlgorithm::ED25519 => ED25519_SIGNATURE_LENGTH, + CryptoAlgorithm::SECP256K1 => SECP256K1_SIGNATURE_LENGTH, + } +} + +/// Return the CryptoAlgorithm from a key. +fn _get_algorithm_from_key(key: &str) -> CryptoAlgorithm { + match &key[..2] { + ED25519_PREFIX => CryptoAlgorithm::ED25519, + _ => CryptoAlgorithm::SECP256K1, + } +} + +/// Return the trait implementation for the provided +/// algorithm enum. +fn _get_algorithm_engine(algo: CryptoAlgorithm) -> Box { + match algo { + CryptoAlgorithm::ED25519 => Box::new(Ed25519), + CryptoAlgorithm::SECP256K1 => Box::new(Secp256k1), + } +} + +/// Return the trait implementation based on the +/// provided key. +fn _get_algorithm_engine_from_key(key: &str) -> Box { + match &key[..2] { + ED25519_PREFIX => _get_algorithm_engine(CryptoAlgorithm::ED25519), + _ => _get_algorithm_engine(CryptoAlgorithm::SECP256K1), + } +} + +/// Generate a seed value that cryptographic keys +/// can be derived from. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::keypairs::generate_seed; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// use xrpl::constants::CryptoAlgorithm; +/// use xrpl::core::addresscodec::utils::SEED_LENGTH; +/// +/// let entropy: Option<[u8; SEED_LENGTH]> = Some([ +/// 207, 45, 227, 120, 251, 221, 126, 46, +/// 232, 125, 72, 109, 251, 90, 123, 255 +/// ]); +/// let algorithm: Option = Some(CryptoAlgorithm::SECP256K1); +/// let seed: String = "sn259rEFXrQrWyx3Q7XneWcwV6dfL".into(); +/// +/// let generator: Option = match generate_seed( +/// entropy, +/// algorithm, +/// ) { +/// Ok(seed) => Some(seed), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnknownSeedEncoding => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(seed), generator); +/// ``` +pub fn generate_seed( + entropy: Option<[u8; SEED_LENGTH]>, + algorithm: Option, +) -> Result { + let mut random_bytes: [u8; SEED_LENGTH] = [0u8; SEED_LENGTH]; + let algo: CryptoAlgorithm; + + if let Some(value) = algorithm { + algo = value; + } else { + algo = CryptoAlgorithm::ED25519; + } + + if let Some(value) = entropy { + random_bytes = value; + } else { + let mut rng = rand_hc::Hc128Rng::from_entropy(); + rng.fill(&mut random_bytes); + } + + encode_seed(random_bytes, algo) +} + +/// Derive the public and private keys from a given seed value. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::keypairs::derive_keypair; +/// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; +/// +/// let seed: &str = "sEdSKaCy2JT7JaM7v95H9SxkhP9wS2r"; +/// let validator: bool = false; +/// let tuple: (String, String) = ( +/// "ED01FA53FA5A7E77798F882ECE20B1ABC00BB358A9E55A202D0D0676BD0CE37A63".into(), +/// "EDB4C4E046826BD26190D09715FC31F4E6A728204EADD112905B08B14B7F15C4F3".into(), +/// ); +/// +/// let generator: Option<(String, String)> = match derive_keypair( +/// seed, +/// validator, +/// ) { +/// Ok(seed) => Some(seed), +/// Err(e) => match e { +/// XRPLKeypairsException::InvalidSignature => None, +/// XRPLKeypairsException::ED25519Error => None, +/// XRPLKeypairsException::SECP256K1Error => None, +/// XRPLKeypairsException::UnsupportedValidatorAlgorithm { expected: _ } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(tuple), generator); +/// ``` +pub fn derive_keypair( + seed: &str, + validator: bool, +) -> Result<(String, String), XRPLKeypairsException> { + let (decoded_seed, algorithm) = decode_seed(seed)?; + let module = _get_algorithm_engine(algorithm); + let (public, private) = module.derive_keypair(&decoded_seed, validator)?; + let signature = sign(SIGNATURE_VERIFICATION_MESSAGE, &private)?; + + if module.is_valid_message(SIGNATURE_VERIFICATION_MESSAGE, &signature, &public) { + Ok((public, private)) + } else { + Err(XRPLKeypairsException::InvalidSignature) + } +} + +/// Derive the XRP Ledger classic address for a given +/// public key. For more information, see Address Derivation. +/// +/// Account ID and Address: +/// `` +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::keypairs::derive_classic_address; +/// use xrpl::core::addresscodec::exceptions::XRPLAddressCodecException; +/// +/// let public_key: &str = "ED01FA53FA5A7E77798F882ECE20B1ABC00\ +/// BB358A9E55A202D0D0676BD0CE37A63"; +/// let address: String = "rLUEXYuLiQptky37CqLcm9USQpPiz5rkpD".into(); +/// +/// let derivation: Option = match derive_classic_address(public_key) { +/// Ok(address) => Some(address), +/// Err(e) => match e { +/// XRPLAddressCodecException::UnexpectedPayloadLength { +/// expected: _, +/// found: _, +/// } => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(address), derivation); +/// ``` +pub fn derive_classic_address(public_key: &str) -> Result { + let account_id = get_account_id(&hex::decode(public_key)?); + encode_classic_address(&account_id) +} + +/// Sign a message using a given private key. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::keypairs::sign; +/// use xrpl::core::keypairs::exceptions::XRPLKeypairsException; +/// +/// let message: &[u8] = "test message".as_bytes(); +/// let private_key: &str = "EDB4C4E046826BD26190D09715FC31F4E\ +/// 6A728204EADD112905B08B14B7F15C4F3"; +/// let signature: String = "CB199E1BFD4E3DAA105E4832EEDFA36413E1F44205E4EFB9\ +/// E27E826044C21E3E2E848BBC8195E8959BADF887599B7310\ +/// AD1B7047EF11B682E0D068F73749750E".into(); +/// +/// let signing: Option = match sign( +/// message, +/// private_key, +/// ) { +/// Ok(signature) => Some(signature), +/// Err(e) => match e { +/// XRPLKeypairsException::ED25519Error => None, +/// XRPLKeypairsException::SECP256K1Error => None, +/// _ => None, +/// } +/// }; +/// +/// assert_eq!(Some(signature), signing); +/// ``` +pub fn sign(message: &[u8], private_key: &str) -> Result { + let module = _get_algorithm_engine_from_key(private_key); + Ok(hex::encode_upper(module.sign(message, private_key)?)) +} + +/// Verifies the signature on a given message. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::keypairs::is_valid_message; +/// +/// let message: &[u8] = "test message".as_bytes(); +/// let signature: &str = "CB199E1BFD4E3DAA105E4832EEDFA36413E1F44205E4EFB9\ +/// E27E826044C21E3E2E848BBC8195E8959BADF887599B7310\ +/// AD1B7047EF11B682E0D068F73749750E"; +/// let public_key: &str = "ED01FA53FA5A7E77798F882ECE20B1ABC00\ +/// BB358A9E55A202D0D0676BD0CE37A63"; +/// +/// assert!(is_valid_message( +/// message, +/// signature, +/// public_key, +/// )); +/// ``` +pub fn is_valid_message(message: &[u8], signature: &str, public_key: &str) -> bool { + let module = _get_algorithm_engine_from_key(public_key); + module.is_valid_message(message, signature, public_key) +} + +/// Trait for cryptographic algorithms in the XRP Ledger. +/// The classes for all cryptographic algorithms are +/// derived from this trait. +pub trait CryptoImplementation { + /// Derives a key pair for use with the XRP Ledger + /// from a seed value. + fn derive_keypair( + &self, + decoded_seed: &[u8], + is_validator: bool, + ) -> Result<(String, String), XRPLKeypairsException>; + + /// Signs a message using a given private key. + /// * `message` - Text about foo. + /// * `private_key` - Text about bar. + fn sign(&self, message: &[u8], private_key: &str) -> Result, XRPLKeypairsException>; + + /// Verifies the signature on a given message. + fn is_valid_message(&self, message: &[u8], signature: &str, public_key: &str) -> bool; +} + +#[cfg(test)] +mod test { + use super::*; + use crate::alloc::string::ToString; + use crate::constants::CryptoAlgorithm; + use crate::core::keypairs::test_cases::*; + + #[test] + fn test_generate_seed() { + assert!(generate_seed(None, None).is_ok()); + assert!(generate_seed(Some(TEST_BYTES), None).is_ok()); + assert_eq!( + generate_seed(Some(TEST_BYTES), Some(CryptoAlgorithm::ED25519)), + Ok(SEED_ED25519.to_string()), + ); + assert_eq!( + generate_seed(Some(TEST_BYTES), Some(CryptoAlgorithm::SECP256K1)), + Ok(SEED_SECP256K1.to_string()), + ); + } + + #[test] + fn test_derive_keypair() { + let (public_ed25519, private_ed25519) = derive_keypair(SEED_ED25519, false).unwrap(); + let (public_secp256k1, private_secp256k1) = derive_keypair(SEED_SECP256K1, false).unwrap(); + + assert_eq!(PRIVATE_ED25519, private_ed25519); + assert_eq!(PUBLIC_ED25519, public_ed25519); + assert_eq!(PRIVATE_SECP256K1, private_secp256k1); + assert_eq!(PUBLIC_SECP256K1, public_secp256k1); + } + + #[test] + fn test_derive_classic_address() { + assert_eq!( + derive_classic_address(PUBLIC_ED25519), + Ok(CLASSIC_ADDRESS_ED25519.to_string()), + ); + + assert_eq!( + derive_classic_address(PUBLIC_SECP256K1), + Ok(CLASSIC_ADDRESS_SECP256K1.to_string()), + ); + } + + #[test] + fn test_sign() { + assert_eq!( + sign(TEST_MESSAGE.as_bytes(), PRIVATE_ED25519), + Ok(hex::encode_upper(SIGNATURE_ED25519)), + ); + + assert_eq!( + sign(TEST_MESSAGE.as_bytes(), PRIVATE_SECP256K1), + Ok(hex::encode_upper(SIGNATURE_SECP256K1)), + ); + } + + #[test] + fn test_is_valid_message() { + let message: &[u8] = TEST_MESSAGE.as_bytes(); + let sig_ed25519: &str = &hex::encode_upper(SIGNATURE_ED25519); + let sig_secp256k1: &str = &hex::encode_upper(SIGNATURE_SECP256K1); + + assert!(is_valid_message(message, sig_ed25519, PUBLIC_ED25519)); + assert!(is_valid_message(message, sig_secp256k1, PUBLIC_SECP256K1)); + } +} diff --git a/src/core/keypairs/test_cases.rs b/src/core/keypairs/test_cases.rs new file mode 100644 index 00000000..dfa0a860 --- /dev/null +++ b/src/core/keypairs/test_cases.rs @@ -0,0 +1,53 @@ +use crate::constants::ACCOUNT_ID_LENGTH; +use crate::core::addresscodec::utils::SEED_LENGTH; +use crate::core::keypairs::utils::SHA512_HASH_LENGTH; +use crate::core::keypairs::ED25519_SIGNATURE_LENGTH; + +pub const TEST_MESSAGE: &str = "test message"; + +pub const TEST_MESSAGE_SHA: [u8; SHA512_HASH_LENGTH] = [ + 149, 11, 42, 126, 255, 167, 143, 81, 166, 53, 21, 236, 69, 224, 62, 206, 190, 80, 239, 47, 28, + 65, 230, 150, 41, 181, 7, 120, 241, 27, 192, 128, +]; + +pub const TEST_ACCOUNT_ID: [u8; ACCOUNT_ID_LENGTH] = [ + 159, 127, 79, 53, 164, 208, 121, 119, 65, 32, 123, 166, 113, 156, 97, 173, 156, 182, 36, 249, +]; + +pub const SEED_ED25519: &str = "sEdSKaCy2JT7JaM7v95H9SxkhP9wS2r"; +pub const SEED_SECP256K1: &str = "sp5fghtJtpUorTwvof1NpDXAzNwf5"; + +pub const TEST_BYTES: [u8; SEED_LENGTH] = + *b"\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10"; + +pub const RAW_PUBLIC_ED25519: &str = + "EDBB664A14F510A366404BC4352A2230A5608364B3D51105C39D7B652DDEAD3ED3"; +pub const RAW_PRIVATE_ED25519: &str = + "ED08C948F6D4ACCBAD74DD560538F86D400BC610CFA74566E87561AB63FB72DD0F"; + +pub const PUBLIC_ED25519: &str = + "ED01FA53FA5A7E77798F882ECE20B1ABC00BB358A9E55A202D0D0676BD0CE37A63"; +pub const PRIVATE_ED25519: &str = + "EDB4C4E046826BD26190D09715FC31F4E6A728204EADD112905B08B14B7F15C4F3"; + +pub const PUBLIC_SECP256K1: &str = + "030D58EB48B4420B1F7B9DF55087E0E29FEF0E8468F9A6825B01CA2C361042D435"; +pub const PRIVATE_SECP256K1: &str = + "00D78B9735C3F26501C7337B8A5727FD53A6EFDBC6AA55984F098488561F985E23"; + +pub const SIGNATURE_ED25519: [u8; ED25519_SIGNATURE_LENGTH] = [ + 203, 25, 158, 27, 253, 78, 61, 170, 16, 94, 72, 50, 238, 223, 163, 100, 19, 225, 244, 66, 5, + 228, 239, 185, 226, 126, 130, 96, 68, 194, 30, 62, 46, 132, 139, 188, 129, 149, 232, 149, 155, + 173, 248, 135, 89, 155, 115, 16, 173, 27, 112, 71, 239, 17, 182, 130, 224, 208, 104, 247, 55, + 73, 117, 14, +]; + +pub const SIGNATURE_SECP256K1: [u8; 70] = [ + 48, 68, 2, 32, 88, 58, 145, 201, 94, 84, 230, 166, 81, 196, 123, 236, 34, 116, 78, 11, 16, 30, + 44, 64, 96, 231, 176, 143, 99, 65, 101, 125, 173, 155, 195, 238, 2, 32, 125, 20, 137, 199, 57, + 93, 176, 24, 141, 58, 86, 169, 119, 236, 186, 84, 179, 111, 169, 55, 27, 64, 49, 150, 85, 177, + 180, 66, 158, 51, 239, 45, +]; + +pub const CLASSIC_ADDRESS_ED25519: &str = "rLUEXYuLiQptky37CqLcm9USQpPiz5rkpD"; +pub const CLASSIC_ADDRESS_SECP256K1: &str = "rU6K7V3Po4snVhBBaU29sesqs2qTQJWDw1"; diff --git a/src/core/keypairs/utils.rs b/src/core/keypairs/utils.rs new file mode 100644 index 00000000..20a3976f --- /dev/null +++ b/src/core/keypairs/utils.rs @@ -0,0 +1,114 @@ +//! Miscellaneous helper functions. + +use crate::constants::ACCOUNT_ID_LENGTH; +use core::convert::TryInto; +use ripemd160::Ripemd160; +use sha2::{Digest, Sha256, Sha512}; + +/// Intermediate private keys are always padded with +/// 4 bytes of zeros. +pub(crate) const SECP256K1_INTERMEDIATE_KEYPAIR_PADDING: [u8; 4] = [0, 0, 0, 0]; +/// String keys must be _KEY_LENGTH long +pub(crate) const SECP256K1_KEY_LENGTH: usize = 66; +/// SECP256K1 sequence size. +pub(crate) const SECP256K1_SEQUENCE_SIZE: u32 = 4; +/// SECP256K1 maximum sequence. +pub(crate) const SECP256K1_SEQUENCE_MAX: u64 = u64::pow(256, SECP256K1_SEQUENCE_SIZE); + +/// Test message for signature verification. +pub(crate) const SIGNATURE_VERIFICATION_MESSAGE: &[u8] = b"This test message should verify."; + +/// String keys must be _KEY_LENGTH long +pub const SECP256K1_SIGNATURE_LENGTH: usize = secp256k1::constants::MAX_SIGNATURE_SIZE; +/// String keys must be _KEY_LENGTH long +pub const ED25519_SIGNATURE_LENGTH: usize = ed25519_dalek::SIGNATURE_LENGTH; +/// Length of half a sha512 hash. +pub const SHA512_HASH_LENGTH: usize = 32; + +/// ED25519 prefix +pub const ED25519_PREFIX: &str = "ED"; +/// SECP256K1 prefix +pub const SECP256K1_PREFIX: char = '0'; + +#[derive(Debug, PartialEq)] +pub(crate) enum Secp256k1Phase { + Root, + Mid, +} + +/// Returns the first 32 bytes of SHA-512 hash of message. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::keypairs::utils::sha512_first_half; +/// use xrpl::core::keypairs::utils::SHA512_HASH_LENGTH; +/// +/// let message: &[u8] = "test message".as_bytes(); +/// let sha_result: [u8; SHA512_HASH_LENGTH] = [ +/// 149, 11, 42, 126, 255, 167, 143, 81, 166, 53, 21, +/// 236, 69, 224, 62, 206, 190, 80, 239, 47, 28, 65, +/// 230, 150, 41, 181, 7, 120, 241, 27, 192, 128, +/// ]; +/// +/// assert_eq!(sha_result, sha512_first_half(message)); +/// ``` +pub fn sha512_first_half(message: &[u8]) -> [u8; SHA512_HASH_LENGTH] { + let mut sha512 = Sha512::new(); + + sha512.update(message); + sha512.finalize()[..SHA512_HASH_LENGTH] + .try_into() + .expect("Invalid slice length") +} + +/// Returns the account ID for a given public key. +/// +/// See Account ID and Address: +/// `` +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::keypairs::utils::get_account_id; +/// use xrpl::constants::ACCOUNT_ID_LENGTH; +/// +/// let public_key: &[u8] = "test message".as_bytes(); +/// let account_id: [u8; ACCOUNT_ID_LENGTH] = [ +/// 159, 127, 79, 53, 164, 208, 121, 119, 65, 32, +/// 123, 166, 113, 156, 97, 173, 156, 182, 36, 249, +/// ]; +/// +/// assert_eq!(account_id, get_account_id(public_key)); +/// ``` +pub fn get_account_id(public_key: &[u8]) -> [u8; ACCOUNT_ID_LENGTH] { + let mut sha256 = Sha256::new(); + let mut ripemd160 = Ripemd160::new(); + + sha256.update(public_key); + ripemd160.update(&sha256.finalize()); + + ripemd160.finalize()[..ACCOUNT_ID_LENGTH] + .try_into() + .expect("Invalid slice length") +} + +#[cfg(test)] +mod test { + use super::*; + use crate::core::keypairs::test_cases::*; + + #[test] + fn test_sha512_first_half() { + assert_eq!(TEST_MESSAGE_SHA, sha512_first_half(TEST_MESSAGE.as_bytes())); + } + + #[test] + fn test_get_account_id() { + assert_eq!(TEST_ACCOUNT_ID, get_account_id(TEST_MESSAGE.as_bytes())); + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs new file mode 100644 index 00000000..0a16f755 --- /dev/null +++ b/src/core/mod.rs @@ -0,0 +1,12 @@ +//! Core codec functions for interacting with the XRPL. + +pub mod addresscodec; +pub mod binarycodec; +pub mod definitions; +pub mod keypairs; +pub mod types; + +pub use self::binarycodec::BinaryParser; +pub use self::binarycodec::BinarySerializer; +pub use self::binarycodec::Parser; +pub use self::definitions::load_definition_map; diff --git a/src/core/test_data/codec-fixtures.json b/src/core/test_data/codec-fixtures.json new file mode 100644 index 00000000..be2e637a --- /dev/null +++ b/src/core/test_data/codec-fixtures.json @@ -0,0 +1,4466 @@ +{ + "accountState": [ + { + "binary": "1100612200000000240000000125000022C52D00000000558D7F42ED0621FBCFAE55CC6F2A9403A2AFB205708CCBA3109BB61DB8DDA261B46240000000160DC0808114712B799C79D1EEE3094B59EF9920C7FEB3CE4499", + "json": { + "OwnerCount": 0, + "Account": "rBKPS4oLSaV2KVVuHH8EpQqMGgGefGFQs7", + "PreviousTxnLgrSeq": 8901, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "8D7F42ED0621FBCFAE55CC6F2A9403A2AFB205708CCBA3109BB61DB8DDA261B4", + "Flags": 0, + "Sequence": 1, + "Balance": "370000000" + } + }, + { + "binary": "1100612200000000240000000125000000072D0000000055DF530FB14C5304852F20080B0A8EEF3A6BDD044F41F4EBBD68B8B321145FE4FF6240000002540BE4008114D0F5430B66E06498D4CEEC816C7B3337F9982337", + "json": { + "OwnerCount": 0, + "Account": "rLs1MzkFWCxTbuAHgjeTZK4fcCDDnf2KRv", + "PreviousTxnLgrSeq": 7, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "DF530FB14C5304852F20080B0A8EEF3A6BDD044F41F4EBBD68B8B321145FE4FF", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110064220000000058059D1E86DE5DCCCF956BF4799675B2425AF9AD44FE4CCA6FEE1C812EEF6423E68214F7FF2D5EA6BB5C26D85343656BEEE94D74B509E0011320908D554AA0D29F660716A3EE65C61DD886B744DDF60DE70E6B16EADB770635DB", + "json": { + "Owner": "rPcHbQ26o4Xrwb2bu5gLc3gWUsS52yx1pG", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "059D1E86DE5DCCCF956BF4799675B2425AF9AD44FE4CCA6FEE1C812EEF6423E6", + "Indexes": ["908D554AA0D29F660716A3EE65C61DD886B744DDF60DE70E6B16EADB770635DB"] + } + }, + { + "binary": "1100612200000000240000000125000068652D0000000055B6632D6376A2D9319F20A1C6DCCB486432D1E4A79951229D4C3DE2946F51D56662400009184E72A00081140DD319918CD5AE792BF7EC80D63B0F01B4573BBC", + "json": { + "OwnerCount": 0, + "Account": "rpGaCyHRYbgKhErgFih3RdjJqXDsYBouz3", + "PreviousTxnLgrSeq": 26725, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "B6632D6376A2D9319F20A1C6DCCB486432D1E4A79951229D4C3DE2946F51D566", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "1100612200000000240000000125000000082D00000000550735A0B32B2A3F4C938B76D6933003E29447DB8C7CE382BBE089402FF12A03E56240000002540BE400811479927BAFFD3D04A26096C0C97B1B0D45B01AD3C0", + "json": { + "OwnerCount": 0, + "Account": "rUnFEsHjxqTswbivzL2DNHBb34rhAgZZZK", + "PreviousTxnLgrSeq": 8, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "0735A0B32B2A3F4C938B76D6933003E29447DB8C7CE382BBE089402FF12A03E5", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110061220000000024000000012500007C3A2D0000000055FBF647E057F5C15EC277246AB843A5EB063646BEF2E3D3914D29456B329032626240000002540BE4008114EEAF1EB8C85B41B32816A3B833BA6B659D6E808A", + "json": { + "OwnerCount": 0, + "Account": "r4mscDrVMQz2on2px31aV5e5ouHeRPn8oy", + "PreviousTxnLgrSeq": 31802, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "FBF647E057F5C15EC277246AB843A5EB063646BEF2E3D3914D29456B32903262", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110064220000000031000000000000000232000000000000000158D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C38214E14829DB4C6419A8EFCAC1EC21D891A1A4339871011340A95EB2892EA15C8B7BCDAF6D1A8F1F21791192586EBD66B7DCBEC582BFAAA19852733E959FD0D25A72E188A26BC406768D91285883108AED061121408DAD4AF0", + "json": { + "Owner": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui", + "IndexNext": "0000000000000002", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000001", + "Flags": 0, + "RootIndex": "D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C3", + "Indexes": [ + "A95EB2892EA15C8B7BCDAF6D1A8F1F21791192586EBD66B7DCBEC582BFAAA198", + "52733E959FD0D25A72E188A26BC406768D91285883108AED061121408DAD4AF0" + ] + } + }, + { + "binary": "1100642200000000580AC869678D387BF526DA37F0C4B8B6BE049E57322EFB53E2C5D7D7A854DA829C82142B6C42A95B3F7EE1971E4A10098E8F1B5F66AA080113806BC1677EB8218F6ECB37FB83723ED4FA4C3089D718A45D5F0BB4F4EC553CDF28263F16D626C701250AD1E9FF56C763132DF4E09B1EF0B2D0A838D265123FBBA8C1C5FB39D6C15C581D822DBAF725EF7EDE40BEC9F93C52398CF5CE9F64154D6CA5C489C3780C320EC1C2CF5A2E22C2F393F91884DC14D18F5F5BED4EE3AFFE00", + "json": { + "Owner": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "0AC869678D387BF526DA37F0C4B8B6BE049E57322EFB53E2C5D7D7A854DA829C", + "Indexes": [ + "6BC1677EB8218F6ECB37FB83723ED4FA4C3089D718A45D5F0BB4F4EC553CDF28", + "263F16D626C701250AD1E9FF56C763132DF4E09B1EF0B2D0A838D265123FBBA8", + "C1C5FB39D6C15C581D822DBAF725EF7EDE40BEC9F93C52398CF5CE9F64154D6C", + "A5C489C3780C320EC1C2CF5A2E22C2F393F91884DC14D18F5F5BED4EE3AFFE00" + ] + } + }, + { + "binary": "1100612200000000240000000225000027522D0000000055C7AECAF0E7ABC3868C37343B7F63BAEC317A53867ABD2CA6BAD1F335C1CA4D6F62400000000BEBC1F68114D9CEEA2E2AD331A8D087C216284D58EBBC6780E8", + "json": { + "OwnerCount": 0, + "Account": "rLiCWKQNUs8CQ81m2rBoFjshuVJviSRoaJ", + "PreviousTxnLgrSeq": 10066, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "C7AECAF0E7ABC3868C37343B7F63BAEC317A53867ABD2CA6BAD1F335C1CA4D6F", + "Flags": 0, + "Sequence": 2, + "Balance": "199999990" + } + }, + { + "binary": "110072220002000025000000EF55C6A2521BBCCF13282C4FFEBC00D47BBA18C6CE5F5E4E0EFC3E3FCE364BAFC6B862800000000000000000000000000000000000000055534400000000000000000000000000000000000000000000000001668000000000000000000000000000000000000000555344000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC67D4C38D7EA4C680000000000000000000000000005553440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 239, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "C6A2521BBCCF13282C4FFEBC00D47BBA18C6CE5F5E4E0EFC3E3FCE364BAFC6B8", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "11007222000100002500005A7937000000000000000038000000000000000055BDBDD6CCF2F8211B41072BC37E934D2270F58EA5D5F44F7CA8483CF348B9377B6280000000000000000000000000000000000000004555520000000000000000000000000000000000000000000000000166D4C71AFD498D00000000000000000000000000004555520000000000169F404D62A8D2C8EE3935F230AA60BC07919E9667800000000000000000000000000000000000000045555200000000009F17DCA26FE8C8A1B258898D533305B92DB75127", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 23161, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "EUR", + "value": "20", + "issuer": "rshceBo6ftSVYo8h5uNPzRWbdqk4W6g9va" + }, + "PreviousTxnID": "BDBDD6CCF2F8211B41072BC37E934D2270F58EA5D5F44F7CA8483CF348B9377B", + "Flags": 65536, + "Balance": { + "currency": "EUR", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "EUR", + "value": "0", + "issuer": "rEWDpTUVU9fZZtzrywAUE6D6UcFzu6hFdE" + } + } + }, + { + "binary": "1100612200000000240000000125000000092D00000000555D2CC18F142044F1D792DC33D82218665359979ECFD5CD29211CC9CD6704F88C6240000002540BE4008114D7C71DCDCD004AA9F6B5AB1076B9F00FD4C4337C", + "json": { + "OwnerCount": 0, + "Account": "rLCvFaWk9WkJCCyg9Byvwbe9gxn1pnMLWL", + "PreviousTxnLgrSeq": 9, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "5D2CC18F142044F1D792DC33D82218665359979ECFD5CD29211CC9CD6704F88C", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000022842D0000000055FB7889B08F6B6413E37A3FBDDA421323B2E00EC2FDDFCE7A9035A2E12CA776E86240000002540BE4008114DB330727BF6CC97F43595AEA787874C155569F77", + "json": { + "OwnerCount": 0, + "Account": "rLzpfV5BFjUmBs8Et75Wurddg4CCXFLDFU", + "PreviousTxnLgrSeq": 8836, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "FB7889B08F6B6413E37A3FBDDA421323B2E00EC2FDDFCE7A9035A2E12CA776E8", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000020592D00000000551CE378FA7D55A2F000943FBB4EE60233AECC75CDE3F2845F0B603165968D9CB462400009184E72A0008114CBA90CB3210843D0CBC018F932DB46582DA0CE84", + "json": { + "OwnerCount": 0, + "Account": "rKZig5RFv5yWAqMi9PtC5akkGpNtn3pz8A", + "PreviousTxnLgrSeq": 8281, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "1CE378FA7D55A2F000943FBB4EE60233AECC75CDE3F2845F0B603165968D9CB4", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "1100642200000000310000000000000003320000000000000002581F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C8214A82BB90BF7031413B42E2C890827EDC2399B7BFA01134085469362B15032D6213572E63175C87C321601E1DDBB588C9CBD08CDB3F276AC2F1F54C50845EBD434A06639160F77CEB7C99C0606A3624F64C3678A9129F08D", + "json": { + "Owner": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa", + "IndexNext": "0000000000000003", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000002", + "Flags": 0, + "RootIndex": "1F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C", + "Indexes": [ + "85469362B15032D6213572E63175C87C321601E1DDBB588C9CBD08CDB3F276AC", + "2F1F54C50845EBD434A06639160F77CEB7C99C0606A3624F64C3678A9129F08D" + ] + } + }, + { + "binary": "110061220000000024000000012500000EAB2D0000000055F2BA48100AAEA92FAA28718F2F3E50452D7069696FAE781FE5043552593F95A5624000246139CA80008114D77E6F927BB1B6D7F80151B0853395733B9B4BC2", + "json": { + "OwnerCount": 0, + "Account": "rLeRkwDgbPVeSakJ2uXC2eqR8NLWMvU3kN", + "PreviousTxnLgrSeq": 3755, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "F2BA48100AAEA92FAA28718F2F3E50452D7069696FAE781FE5043552593F95A5", + "Flags": 0, + "Sequence": 1, + "Balance": "40000000000000" + } + }, + { + "binary": "110072220002000025000000DE557B4DEC82CF3BE513DA95C57282FBD0659E4E352BF59FA04C6C819E4BBD7CFFC5628000000000000000000000000000000000000000425443000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004254430000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA67D491C37937E080000000000000000000000000004254430000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 222, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + }, + "PreviousTxnID": "7B4DEC82CF3BE513DA95C57282FBD0659E4E352BF59FA04C6C819E4BBD7CFFC5", + "Flags": 131072, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "5", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110061220000000024000000012500005EA62D000000005561055B0F50077E654D61666A1A58E24C9B4FB4C2541AC09E2CA1F850C57E0C7B6240000002540BE40081148362C2AF4216ED4BFCE8B902F5398E7BE5B3E06D", + "json": { + "OwnerCount": 0, + "Account": "rUy6q3TxE4iuVWMpzycrQfD5uZok51g1cq", + "PreviousTxnLgrSeq": 24230, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "61055B0F50077E654D61666A1A58E24C9B4FB4C2541AC09E2CA1F850C57E0C7B", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110072220001000025000022BD553906085AB9862A404113E9B17BF00E796D8A80ED2B1066212AB4F00A7D7BCD1D6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4C8E1BC9BF0400000000000000000000000000055534400000000002C371D25803A0BF6F37CC37F99A10EC948B876F36780000000000000000000000000000000000000005553440000000000F8B331F4AEC7900AD1B990899C54F87633EBB741", + "json": { + "PreviousTxnLgrSeq": 8893, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "25", + "issuer": "rnp8kFTTm6KW8wsbgczfmv56kWXghPSWbK" + }, + "PreviousTxnID": "3906085AB9862A404113E9B17BF00E796D8A80ED2B1066212AB4F00A7D7BCD1D", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rPgrEG6nMMwAM1VbTumL23dnEX4UmeUHk7" + } + } + }, + { + "binary": "1100722200030000250000275A557AE0C1DBADD06E4150AB00E94BD5AD41A3D1E5FC852C8A6922B5EFD2E812709E6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4D1C37937E08000000000000000000000000000555344000000000058C742CF55C456DE367686CB9CED83750BD2497967D4D1C37937E080000000000000000000000000005553440000000000E8ACFC6B5EF4EA0601241525375162F43C2FF285", + "json": { + "PreviousTxnLgrSeq": 10074, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "50", + "issuer": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx" + }, + "PreviousTxnID": "7AE0C1DBADD06E4150AB00E94BD5AD41A3D1E5FC852C8A6922B5EFD2E812709E", + "Flags": 196608, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "50", + "issuer": "r4DGz8SxHXLaqsA9M2oocXsrty6BMSQvw3" + } + } + }, + { + "binary": "11006422000000005817CC40C6872E0C0E658C49B75D0812A70D4161DDA53324DF51FA58D3819C814B82149BBFDA47BD85A1E2D03F9A528BC95DC20432ED6E011320571BF14F28C4D97871CDACD344A8CF57E6BA287BF0440B9E0D0683D02751CC7B", + "json": { + "Owner": "rEUXZtdhEtCDPxJ3MAgLNMQpq4ASgjrV6i", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "17CC40C6872E0C0E658C49B75D0812A70D4161DDA53324DF51FA58D3819C814B", + "Indexes": ["571BF14F28C4D97871CDACD344A8CF57E6BA287BF0440B9E0D0683D02751CC7B"] + } + }, + { + "binary": "110061220000000024000000012500000E982D00000000552748D2555190DD2CC803E616C5276E299841DA53FB15AA9EFBEA1003549F7DD1624000B5E620F480008114E7D24CED46262A1BD2E415E1BD89F20DDB4042F5", + "json": { + "OwnerCount": 0, + "Account": "r43mpEMKY1cVUX8k6zKXnRhZMEyPU9aHzR", + "PreviousTxnLgrSeq": 3736, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "2748D2555190DD2CC803E616C5276E299841DA53FB15AA9EFBEA1003549F7DD1", + "Flags": 0, + "Sequence": 1, + "Balance": "200000000000000" + } + }, + { + "binary": "11006122000000002400000001250000000A2D000000005526B0EFCF1501118BD60F2BCD075E18865D8660B18C6581A8DDD626FA309762576240000002540BE4008114DD21BD1EC4969F62F7EEEA4315DF8D3A84EDB4D6", + "json": { + "OwnerCount": 0, + "Account": "rMwNkcpvcJucoWbFW89EGT6TfZyGUkaGso", + "PreviousTxnLgrSeq": 10, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "26B0EFCF1501118BD60F2BCD075E18865D8660B18C6581A8DDD626FA30976257", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000225000027902D0000000155C26F87CB499395AC8CD2DEDB7E944F4F16CEE07ECA0B481F9E2536450B7CC0DA6240000002540BE3F681145EBBD3E507B0FB7C03D592FF4F27E08A28AA5C50", + "json": { + "OwnerCount": 1, + "Account": "r9duXXmUuhSs6JxKpPCSh2tPUg9AGvE2cG", + "PreviousTxnLgrSeq": 10128, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "C26F87CB499395AC8CD2DEDB7E944F4F16CEE07ECA0B481F9E2536450B7CC0DA", + "Flags": 0, + "Sequence": 2, + "Balance": "9999999990" + } + }, + { + "binary": "1100642200000000581BCA9161A199AD5E907751CBF3FBA49689D517F0E8EE823AE17B737039B41DE1821427E48F6D22BCB31D5F3D315CC512E60EFF80673D01132026B894EE68470AD5AEEB55D5EBF936E6397CEE6957B93C56A2E7882CA9082873", + "json": { + "Owner": "rhdAw3LiEfWWmSrbnZG3udsN7PoWKT56Qo", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "1BCA9161A199AD5E907751CBF3FBA49689D517F0E8EE823AE17B737039B41DE1", + "Indexes": ["26B894EE68470AD5AEEB55D5EBF936E6397CEE6957B93C56A2E7882CA9082873"] + } + }, + { + "binary": "1100612200000000240000000125000068522D0000000055DA77B52C935CB91BE7E5D62FC3D1443A4861944944B4BD097F9210320C0AD85962400009184E72A00081145729F5F4CA2268C20F391BEFF6C01C081BEC8481", + "json": { + "OwnerCount": 0, + "Account": "r3AthBf5eW4b9ujLoXNHFeeEJsK3PtJDea", + "PreviousTxnLgrSeq": 26706, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "DA77B52C935CB91BE7E5D62FC3D1443A4861944944B4BD097F9210320C0AD859", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "11006122000000002400000001250000000C2D000000005516112AA178A920DA89B5F8D9DFCBA3487A767BD612AE4AF60B214A2136F7BEF56240000000773594008114B6D460A8040B7FE414E8CA08B78546547B159CF4", + "json": { + "OwnerCount": 0, + "Account": "rHC5QwZvGxyhC75StiJwZCrfnHhtSWrr8Y", + "PreviousTxnLgrSeq": 12, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "16112AA178A920DA89B5F8D9DFCBA3487A767BD612AE4AF60B214A2136F7BEF5", + "Flags": 0, + "Sequence": 1, + "Balance": "2000000000" + } + }, + { + "binary": "1100612200000000240000000125000068B02D0000000055D51FCBB193C8B1B3B981F894E0535FD58478B1146ECCC35DB462FE0C1A6B6CB66240000002540BE4008114F1F4D84D7870FEE3304E2959059C89D1C33608B1", + "json": { + "OwnerCount": 0, + "Account": "rPhMwMcn8ewJiM6NnP6xrm9NZBbKZ57kw1", + "PreviousTxnLgrSeq": 26800, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D51FCBB193C8B1B3B981F894E0535FD58478B1146ECCC35DB462FE0C1A6B6CB6", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100642200000000310000000000000001320000000000000003581F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C8214A82BB90BF7031413B42E2C890827EDC2399B7BFA011340AC2875C846CBD37CAF8409A623F3AA7D62916A0E043E02C909C9EF3A7B06F8CF142355A88F0729A5014DB835C24DA05F062293A439151A0BE9ACB80F20B2CDC5", + "json": { + "Owner": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa", + "IndexNext": "0000000000000001", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000003", + "Flags": 0, + "RootIndex": "1F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C", + "Indexes": [ + "AC2875C846CBD37CAF8409A623F3AA7D62916A0E043E02C909C9EF3A7B06F8CF", + "142355A88F0729A5014DB835C24DA05F062293A439151A0BE9ACB80F20B2CDC5" + ] + } + }, + { + "binary": "1100642200000000581F9FF48419CA69FDDCC294CCEEE608F5F8A8BE11E286AD5743ED2D457C5570C48214A3E4374D5570FDC25AA9F856E2A6635C66E9CFA50113207D4325BE338A40BBCBCC1F351B3272EB3E76305A878E76603DE206A795871619", + "json": { + "Owner": "rEA2XzkTXi6sWRzTVQVyUoSX4yJAzNxucd", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "1F9FF48419CA69FDDCC294CCEEE608F5F8A8BE11E286AD5743ED2D457C5570C4", + "Indexes": ["7D4325BE338A40BBCBCC1F351B3272EB3E76305A878E76603DE206A795871619"] + } + }, + { + "binary": "1100612200000000240000000125000079CB2D0000000055D612015F70931DC1CE2D65713408F0C4EE6230911F52E08678898D24C888A43A62400000E8D4A510008114C2225C8CBC2B6D0E53C763B53A9AC66C658A4EEC", + "json": { + "OwnerCount": 0, + "Account": "rJ6VE6L87yaVmdyxa9jZFXSAdEFSoTGPbE", + "PreviousTxnLgrSeq": 31179, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D612015F70931DC1CE2D65713408F0C4EE6230911F52E08678898D24C888A43A", + "Flags": 0, + "Sequence": 1, + "Balance": "1000000000000" + } + }, + { + "binary": "1100612200000000240000000125000045222D000000005519CDDD9E0DE5F269E1EAFC09E0C2D3E54BEDD7C67F890D020E883B69A653A4BA62400000001DCD6500811487057DF0267E7A0ED8E1197ADC0EF8C4471A90A8", + "json": { + "OwnerCount": 0, + "Account": "rDJvoVn8PyhwvHAWuTdtqkH4fuMLoWsZKG", + "PreviousTxnLgrSeq": 17698, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "19CDDD9E0DE5F269E1EAFC09E0C2D3E54BEDD7C67F890D020E883B69A653A4BA", + "Flags": 0, + "Sequence": 1, + "Balance": "500000000" + } + }, + { + "binary": "11006422000000005823E4C9FB1A108E7A4A188E13C3735432DDF7E104296DA2E493E3B0634CD529FF8214BF3389DD51B5B8CC5AEA410403036A2990896C70011340E1A4C98A789F35BA9947BD4920CDA9BF2C1A74E831208F7616FA485D5F016714F8608765CAD8DCA6FD3A5D417D008DB687732804BDABA32737DCB527DAC70B06", + "json": { + "Owner": "rJRyob8LPaA3twGEQDPU2gXevWhpSgD8S6", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "23E4C9FB1A108E7A4A188E13C3735432DDF7E104296DA2E493E3B0634CD529FF", + "Indexes": [ + "E1A4C98A789F35BA9947BD4920CDA9BF2C1A74E831208F7616FA485D5F016714", + "F8608765CAD8DCA6FD3A5D417D008DB687732804BDABA32737DCB527DAC70B06" + ] + } + }, + { + "binary": "11007222000100002500004ED73700000000000000003800000000000000005554FFA99A7418A342392237BA37874EF1B8DF48E9FA9E810C399EEE112CA3E2FA6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5438D7EA4C6800000000000000000000000000055534400000000007588B8DBDC8932DC410E8571045466C03F5A6B696780000000000000000000000000000000000000005553440000000000F182797BA121247C17BD87C4563B881EDA680521", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 20183, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "1000", + "issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY" + }, + "PreviousTxnID": "54FFA99A7418A342392237BA37874EF1B8DF48E9FA9E810C399EEE112CA3E2FA", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rPrz9m9yaXT94nWbqEG2SSe9kdU4Jo1CxA" + } + } + }, + { + "binary": "1100722200020000250000456B37000000000000000038000000000000000055A5C133CE96EEE6769F98A24B476A2E4B7B235C64C70527D8992A54D7A96252646280000000000000000000000000000000000000004A5059000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004A505900000000002B6C42A95B3F7EE1971E4A10098E8F1B5F66AA0867D59550F7DCA700000000000000000000000000004A5059000000000062FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 17771, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "JPY", + "value": "0", + "issuer": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy" + }, + "PreviousTxnID": "A5C133CE96EEE6769F98A24B476A2E4B7B235C64C70527D8992A54D7A9625264", + "Flags": 131072, + "Balance": { + "currency": "JPY", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "JPY", + "value": "60000", + "issuer": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j" + } + } + }, + { + "binary": "110061220000000024000000012500000B9C2D000000005564EFAD0087AD213CA25ABEA801E34E4719BF21A175A886EC9FD689E8424560B562400000021E66FB008114E4FE687C90257D3D2D694C8531CDEECBE84F3367", + "json": { + "OwnerCount": 0, + "Account": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo", + "PreviousTxnLgrSeq": 2972, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "64EFAD0087AD213CA25ABEA801E34E4719BF21A175A886EC9FD689E8424560B5", + "Flags": 0, + "Sequence": 1, + "Balance": "9100000000" + } + }, + { + "binary": "110061220000000024000000022500007A552D00000001552485FDC606352F1B0785DA5DE96FB9DBAF43EB60ECBB01B7F6FA970F512CDA5F624000002540BE3FF681142C371D25803A0BF6F37CC37F99A10EC948B876F3", + "json": { + "OwnerCount": 1, + "Account": "rnp8kFTTm6KW8wsbgczfmv56kWXghPSWbK", + "PreviousTxnLgrSeq": 31317, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "2485FDC606352F1B0785DA5DE96FB9DBAF43EB60ECBB01B7F6FA970F512CDA5F", + "Flags": 0, + "Sequence": 2, + "Balance": "159999999990" + } + }, + { + "binary": "11007222000100002500004124370000000000000000380000000000000000552E504E650BEE8920BF979ADBE6236C6C3F239FA2B37F22741DCFAF245091115D6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4C38D7EA4C68000000000000000000000000000555344000000000027E48F6D22BCB31D5F3D315CC512E60EFF80673D6780000000000000000000000000000000000000005553440000000000550FC62003E785DC231A1058A05E56E3F09CF4E6", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 16676, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "10", + "issuer": "rhdAw3LiEfWWmSrbnZG3udsN7PoWKT56Qo" + }, + "PreviousTxnID": "2E504E650BEE8920BF979ADBE6236C6C3F239FA2B37F22741DCFAF245091115D", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV" + } + } + }, + { + "binary": "1100612200000000240000000B25000045B12D00000006554E690ECBC944A7A3C57DFDC24D7CA1A0BDEFEB916901B7E35CDD4FE7E81848D6624000000165A0BB9C811462FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "json": { + "OwnerCount": 6, + "Account": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j", + "PreviousTxnLgrSeq": 17841, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4E690ECBC944A7A3C57DFDC24D7CA1A0BDEFEB916901B7E35CDD4FE7E81848D6", + "Flags": 0, + "Sequence": 11, + "Balance": "5999999900" + } + }, + { + "binary": "110064220000000058289CFC476B5876F28C8A3B3C5B7058EC2BDF668C37B846EA7E5E1A73A4AA081682140B8D970A5E6BB9C50EC063E9318C7218D65ECE43011320BC10E40AFB79298004CDE51CB065DBDCABA86EC406E3A1CF02CE5F8A9628A2BD", + "json": { + "Owner": "rphasxS8Q5p5TLTpScQCBhh5HfJfPbM2M8", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "289CFC476B5876F28C8A3B3C5B7058EC2BDF668C37B846EA7E5E1A73A4AA0816", + "Indexes": ["BC10E40AFB79298004CDE51CB065DBDCABA86EC406E3A1CF02CE5F8A9628A2BD"] + } + }, + { + "binary": "1100612200000000240000000125000000102D000000005512B9558B22BB2DF05643B745389145A2E12A07CD9AD6AB7F653A4B24DC50B2E06240000004B9F96B00811470EFFAAE000322A78E0D9DC9081564888C256C37", + "json": { + "OwnerCount": 0, + "Account": "rBJwwXADHqbwsp6yhrqoyt2nmFx9FB83Th", + "PreviousTxnLgrSeq": 16, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "12B9558B22BB2DF05643B745389145A2E12A07CD9AD6AB7F653A4B24DC50B2E0", + "Flags": 0, + "Sequence": 1, + "Balance": "20300000000" + } + }, + { + "binary": "110064220000000031000000000000000432000000000000000358D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C38214E14829DB4C6419A8EFCAC1EC21D891A1A43398710113409C3784EB4832563535522198D9D14E91D2760644174813689EE6A03AD43C6E4CED54FC8E215EFAE23E396D27099387D6687BDB63F7F282111BB0F567F8D1D649", + "json": { + "Owner": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui", + "IndexNext": "0000000000000004", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000003", + "Flags": 0, + "RootIndex": "D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C3", + "Indexes": [ + "9C3784EB4832563535522198D9D14E91D2760644174813689EE6A03AD43C6E4C", + "ED54FC8E215EFAE23E396D27099387D6687BDB63F7F282111BB0F567F8D1D649" + ] + } + }, + { + "binary": "110061220000000024000000012500005E762D00000000557C27C4820F6E4BA7B0698253A38410EB68D82B5433EA320848677F9B1180B4476240000002540BE400811430D253198122405C114A30ECC1DBE88C7BA15EC8", + "json": { + "OwnerCount": 0, + "Account": "rnT9PFSfAnWyj2fd7D5TCoCyCYbK4n356A", + "PreviousTxnLgrSeq": 24182, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "7C27C4820F6E4BA7B0698253A38410EB68D82B5433EA320848677F9B1180B447", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000039D42D00000000551EC7E89A17180B5DBA0B15709B616CB2CD703DC54702EB5EDCB1B95F3526AAC36240000002540BE4008114A42C21956B12F80C27355C6A337C4F032C643011", + "json": { + "OwnerCount": 0, + "Account": "rEyhgkRqGdCK7nXtfmADrqWYGT6rSsYYEZ", + "PreviousTxnLgrSeq": 14804, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "1EC7E89A17180B5DBA0B15709B616CB2CD703DC54702EB5EDCB1B95F3526AAC3", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110061220000000024000000042500004ED72D000000005554FFA99A7418A342392237BA37874EF1B8DF48E9FA9E810C399EEE112CA3E2FA624000048BEB9E85E28114F182797BA121247C17BD87C4563B881EDA680521", + "json": { + "OwnerCount": 0, + "Account": "rPrz9m9yaXT94nWbqEG2SSe9kdU4Jo1CxA", + "PreviousTxnLgrSeq": 20183, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "54FFA99A7418A342392237BA37874EF1B8DF48E9FA9E810C399EEE112CA3E2FA", + "Flags": 0, + "Sequence": 4, + "Balance": "4998999999970" + } + }, + { + "binary": "1100612200000000240000002F25000031AE2D00000000554EF16211BE5869C19E010B639568AA335DB9D9C7D02AC952A97E314A9C04743A62400000000BFB02748114B5F762798A53D543A014CAF8B297CFF8F2F937E8", + "json": { + "OwnerCount": 0, + "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "PreviousTxnLgrSeq": 12718, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4EF16211BE5869C19E010B639568AA335DB9D9C7D02AC952A97E314A9C04743A", + "Flags": 0, + "Sequence": 47, + "Balance": "200999540" + } + }, + { + "binary": "110061220000000024000000012500000EA42D00000000552C41F6108DF7234FFA6CCE96079196CF0F0B86E988993ECC8BAD6B61F0FAEFAE6240003691D6AFC000811426822909E3F673664367E8C7537098EB4CBAA13D", + "json": { + "OwnerCount": 0, + "Account": "rhWcbzUj9SVJocfHGLn58VYzXvoVnsU44u", + "PreviousTxnLgrSeq": 3748, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "2C41F6108DF7234FFA6CCE96079196CF0F0B86E988993ECC8BAD6B61F0FAEFAE", + "Flags": 0, + "Sequence": 1, + "Balance": "60000000000000" + } + }, + { + "binary": "1100642200000000582C9F00EFA5CCBD43452EF364B12C8DFCEF2B910336E5EFCE3AA412A55699158282146A03714FE4B738A637A094271E0DE8414D904CFA011320F721E924498EE68BFF906CD856E8332073DD350BAC9E8977AC3F31860BA1E33A", + "json": { + "Owner": "rwCYkXihZPm7dWuPCXoS3WXap7vbnZ8uzB", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "2C9F00EFA5CCBD43452EF364B12C8DFCEF2B910336E5EFCE3AA412A556991582", + "Indexes": ["F721E924498EE68BFF906CD856E8332073DD350BAC9E8977AC3F31860BA1E33A"] + } + }, + { + "binary": "110072220002000025000000F7550C3BB479DBDF48DFC99792E46DEE584545F365D04351D57480A0FBD4543980EC62800000000000000000000000000000000000000042544300000000000000000000000000000000000000000000000001668000000000000000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC67D4838D7EA4C680000000000000000000000000004254430000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA", + "json": { + "PreviousTxnLgrSeq": 247, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "0C3BB479DBDF48DFC99792E46DEE584545F365D04351D57480A0FBD4543980EC", + "Flags": 131072, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "1", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + } + } + }, + { + "binary": "1100642200000000364F0415EB4EA0C727582FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82144F0415EB4EA0C7270111000000000000000000000000555344000000000002112B6C42A95B3F7EE1971E4A10098E8F1B5F66AA0803110000000000000000000000000000000000000000041100000000000000000000000000000000000000000113205F22826818CC83448C9DF34939AB4019D3F80C70DEB8BDBDCF0496A36DC68719", + "json": { + "TakerPaysCurrency": "0000000000000000000000005553440000000000", + "ExchangeRate": "4F0415EB4EA0C727", + "TakerGetsCurrency": "0000000000000000000000000000000000000000", + "TakerGetsIssuer": "0000000000000000000000000000000000000000", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "2FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82144F0415EB4EA0C727", + "Indexes": ["5F22826818CC83448C9DF34939AB4019D3F80C70DEB8BDBDCF0496A36DC68719"], + "TakerPaysIssuer": "2B6C42A95B3F7EE1971E4A10098E8F1B5F66AA08" + } + }, + { + "binary": "1100642200000000365003BAF82D03A000582FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82145003BAF82D03A0000111000000000000000000000000555344000000000002112B6C42A95B3F7EE1971E4A10098E8F1B5F66AA0803110000000000000000000000000000000000000000041100000000000000000000000000000000000000000113205B7F148A8DDB4EB7386C9E75C4C1ED918DEDE5C52D5BA51B694D7271EF8BDB46", + "json": { + "TakerPaysCurrency": "0000000000000000000000005553440000000000", + "ExchangeRate": "5003BAF82D03A000", + "TakerGetsCurrency": "0000000000000000000000000000000000000000", + "TakerGetsIssuer": "0000000000000000000000000000000000000000", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "2FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82145003BAF82D03A000", + "Indexes": ["5B7F148A8DDB4EB7386C9E75C4C1ED918DEDE5C52D5BA51B694D7271EF8BDB46"], + "TakerPaysIssuer": "2B6C42A95B3F7EE1971E4A10098E8F1B5F66AA08" + } + }, + { + "binary": "1100612200000000240000000125000022C12D00000000551A9B9C5537F2BD2C221B34AA61B30E076F0DDC74931327DE6B6B727270672F446240000000160DC08081149BBFDA47BD85A1E2D03F9A528BC95DC20432ED6E", + "json": { + "OwnerCount": 0, + "Account": "rEUXZtdhEtCDPxJ3MAgLNMQpq4ASgjrV6i", + "PreviousTxnLgrSeq": 8897, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "1A9B9C5537F2BD2C221B34AA61B30E076F0DDC74931327DE6B6B727270672F44", + "Flags": 0, + "Sequence": 1, + "Balance": "370000000" + } + }, + { + "binary": "1100612200000000240000000125000000152D000000005507F84B7AF363F23B822105078EBE49CC18E846B23697A3652294B2CCD333ACCF6240000002540BE4008114C36A74182B6D5FB7DD17264BD52056FB45B732F9", + "json": { + "OwnerCount": 0, + "Account": "rJFGHvCtpPrftTmeNAs8bYy5xUeTaxCD5t", + "PreviousTxnLgrSeq": 21, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "07F84B7AF363F23B822105078EBE49CC18E846B23697A3652294B2CCD333ACCF", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11007222000100002500000B845538C911C5DAF1615BAA58B7D2265590DE1DAD40C79B3F7597C47ECE8047E1E4F46280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D4838D7EA4C68000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC6780000000000000000000000000000000000000004254430000000000C2659C14642A6604CE305966307E5F21817A092D", + "json": { + "PreviousTxnLgrSeq": 2948, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "1", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "38C911C5DAF1615BAA58B7D2265590DE1DAD40C79B3F7597C47ECE8047E1E4F4", + "Flags": 65536, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rJ51FBSh6hXSUkFdMxwmtcorjx9izrC1yj" + } + } + }, + { + "binary": "1100722200030000250000104E55D890893A91DC745BE221820C17EC3E8AF4CC119A93AA8AB8FD42C16D264521FA6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4C38D7EA4C68000000000000000000000000000555344000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC67D4C38D7EA4C680000000000000000000000000005553440000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA", + "json": { + "PreviousTxnLgrSeq": 4174, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "10", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "D890893A91DC745BE221820C17EC3E8AF4CC119A93AA8AB8FD42C16D264521FA", + "Flags": 196608, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + } + } + }, + { + "binary": "1100642200000000583811BC6A986CEBA5E5268A5F58800DA3A6611AC2A90155C1EA745A41E9A217F68214712B799C79D1EEE3094B59EF9920C7FEB3CE4499011340F8608765CAD8DCA6FD3A5D417D008DB687732804BDABA32737DCB527DAC70B06B82A83B063FF08369F9BDEDC73074352FE37733E8373F6EDBFFC872489B57D93", + "json": { + "Owner": "rBKPS4oLSaV2KVVuHH8EpQqMGgGefGFQs7", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "3811BC6A986CEBA5E5268A5F58800DA3A6611AC2A90155C1EA745A41E9A217F6", + "Indexes": [ + "F8608765CAD8DCA6FD3A5D417D008DB687732804BDABA32737DCB527DAC70B06", + "B82A83B063FF08369F9BDEDC73074352FE37733E8373F6EDBFFC872489B57D93" + ] + } + }, + { + "binary": "11006122000000002400000001250000001C2D0000000055DA64AD82376A8162070421BBB7644282F0B012779A0DECA1FC2EF01285E9F7A06240000002540BE4008114028A5595EBD2F6069C382B641259C21D3A806A29", + "json": { + "OwnerCount": 0, + "Account": "rNSnpURu2o7mD9JPjaLsdUw2HEMx5xHzd", + "PreviousTxnLgrSeq": 28, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "DA64AD82376A8162070421BBB7644282F0B012779A0DECA1FC2EF01285E9F7A0", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006122000000002400000001250000004C2D0000000055D4AFFB56DCCCB4394A067BF61EA8084E53317392732A27D021FBB6B2ED4B19B962400000746A5288008114B49B00D585F63AAB70096D8615EAEC24EE684A2C", + "json": { + "OwnerCount": 0, + "Account": "rHTxKLzRbniScyQFGMb3NodmxA848W8dKM", + "PreviousTxnLgrSeq": 76, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D4AFFB56DCCCB4394A067BF61EA8084E53317392732A27D021FBB6B2ED4B19B9", + "Flags": 0, + "Sequence": 1, + "Balance": "500000000000" + } + }, + { + "binary": "11006422000000003100000000000000023200000000000000015898082E695CAB618590BEEA0647A5F24D2B610A686ECD49310604FC7431FAAB0D8214B544029B077F39117BD0C9FB2913FCE08F9345CF011340A2EFB4B11D6FDF01643DEE32792BA65BCCC5A98189A4955EB3C73911DDB648DBD24FA4A3422BA1E91109B83D2A7545FC6369EAC13E7F4673F464BBBBC77AB2BE", + "json": { + "Owner": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr", + "IndexNext": "0000000000000002", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000001", + "Flags": 0, + "RootIndex": "98082E695CAB618590BEEA0647A5F24D2B610A686ECD49310604FC7431FAAB0D", + "Indexes": [ + "A2EFB4B11D6FDF01643DEE32792BA65BCCC5A98189A4955EB3C73911DDB648DB", + "D24FA4A3422BA1E91109B83D2A7545FC6369EAC13E7F4673F464BBBBC77AB2BE" + ] + } + }, + { + "binary": "1100612200000000240000000125000069422D000000005562EFA3F14D0DE4136D444B65371C6EFE842C9B4782437D6DE81784329E0400126240005AF3107A40008114B1E47CDF535B1E23C9750FDB57C2805DD7FA072E", + "json": { + "OwnerCount": 0, + "Account": "rHDcKZgR7JDGQEe9r13UZkryEVPytV6L6F", + "PreviousTxnLgrSeq": 26946, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "62EFA3F14D0DE4136D444B65371C6EFE842C9B4782437D6DE81784329E040012", + "Flags": 0, + "Sequence": 1, + "Balance": "100000000000000" + } + }, + { + "binary": "11006122000000002400000001250000207B2D00000000554D2FA912FFA328BC36A0804E1CC9FB4A54689B1419F9D22188DA59E739578E7962400009184E72A00081149445CAC489FF48025BBBB7A2EBC1C7F2019771BC", + "json": { + "OwnerCount": 0, + "Account": "rNWzcdSkXL28MeKaPwrvR3i7yU6XoqCiZc", + "PreviousTxnLgrSeq": 8315, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4D2FA912FFA328BC36A0804E1CC9FB4A54689B1419F9D22188DA59E739578E79", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "1100612200000000240000000E25000000F02D0000000C550C164A054712296CB0946EEC6F3BF43DFE94662DA03238AABF4C1B13C32DAC2462400000012B8369BE8114E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "OwnerCount": 12, + "Account": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui", + "PreviousTxnLgrSeq": 240, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "0C164A054712296CB0946EEC6F3BF43DFE94662DA03238AABF4C1B13C32DAC24", + "Flags": 0, + "Sequence": 14, + "Balance": "5024999870" + } + }, + { + "binary": "1100612200000000240000000125000000202D0000000055D70ACED6430B917DBFD98F92ECC1B7222C41E0283BC5854CC064EB01288889BF6240000002540BE40081147ECB328E2AFDCDB45D94FC6856790274BE8EC14D", + "json": { + "OwnerCount": 0, + "Account": "rUZRZ2b4NyCxjHSQKiYnpBuCWkKwDWTjxw", + "PreviousTxnLgrSeq": 32, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D70ACED6430B917DBFD98F92ECC1B7222C41E0283BC5854CC064EB01288889BF", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100642200000000583F2BADB38F12C87D111D3970CD1F05FE698DB86F14DC7C5FAEB05BFB6391B00E82145EBBD3E507B0FB7C03D592FF4F27E08A28AA5C5001132073E075E64CA5E7CE60FFCD5359C1D730EDFFEE7C4D992760A87DF7EA0A34E40F", + "json": { + "Owner": "r9duXXmUuhSs6JxKpPCSh2tPUg9AGvE2cG", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "3F2BADB38F12C87D111D3970CD1F05FE698DB86F14DC7C5FAEB05BFB6391B00E", + "Indexes": ["73E075E64CA5E7CE60FFCD5359C1D730EDFFEE7C4D992760A87DF7EA0A34E40F"] + } + }, + { + "binary": "1100642200000000310000000000000001320000000000000001583F491053BB45B0B08D9F0CBE85C29206483E7FA9CE889E1D248108565711F0A98214F8B331F4AEC7900AD1B990899C54F87633EBB7410113401595E5D5197330F58A479200A2FDD434D7A244BD1FFEC5E5EE8CF064AE77D3F5E1A4C98A789F35BA9947BD4920CDA9BF2C1A74E831208F7616FA485D5F016714", + "json": { + "Owner": "rPgrEG6nMMwAM1VbTumL23dnEX4UmeUHk7", + "IndexNext": "0000000000000001", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000001", + "Flags": 0, + "RootIndex": "3F491053BB45B0B08D9F0CBE85C29206483E7FA9CE889E1D248108565711F0A9", + "Indexes": [ + "1595E5D5197330F58A479200A2FDD434D7A244BD1FFEC5E5EE8CF064AE77D3F5", + "E1A4C98A789F35BA9947BD4920CDA9BF2C1A74E831208F7616FA485D5F016714" + ] + } + }, + { + "binary": "1100642200000000584235CD082112FB621C02D6DA2E4F4ACFAFC91CB0585E034B936C29ABF4A76B01821480799D02FF2097DEB21F66472FCF477C36E7039F0113206C4C3F1C6B9D76A6EF50F377E7C3991825694C604DBE0C1DD09362045EE41997", + "json": { + "Owner": "rU5KBPzSyPycRVW1HdgCKjYpU6W9PKQdE8", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "4235CD082112FB621C02D6DA2E4F4ACFAFC91CB0585E034B936C29ABF4A76B01", + "Indexes": ["6C4C3F1C6B9D76A6EF50F377E7C3991825694C604DBE0C1DD09362045EE41997"] + } + }, + { + "binary": "11007222000100002500000B9C5564EFAD0087AD213CA25ABEA801E34E4719BF21A175A886EC9FD689E8424560B56280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D4838D7EA4C68000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC6780000000000000000000000000000000000000004254430000000000E4FE687C90257D3D2D694C8531CDEECBE84F3367", + "json": { + "PreviousTxnLgrSeq": 2972, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "1", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "64EFAD0087AD213CA25ABEA801E34E4719BF21A175A886EC9FD689E8424560B5", + "Flags": 65536, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo" + } + } + }, + { + "binary": "1100612200000000240000000225000069252D0000000055D1DDAEDC74BC308B26BF3112D42F12E0D125F826506E0DB13654AD22115D3C31624000000253FCA1B681144B3FB75933675A8C3189B3A360B4AE272272B3E2", + "json": { + "OwnerCount": 0, + "Account": "rfitr7nL7MX85LLKJce7E3ATQjSiyUPDfj", + "PreviousTxnLgrSeq": 26917, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D1DDAEDC74BC308B26BF3112D42F12E0D125F826506E0DB13654AD22115D3C31", + "Flags": 0, + "Sequence": 2, + "Balance": "9998999990" + } + }, + { + "binary": "110064220000000031000000000000000132000000000000000558433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840821436D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC011340E49318D6DF22411C3F35581B1D28297A36E47F68B45F36A587C156E6E43CE0A64FFCC3F4D53FD3B5F488C8EB8E5D779F9028130F9160218020DA73CD5E630454", + "json": { + "Owner": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm", + "IndexNext": "0000000000000001", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000005", + "Flags": 0, + "RootIndex": "433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840", + "Indexes": [ + "E49318D6DF22411C3F35581B1D28297A36E47F68B45F36A587C156E6E43CE0A6", + "4FFCC3F4D53FD3B5F488C8EB8E5D779F9028130F9160218020DA73CD5E630454" + ] + } + }, + { + "binary": "110072220002000025000027775593DDD4D2532AB1BFC2A18FB2E32542A24EA353AEDF482520792F8BCDEAA77E876280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000042544300000000004A77EF82417FCC4B8D801A9D64C9070C0824E5E767D48AA87BEE538000000000000000000000000000425443000000000058C742CF55C456DE367686CB9CED83750BD24979", + "json": { + "PreviousTxnLgrSeq": 10103, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rf8kg7r5Fc8cCszGdD2jeUZt2FrgQd76BS" + }, + "PreviousTxnID": "93DDD4D2532AB1BFC2A18FB2E32542A24EA353AEDF482520792F8BCDEAA77E87", + "Flags": 131072, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "3", + "issuer": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx" + } + } + }, + { + "binary": "110064220000000058433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840821436D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC01134042E28285A82D01DCA856118A064C8AEEE1BF8167C08186DA5BFC678687E86F7CAB124EEAB087452070EC70D9DEA1A22C9766FFBBEE1025FD46495CC74148CCA8", + "json": { + "Owner": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840", + "Indexes": [ + "42E28285A82D01DCA856118A064C8AEEE1BF8167C08186DA5BFC678687E86F7C", + "AB124EEAB087452070EC70D9DEA1A22C9766FFBBEE1025FD46495CC74148CCA8" + ] + } + }, + { + "binary": "11006422000000005848E91FD14597FB089654DADE7B70EB08CAF421EA611D703F3E871F7D4B5AAB5D8214F182797BA121247C17BD87C4563B881EDA68052101132025DCAC87FBE4C3B66A1AFDE3C3F98E5A16333975C4FD46682F7497F27DFB9766", + "json": { + "Owner": "rPrz9m9yaXT94nWbqEG2SSe9kdU4Jo1CxA", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "48E91FD14597FB089654DADE7B70EB08CAF421EA611D703F3E871F7D4B5AAB5D", + "Indexes": ["25DCAC87FBE4C3B66A1AFDE3C3F98E5A16333975C4FD46682F7497F27DFB9766"] + } + }, + { + "binary": "110061220000000024000000012500000E9B2D000000005566E8E26B5D80658B19D772E9CA701F5E6101A080DA6C45B386521EE2191315EE6240005AF3107A4000811479ED4C4AF533242AF5156D67A6BF04CAFCAD4BF7", + "json": { + "OwnerCount": 0, + "Account": "rUf6pynZ8ucVj1jC9bKExQ7mb9sQFooTPK", + "PreviousTxnLgrSeq": 3739, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "66E8E26B5D80658B19D772E9CA701F5E6101A080DA6C45B386521EE2191315EE", + "Flags": 0, + "Sequence": 1, + "Balance": "100000000000000" + } + }, + { + "binary": "1100612200000000240000000125000000212D00000000554FD7B01EF2A4D4A34336DAD124D774990DBBDD097E2A4DD8E8FB992C7642DDA16240000000773594008114E7DF5D1ACB4FDF118AE260E3EDD5EEE09681FFCF", + "json": { + "OwnerCount": 0, + "Account": "r49pCti5xm7WVNceBaiz7vozvE9zUGq8z2", + "PreviousTxnLgrSeq": 33, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4FD7B01EF2A4D4A34336DAD124D774990DBBDD097E2A4DD8E8FB992C7642DDA1", + "Flags": 0, + "Sequence": 1, + "Balance": "2000000000" + } + }, + { + "binary": "1100612200000000240000000125000094F12D00000000553B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF6240000002540BE4008114D4CC8AB5B21D86A82C3E9E8D0ECF2404B77FECBA", + "json": { + "OwnerCount": 0, + "Account": "rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj", + "PreviousTxnLgrSeq": 38129, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "3B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100642200000000583F491053BB45B0B08D9F0CBE85C29206483E7FA9CE889E1D248108565711F0A98214F8B331F4AEC7900AD1B990899C54F87633EBB741011340B15AB125CC1D8CACDC22B76E5AABF74A6BB620A5C223BE81ECB71EF17F1C3489571BF14F28C4D97871CDACD344A8CF57E6BA287BF0440B9E0D0683D02751CC7B", + "json": { + "Owner": "rPgrEG6nMMwAM1VbTumL23dnEX4UmeUHk7", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "3F491053BB45B0B08D9F0CBE85C29206483E7FA9CE889E1D248108565711F0A9", + "Indexes": [ + "B15AB125CC1D8CACDC22B76E5AABF74A6BB620A5C223BE81ECB71EF17F1C3489", + "571BF14F28C4D97871CDACD344A8CF57E6BA287BF0440B9E0D0683D02751CC7B" + ] + } + }, + { + "binary": "1100642200000000584EFC0442D07AE681F7FDFAA89C75F06F8E28CFF888593440201B0320E8F2C7BD82142C371D25803A0BF6F37CC37F99A10EC948B876F30113201595E5D5197330F58A479200A2FDD434D7A244BD1FFEC5E5EE8CF064AE77D3F5", + "json": { + "Owner": "rnp8kFTTm6KW8wsbgczfmv56kWXghPSWbK", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "4EFC0442D07AE681F7FDFAA89C75F06F8E28CFF888593440201B0320E8F2C7BD", + "Indexes": ["1595E5D5197330F58A479200A2FDD434D7A244BD1FFEC5E5EE8CF064AE77D3F5"] + } + }, + { + "binary": "1100612200000000240000000425000022C12D00000003551A9B9C5537F2BD2C221B34AA61B30E076F0DDC74931327DE6B6B727270672F446240000001FBD4E1E28114F8B331F4AEC7900AD1B990899C54F87633EBB741", + "json": { + "OwnerCount": 3, + "Account": "rPgrEG6nMMwAM1VbTumL23dnEX4UmeUHk7", + "PreviousTxnLgrSeq": 8897, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "1A9B9C5537F2BD2C221B34AA61B30E076F0DDC74931327DE6B6B727270672F44", + "Flags": 0, + "Sequence": 4, + "Balance": "8519999970" + } + }, + { + "binary": "110061220000000024000000012500003F1A2D0000000055B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC096240000002540BE40081145E7B112523F68D2F5E879DB4EAC51C6698A69304", + "json": { + "OwnerCount": 0, + "Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + "PreviousTxnLgrSeq": 16154, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "B24159F8552C355D35E43623F0E5AD965ADBF034D482421529E2703904E1EC09", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100722200010000250000009855DFAC2C5FBD6C6DF48E65345F7926A5F158D62C31151E9A982FDFF65BC8627B426280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D448E1BC9BF04000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC6780000000000000000000000000000000000000004254430000000000550FC62003E785DC231A1058A05E56E3F09CF4E6", + "json": { + "PreviousTxnLgrSeq": 152, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0.25", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "DFAC2C5FBD6C6DF48E65345F7926A5F158D62C31151E9A982FDFF65BC8627B42", + "Flags": 65536, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "0", + "issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV" + } + } + }, + { + "binary": "1100612200000000240000002025000048992D00000002551B91A44428CA0752C4111A528AB32593852A83AB372883A46A8929FF0F06A8996240000002526FE60A81148D3A0AEF277858BD4D9751ECECD16779C0CC86D0", + "json": { + "OwnerCount": 2, + "Account": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x", + "PreviousTxnLgrSeq": 18585, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "1B91A44428CA0752C4111A528AB32593852A83AB372883A46A8929FF0F06A899", + "Flags": 0, + "Sequence": 32, + "Balance": "9972999690" + } + }, + { + "binary": "110072220002000025000000E15590931F239C10EA28D263A3E09A5817818B80A8E2501930F413455CDA7D586481628000000000000000000000000000000000000000555344000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000005553440000000000B544029B077F39117BD0C9FB2913FCE08F9345CF67D4C38D7EA4C680000000000000000000000000005553440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 225, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr" + }, + "PreviousTxnID": "90931F239C10EA28D263A3E09A5817818B80A8E2501930F413455CDA7D586481", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110064220000000031000000000000000532000000000000000458433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840821436D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC011340353D47B7B033F5EC041BD4E367437C9EDA160D14BFBC3EF43B3335259AA5D5D572307CB57E53604A0C50E653AB10E386F3835460B5585B70CB7F668C1E04AC8B", + "json": { + "Owner": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm", + "IndexNext": "0000000000000005", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000004", + "Flags": 0, + "RootIndex": "433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840", + "Indexes": [ + "353D47B7B033F5EC041BD4E367437C9EDA160D14BFBC3EF43B3335259AA5D5D5", + "72307CB57E53604A0C50E653AB10E386F3835460B5585B70CB7F668C1E04AC8B" + ] + } + }, + { + "binary": "110061220000000024000000122500007E2D2D00000005552E30F12CCD06E57502C5C9E834CA8834935B7DE14C79DA9ABE72E9D508BCBCB16240000002363E7E56811458C742CF55C456DE367686CB9CED83750BD24979", + "json": { + "OwnerCount": 5, + "Account": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx", + "PreviousTxnLgrSeq": 32301, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "2E30F12CCD06E57502C5C9E834CA8834935B7DE14C79DA9ABE72E9D508BCBCB1", + "Flags": 0, + "Sequence": 18, + "Balance": "9499999830" + } + }, + { + "binary": "110061220000000024000000012500004ED32D00000000553662FED78877C7E424BEF91C02B9ECA5E02AD3A8638F0A3B89C1EAC6C9CC92536240002D79883D20008114D99223BCD7B2E92968DC60BC9C63D1D808191FB3", + "json": { + "OwnerCount": 0, + "Account": "rLqQ62u51KR3TFcewbEbJTQbCuTqsg82EY", + "PreviousTxnLgrSeq": 20179, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "3662FED78877C7E424BEF91C02B9ECA5E02AD3A8638F0A3B89C1EAC6C9CC9253", + "Flags": 0, + "Sequence": 1, + "Balance": "50000000000000" + } + }, + { + "binary": "1100612200000000240000000225000022C82D000000015545CA59F7752331A307FF9BCF016C3243267D8506D0D0FA51965D322F7D59DF366240000000160DC07681142ECC94F5447A3CC1348E94C84D4677442C9E4227", + "json": { + "OwnerCount": 1, + "Account": "rnGTwRTacmqZZBwPB6rh3H1W4GoTZCQtNA", + "PreviousTxnLgrSeq": 8904, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "45CA59F7752331A307FF9BCF016C3243267D8506D0D0FA51965D322F7D59DF36", + "Flags": 0, + "Sequence": 2, + "Balance": "369999990" + } + }, + { + "binary": "1100612200000000240000000125000000272D00000000554EFE780DF3B843610B1F045EC6C0D921D5EE1A2225ADD558946AD5149170BB3D6240000002540BE400811440C0D2721826B0943F973A78931C4D2546D1FF01", + "json": { + "OwnerCount": 0, + "Account": "rauPN85FeNYLBpHgJJFH6g9fYUWBmJKKhs", + "PreviousTxnLgrSeq": 39, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4EFE780DF3B843610B1F045EC6C0D921D5EE1A2225ADD558946AD5149170BB3D", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000000292D0000000055126733220B5DDAD7854EF9F763D65BCBC1BBD61FD1FEEEF9CD7356037162C33F6240000002540BE40081149D8285F4BF6E88DB66575E9462A6F9DFFF2B3280", + "json": { + "OwnerCount": 0, + "Account": "rEMqTpu21XNk62QjTgVXKDig5HUpNnHvij", + "PreviousTxnLgrSeq": 41, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "126733220B5DDAD7854EF9F763D65BCBC1BBD61FD1FEEEF9CD7356037162C33F", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110072220002000025000022C1551A9B9C5537F2BD2C221B34AA61B30E076F0DDC74931327DE6B6B727270672F446280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000055534400000000009BBFDA47BD85A1E2D03F9A528BC95DC20432ED6E67D4C38D7EA4C680000000000000000000000000005553440000000000F8B331F4AEC7900AD1B990899C54F87633EBB741", + "json": { + "PreviousTxnLgrSeq": 8897, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rEUXZtdhEtCDPxJ3MAgLNMQpq4ASgjrV6i" + }, + "PreviousTxnID": "1A9B9C5537F2BD2C221B34AA61B30E076F0DDC74931327DE6B6B727270672F44", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rPgrEG6nMMwAM1VbTumL23dnEX4UmeUHk7" + } + } + }, + { + "binary": "11006122000000002400000007250000010E2D00000005559C9AF3AC76FC4626D86305D1D6082944D1E56EC92E3ECEC07503E3B26BF233B262400000012A05F1C48114A82BB90BF7031413B42E2C890827EDC2399B7BFA", + "json": { + "OwnerCount": 5, + "Account": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa", + "PreviousTxnLgrSeq": 270, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "9C9AF3AC76FC4626D86305D1D6082944D1E56EC92E3ECEC07503E3B26BF233B2", + "Flags": 0, + "Sequence": 7, + "Balance": "4999999940" + } + }, + { + "binary": "11006422000000005898082E695CAB618590BEEA0647A5F24D2B610A686ECD49310604FC7431FAAB0D8214B544029B077F39117BD0C9FB2913FCE08F9345CF0113209BF3216E42575CA5A3CB4D0F2021EE81D0F7835BA2EDD78E05CAB44B655962BB", + "json": { + "Owner": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "98082E695CAB618590BEEA0647A5F24D2B610A686ECD49310604FC7431FAAB0D", + "Indexes": ["9BF3216E42575CA5A3CB4D0F2021EE81D0F7835BA2EDD78E05CAB44B655962BB"] + } + }, + { + "binary": "110061220000000024000000052500005ADC2D0000000255F9ED6C634DE09655F9F7C8E088B9157BB785571CAA4305A6DD7BC876BD57671D62400000001DCD64D88114169F404D62A8D2C8EE3935F230AA60BC07919E96", + "json": { + "OwnerCount": 2, + "Account": "rshceBo6ftSVYo8h5uNPzRWbdqk4W6g9va", + "PreviousTxnLgrSeq": 23260, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "F9ED6C634DE09655F9F7C8E088B9157BB785571CAA4305A6DD7BC876BD57671D", + "Flags": 0, + "Sequence": 5, + "Balance": "499999960" + } + }, + { + "binary": "110061220000000024000000042500001F352D0000000055FE8A433C90ED67E78FB7F8B8DED39E1ECD8DEC17DC748DB3E2671695E141D38962400000000C8458628114840CB75C9645F23054D2449DFD4BC1DD8D4FE6B0", + "json": { + "OwnerCount": 0, + "Account": "rDsDR1pFaY8Ythr8px4N98bSueixyrKvPx", + "PreviousTxnLgrSeq": 7989, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "FE8A433C90ED67E78FB7F8B8DED39E1ECD8DEC17DC748DB3E2671695E141D389", + "Flags": 0, + "Sequence": 4, + "Balance": "209999970" + } + }, + { + "binary": "11006F2200000000240000000825000045A23300000000000000003400000000000000005515955F0DCBF3237CE8F5ACAB92C81B4368857AF2E9BD2BC3D0C1D9CEA26F45BA50102FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82145003BAF82D03A00064D4CB30E8870AE00000000000000000000000000055534400000000002B6C42A95B3F7EE1971E4A10098E8F1B5F66AA086540000000002DC6C0811462FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "json": { + "TakerPays": { + "currency": "USD", + "value": "31.5", + "issuer": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy" + }, + "Account": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j", + "PreviousTxnLgrSeq": 17826, + "BookDirectory": "2FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82145003BAF82D03A000", + "LedgerEntryType": "Offer", + "OwnerNode": "0000000000000000", + "PreviousTxnID": "15955F0DCBF3237CE8F5ACAB92C81B4368857AF2E9BD2BC3D0C1D9CEA26F45BA", + "TakerGets": "3000000", + "Flags": 0, + "Sequence": 8, + "BookNode": "0000000000000000" + } + }, + { + "binary": "11006122000000002400000001250000002E2D0000000055152DB308576D51A37ADF51D121BFE1B5BB0EDD15AC22F1D45C36CAF8C88A318B62400000003B9ACA0081143339A3C2251CA81F31EC67FCB6BA026C2CECE587", + "json": { + "OwnerCount": 0, + "Account": "rnCiWCUZXAHPpEjLY1gCjtbuc9jM1jq8FD", + "PreviousTxnLgrSeq": 46, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "152DB308576D51A37ADF51D121BFE1B5BB0EDD15AC22F1D45C36CAF8C88A318B", + "Flags": 0, + "Sequence": 1, + "Balance": "1000000000" + } + }, + { + "binary": "1100722200020000250000276C55BBE44659984409F29AF04F9422A2026D4A4D4343F80F53337BF5086555A1FD076280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000055534400000000004A77EF82417FCC4B8D801A9D64C9070C0824E5E767D4D1C37937E08000000000000000000000000000555344000000000058C742CF55C456DE367686CB9CED83750BD24979", + "json": { + "PreviousTxnLgrSeq": 10092, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rf8kg7r5Fc8cCszGdD2jeUZt2FrgQd76BS" + }, + "PreviousTxnID": "BBE44659984409F29AF04F9422A2026D4A4D4343F80F53337BF5086555A1FD07", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "50", + "issuer": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx" + } + } + }, + { + "binary": "110064220000000031000000000000000332000000000000000258433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840821436D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC0113406231CFA6BE243E92EC33050DC23C6E8EC972F22A111D96328873207A7CCCC7C735FB1D334ECCD52B94253E7A33BA37C3D845E26F11FDEC08A56527C92907C3AC", + "json": { + "Owner": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm", + "IndexNext": "0000000000000003", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000002", + "Flags": 0, + "RootIndex": "433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840", + "Indexes": [ + "6231CFA6BE243E92EC33050DC23C6E8EC972F22A111D96328873207A7CCCC7C7", + "35FB1D334ECCD52B94253E7A33BA37C3D845E26F11FDEC08A56527C92907C3AC" + ] + } + }, + { + "binary": "1100612200000000240000000125000000302D000000005564F24DBD7EEBAF80F204C70EF972EC884EF4192F0D9D579588F5DA740D7199F66240000000773594008114CC4F138AF6612C3600A82D356D346D000094A27B", + "json": { + "OwnerCount": 0, + "Account": "rKdH2TKVGjoJkrE8zQKosL2PCvG2LcPzs5", + "PreviousTxnLgrSeq": 48, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "64F24DBD7EEBAF80F204C70EF972EC884EF4192F0D9D579588F5DA740D7199F6", + "Flags": 0, + "Sequence": 1, + "Balance": "2000000000" + } + }, + { + "binary": "11006F22000000002400000007250000459B33000000000000000034000000000000000055433789526B3A7A57B6402A867815A44F1F12800E552E581FA38EC6360471E5A450102FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82144F0415EB4EA0C72764D4871AFD498D000000000000000000000000000055534400000000002B6C42A95B3F7EE1971E4A10098E8F1B5F66AA086540000000001A897A811462FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "json": { + "TakerPays": { + "currency": "USD", + "value": "2", + "issuer": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy" + }, + "Account": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j", + "PreviousTxnLgrSeq": 17819, + "BookDirectory": "2FB4904ACFB96228FC002335B1B5A4C5584D9D727BBE82144F0415EB4EA0C727", + "LedgerEntryType": "Offer", + "OwnerNode": "0000000000000000", + "PreviousTxnID": "433789526B3A7A57B6402A867815A44F1F12800E552E581FA38EC6360471E5A4", + "TakerGets": "1739130", + "Flags": 0, + "Sequence": 7, + "BookNode": "0000000000000000" + } + }, + { + "binary": "11006F2200000000240000000925000045AB33000000000000000034000000000000000055DAB224E1847C8F0D69F77F53F564CCCABF25BE502128B34D772B1A8BB91E613C501062AE37A44FE44BDCFC2BA5DD14D74BEC0AC346DA2DC1F04756044364C5BB000064D544B088731A80000000000000000000000000004A505900000000002B6C42A95B3F7EE1971E4A10098E8F1B5F66AA0865D503E871B540C000000000000000000000000000555344000000000062FE474693228F7F9ED1C5EFADB3B6555FBEAFBE811462FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "json": { + "TakerPays": { + "currency": "JPY", + "value": "1320", + "issuer": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy" + }, + "Account": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j", + "PreviousTxnLgrSeq": 17835, + "BookDirectory": "62AE37A44FE44BDCFC2BA5DD14D74BEC0AC346DA2DC1F04756044364C5BB0000", + "LedgerEntryType": "Offer", + "OwnerNode": "0000000000000000", + "PreviousTxnID": "DAB224E1847C8F0D69F77F53F564CCCABF25BE502128B34D772B1A8BB91E613C", + "TakerGets": { + "currency": "USD", + "value": "110", + "issuer": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j" + }, + "Flags": 0, + "Sequence": 9, + "BookNode": "0000000000000000" + } + }, + { + "binary": "1100612200000000240000000125000000312D0000000055C3157F699F23C4ECBA78442D68DDCAB29AE1C54C3EC9302959939C4B34913BE86240000002540BE40081148484558E9640B69D082861AE0FC8081858FA4D45", + "json": { + "OwnerCount": 0, + "Account": "rDngjhgeQZj9FNtW8adgHvdpMJtSBMymPe", + "PreviousTxnLgrSeq": 49, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "C3157F699F23C4ECBA78442D68DDCAB29AE1C54C3EC9302959939C4B34913BE8", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110072220002000025000000F0550C164A054712296CB0946EEC6F3BF43DFE94662DA03238AABF4C1B13C32DAC2462800000000000000000000000000000000000000043414400000000000000000000000000000000000000000000000001668000000000000000000000000000000000000000434144000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC67D4C38D7EA4C680000000000000000000000000004341440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 240, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "CAD", + "value": "0", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "0C164A054712296CB0946EEC6F3BF43DFE94662DA03238AABF4C1B13C32DAC24", + "Flags": 131072, + "Balance": { + "currency": "CAD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "CAD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "11006422000000003656044364C5BB00005862AE37A44FE44BDCFC2BA5DD14D74BEC0AC346DA2DC1F04756044364C5BB000001110000000000000000000000004A5059000000000002112B6C42A95B3F7EE1971E4A10098E8F1B5F66AA0803110000000000000000000000005553440000000000041162FE474693228F7F9ED1C5EFADB3B6555FBEAFBE011320600A398F57CAE44461B4C8C25DE12AC289F87ED125438440B33B97417FE3D82C", + "json": { + "TakerPaysCurrency": "0000000000000000000000004A50590000000000", + "ExchangeRate": "56044364C5BB0000", + "TakerGetsCurrency": "0000000000000000000000005553440000000000", + "TakerGetsIssuer": "62FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "62AE37A44FE44BDCFC2BA5DD14D74BEC0AC346DA2DC1F04756044364C5BB0000", + "Indexes": ["600A398F57CAE44461B4C8C25DE12AC289F87ED125438440B33B97417FE3D82C"], + "TakerPaysIssuer": "2B6C42A95B3F7EE1971E4A10098E8F1B5F66AA08" + } + }, + { + "binary": "1100612200000000240000000225000022C52D00000001558D7F42ED0621FBCFAE55CC6F2A9403A2AFB205708CCBA3109BB61DB8DDA261B46240000000160DC0768114BF3389DD51B5B8CC5AEA410403036A2990896C70", + "json": { + "OwnerCount": 1, + "Account": "rJRyob8LPaA3twGEQDPU2gXevWhpSgD8S6", + "PreviousTxnLgrSeq": 8901, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "8D7F42ED0621FBCFAE55CC6F2A9403A2AFB205708CCBA3109BB61DB8DDA261B4", + "Flags": 0, + "Sequence": 2, + "Balance": "369999990" + } + }, + { + "binary": "1100612200000000240000000225000007EA2D0000000155A39F6B89F50033153C9CC1233BB175BE52685A31AE038A58BEC1A88898E834206240000002540BE3F681140B8D970A5E6BB9C50EC063E9318C7218D65ECE43", + "json": { + "OwnerCount": 1, + "Account": "rphasxS8Q5p5TLTpScQCBhh5HfJfPbM2M8", + "PreviousTxnLgrSeq": 2026, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "A39F6B89F50033153C9CC1233BB175BE52685A31AE038A58BEC1A88898E83420", + "Flags": 0, + "Sequence": 2, + "Balance": "9999999990" + } + }, + { + "binary": "110072220001000025000079CB37000000000000000038000000000000000055D612015F70931DC1CE2D65713408F0C4EE6230911F52E08678898D24C888A43A6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5438D7EA4C6800000000000000000000000000055534400000000007588B8DBDC8932DC410E8571045466C03F5A6B696780000000000000000000000000000000000000005553440000000000C2225C8CBC2B6D0E53C763B53A9AC66C658A4EEC", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 31179, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "1000", + "issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY" + }, + "PreviousTxnID": "D612015F70931DC1CE2D65713408F0C4EE6230911F52E08678898D24C888A43A", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rJ6VE6L87yaVmdyxa9jZFXSAdEFSoTGPbE" + } + } + }, + { + "binary": "11006122000000002400000001250000004F2D0000000055523944DE44018CB5D3F2BB38FE0F54BF3953669328CEF1CF4751E91034B553FE62400009184E72A00081143F3DDC9342B131C3B20436F75DF3D1C691A0ADCF", + "json": { + "OwnerCount": 0, + "Account": "ramPgJkA1LSLevMg2Yrs1jWbqPTsSbbYHQ", + "PreviousTxnLgrSeq": 79, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "523944DE44018CB5D3F2BB38FE0F54BF3953669328CEF1CF4751E91034B553FE", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "110061220000000024000000012500000E942D0000000055D994F257D0E357601AE60B58D7066906B6112D49771AD8F0A4B27F59C74A44156240038D7EA4C6800081141DEAA7EE1C600201BDADF21B7EC549BCF9E200C9", + "json": { + "OwnerCount": 0, + "Account": "rsjB6kHDBDUw7iB5A1EVDK1WmgmR6yFKpB", + "PreviousTxnLgrSeq": 3732, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D994F257D0E357601AE60B58D7066906B6112D49771AD8F0A4B27F59C74A4415", + "Flags": 0, + "Sequence": 1, + "Balance": "1000000000000000" + } + }, + { + "binary": "110061220000000024000000012500000EA92D00000000554B03D5A4EEFCAF8ADB8488E9FD767A69C6B5AC3020E8FF2128A8CBB8A4BA0AFF6240003691D6AFC000811433F791D7AA1775BDD6DD7AD89558AFF1054C765B", + "json": { + "OwnerCount": 0, + "Account": "rnj8sNUBCw3J6sSstY9QDDoncnijFwH7Cs", + "PreviousTxnLgrSeq": 3753, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4B03D5A4EEFCAF8ADB8488E9FD767A69C6B5AC3020E8FF2128A8CBB8A4BA0AFF", + "Flags": 0, + "Sequence": 1, + "Balance": "60000000000000" + } + }, + { + "binary": "1100682200000000201A00000100201B000094000213D2BF46CA85D119A4FDF7644339663A813131C791BD21472BB85C526E9E4072F87ABA30E34F73546C514C1BD389E1A71FBC266DCED3FC1DB7A186F3F0FCF1174845284EB7F83E4AE050D7C46744FC40C7C506E2D2468F520A1CF50325406231AB7BA0D7349AEAE4337A11FDF98A60F01CEDD7CA38CE8835FE66CCA2E4B227928A2AF5AA4CD783DE0238A741BE8C0FEFCBC37E8A07C75195919F617749D02ED86485819AB0D5C6ED47FBE7513253A012431D5BDEE6E9FE19D6E39A537C72212FDCBA53BF4836310428AE05209D703DB7BA805DBD309959DDFB1A013666CADFB3B02C2420D40412CA443B8972F7EFBD79D5ABA60A68744C75689A75050ECDE3106AE60FE1A81A0AC6B45B488E4D2EEA57E9D04BBFB078B4B72837C8754599D25C4C8D3B188FC5B7DC6F0CE992BA19ED4405BA8B3399FA69485CDEDE42F1BED9E4355447EA8C6297E23798A28D4D03782CFF274C45832141D2A084C6D550251D40C6F5E3B45FD83C30ACB19688E9F482C2BC5EA99242431DDFC761266FCE88FD5FFB4B22142F2DC1D72345824169FC14971E40154268A393AC0CEC260AAB171E77992218A464B55B61046CED55778A3F23AB49908EDC310223E74F4EFFAE195602655CDAE1532980D3BAA96CBE31A35DA8D85C4187DD2DC9285168D0BEFAB23AB7AFCA6CE57645836A7238265D735DCC3318446B513D95405759044EE367DA3933F902EB71BCE9D7C1CFFE3E8843FF25A3DAD31384AB561A91F4A0F074F5210F5E6BCA7703FD3FF1D27D5E4E9768B03A9438DC39827F34E559A82FD8FE3E0894ADF122A04E1F327BA0FAB0536C023A36ADC9226411FF4C7854CB73BA7230484D3B26A4FDDF40D6608787FFB9B9324C3E40388500CAF4B3CFC4C1857A7806C421D86787B40A80A82CE9A46DBFA6423FFC1D6B0FE0E05ECA4DC379D4A24E5AB78EED5D8D54E439B3FEF555EA03AFB0D518942C42EB546934B8DD57D8C531EA1A6EDCEF3D0E2153861CFA542E2AE593B38F1793268138D0EB462E21D45F9A40A84A12F42D19E1025986FB51465C5226BFF003F5C03D25C8C2D37A82D8A389EF536CF18F58150EF09ED9B4651FA200C7CFA76186B7F418B2F3C4838EEBC34AF679AC83F04DA5E0798890BD8C632C9B184BAC912129A000C21D9C261238CFFD49B85231F8691E0B666FF1E19633C145695FB01C8FC10EA5F51CB230ABD781F6306334D83DD4BCBA16AECB9E2CF8D68D31675C4B17A7D5A3E12ABAB9C47EA48942FBEC8BE574D541EA17AFDD8A096C2C37AEB30D6C840E394E9B33F1A23B46A6E5A0B458E602E79B359B65D509275EF1F8F97C90CCA7F1538689968C94BA5F93353E57EB1B4DEF8CEA4D27D9DAF3158A0DBB68FE7FB78CD9A01E880070EE5AD3F7C172B860F0CDC80FD9750847F44BBFE0A36A6ADC1653F48EDA3DFF515ED45C5D1DB455686138BE25E0085DDB6A18232448CE2CBFC4D9DADBC2613B026A334FE234811DCC2DCF08A08EA0C5D1E13BCD5F7032C1E27F0856EA10BDCDC163DF0FB396FE2CABA31BA68967254F794A6B6ADCF83A7FFA225276F3ADF81E23E2E9DBB48B103F4F287AD901F12F3B934543752F66F62DE2F70261499879611F3536C4C9F38CFED34FA2CF03209B2601483CCE4A08D000CE4B51B851CE8AC0E426675987BE5CF803C18CBA6878132CFDF8C68056875B00E33EF08FCF7BEA2A62193C36DE3B1875EF3B9D2A566045F7A7A243BAC56BE8EC924F02339E3C76F8DC7E046F7C11AA8C342B404AF051B5585F6222321E5F5B850353C03E0D63DAF5E143040471B444ABB728254A97A57376069E1257020D5501112A14CC01E9D0F05100C957C7BEDE339E50F837C67F8F6D16F25E5D4667934137D34723750B5D9FE83166A7D6D76B36C8C179707E9AF5FBFB4F9C49CF86A9574F5D92E2A116A33BC9DE99718880289A0788D9B42F73034086D2F0EBC448F712C593A03C4BEBFB8744D3BAD3E09A20F01828A30A2F54078737CFB3DC5926C59953D554A8694EF61B3636DAC92EBD43388889E270924BD1A23B8E5507FE9926CB313EA8B1DE0448A56745323431406CA16274FD05575230980F40E49E56A104C14F1E782D1BEF983698F5D362B5F07DE0D428E8FB1C5932BE70A69CCF6DBA76107FAA3BA1007ED13DFD1AAB17FC425A10EB158125FC22191B032F57E2FA33BE3479C2BDEA11CF9E2E2EFF708B27A87B1B523A89211A029A97B7F8B3BBCA0E522BDF59E38E9A31D81BE9E992D29DE8F2D4E22A4F3D854A51048D99BABF0007FBF7B689E3E098C30203BF6D1DA3D5E331EC14DD9822393FDC141993373E998910AEBCC3A325208E6C2F5AF5F0EA89AF8E707035BB48F51EB63247C8444E30FBF1C96C82732078EF017FD24E50F153701762D1C73C1933B26C54C13B95D4F5BB231C5C6C815F1CC549BB566176C69685FAB89B2D3018B0E6978BDA0F820ECCC2812D3BCBA26B5FCD82162BE4ECFBB2B37F4202022CAE268627782C85AE0B585DEBB70A360A25325163E052DEFC778B12D6F5F3140DA9D9251250BEA2908137E2BF02B5265489682CC0767ABDDEFEF2081AA400BE22E3D86A55F1C1247128CBDD92DCD6825ABA2B1A0CF11E708D4B7A095691FA921266ACAE0C1C0131C3C3E44F97EE718CF06D477745BBC5C1E62B5FAA1E64755C4064424CDBB7213281B55264AC5EDDBF8E3C08DC9F91AC522BC27F54E6927AEBF5E99D194A57372231B580720BFF3FBF97C9012E0FE1F24B9B8B1D4B3F712E0FFD28D45AD772BB438DB55ADC3F3E10A661C2386D530E7B818D66705830E642BACFF4BD92C5D43BD66D9C88CD8F7155CD9D4239D4BFDEAE39A59D508C55E94D92C9BB4569ABCAE972A58BBE235AA15804A1A5C338A88D58105EE156E691379FF6D6700B19B81823E8F5772800F7E43049733D5B0704DD2D1FE381D698BB80DD72619EA049B105956ECD1848EFDBF1BA5AC77200254D3B200B59A520216BF52F8B33E037EF16B6EC5247FF05EE5F2D3541E4F7DDB584FDB54369D89ED8C4F169A1BAFB5B4ACAF66451AD44AFC25E52A8883AD8F7C2D92A399BD06728AB949A0C254328416DC35EC84994E022398FE3D33AA5ECFB32402F3ECE8AE5AD44795653D80F9D65AA309D29A33CC11CF9A862764981A66ECAB7CD37857F7D088AF0E670117C04855D0B5B7C7BF3BF670C2E2A623AF02AABE2F1DAC85B81B63D7BB980FAE348819D06F863296E89D4B3D48E14A5B227EC89053F8CC9FE15029E22C34C912B927195B5FA6F039200B3F9A1C9E831502C9AA22794D8CEF4C35F72399B0C4B6F42D17CFB13B3657BED7CB506DB48258ED0DC0ABE9B6D04C75030DF208BE07EECA08930F9420CF861AD268B206BFCAA3BAB1D28906E43B6C4F0297D1D6579D58109131945F850C00D65D13712F64B41B1DFD7D6649AD78B3ADDCDC0AB51FB0A2FC2811B87B783CD76B9E612B867B355FB8CC4ABDAE9FA302C532733C41B52AB5FFE439CE2133E0C5DF12A0DB86AFF23D2D0CE8ADFEABDF2E2F3F568D58DA7A1C2778CEC9F269F28CD00FE9B4811F841BEE5E3937AC81EA30D17207CEEC9832091FB2EB319E8D384357255F300CC78E82FF7ECF84949A07D7043AE67645DD0D1708ADC6F10FD8B4E1BAA925BD1919BE70DF251B192B72D5CBF1BD42C69B5F9D9E33A7B2B336FB5149DB963FC0C84EEBD4271B7DC33A79FACCDB9CD3C98F821B8C11C5F8B5CF6AEE7ADF8FA23122E2AF6BBAD77E50D077483B545A9B6EBF6ECF13FC50C43B20F1A457970C8CEFAC5C1642EA8996BBD70DD2109AAD84E4D33CD1A97F3777E49B89E8C2D06ADF2F36817BB029F52A03469B71821F6EF77B6907611486BD91A0474F4B64E36D374C2AF78ADF85BA5844EDF4E72056944015B3349532A2977B609A5BC8BD805D581B06C2423E90C618C68484166632702DB08B0184C3B2605E41DCF6ED8D6154EF0D6AC8FD7302561E69DB1A8938C0AF9CC947343D80DED6C3AEB3E66F133591E6D20412B1816EAED5AF5643CB51D06188D36294AA9758CDD76DE696BDFEC3F18EAA16C17B78E9C8C5B56FA0FEB224B579A589C983F549C01502B404586D235FBB5FECB8D1CDE64F865AA1DA2A5EC877374DE717FEBF4A0FAB3679AE0D51A0BCB4AF004228AF5DB6DBD42CD1B0415BE5A83D282F6B448A1C7E8C01A3F5D0756F393CE2D1A14073EA0E4126D0170E04FE2F9CB97EF3A3C862A6419BD4F9111CC03F4B0EB459B902B69F0A685FCB20E5ACAE24903113FF2186BF67DCAC27D3E57F3B85FECDC1C6E6C70CE26EF27136EF38C45E6794BE9A40F2FD59B8594521D4F2DA9004B493BFA87C387694111C08BDFD66E69AEE4752BCF887647702EBA5D91A05A5FAAF36A4793CF8B6292612A96539F80FEB5D67A6CABB613E7A7A119E99DFF72B7B6F67906B211F95FF679686F65EDF5E32047FEBC60C010B145685BCBD6CF1CED7E42E8950910B9DD01D0D72BBE0EA9A52464386EC04564B180267F9DE53016E25E8D8AA20F9ACCAC4E76BAE60BACC8ACA70E766DD13CB16E7A33032CA80E0F935665304CEC1E61EE9BBB4B7A51E4B2708E6E7FAB7E96BF360D0FE736D520582AE5574A9FB6D482810F37B12504C7754FB9B0368D10F116AB72EBE3F85B7715599C6F88165550F88948883D293326D7A3A37607AD7F338BFAD9143B14AACDF99F0F16C9819585903EE264DFCA996FC0E29644EB26164B7EB8D620A38925EB26EB7BFE1D0D8A9F7A3F542CA79F25443C051349F75597D2F7152FC9E0B243A0AA33B1DF2B8AD0229D6BA08129A5E1BE79EF339017234E3EA85163CFA860E378792CC9C0F97B7B6D1A67A3E10A1D19D506EF4A07784CE9BBC59E15FEB70E5BA51B37DEADE73F386F3F6C32BF01E964C21ADEA961156A05A783B441DABD6E8FB82617396E676039714CDD55530AEFE9FA1950DDFB12D19C0983655D0A7A0A2EA9F47F4621098B62BD2350E3721F6126D852E14628F75B495E772F93AB27ED7ABC015E65DAC1636C4BEE65161B0C365E54C7B4E7253D085A73850E9B09C456566B1233F7E3E99A6C8A8DAC815351CAA8647733AFCDA89242EB2E44652F1D48125B2B1D765EEC7E666CD61774A09DC5062332975B1E5BB9CEB067B4B47A15BC454EAF10F1528CDA034050E14ED6B4C7B8A69152252EB7EC5961D7F81182F42A97B32AE2CA5FF818C6D6BD1252822C9DE96412CF9C44C26E5284BAF99B7C240088590D793FB2AB3E0CFD7EC8A22771D78870FEAF902FBCA3CA47ED82D33D08A086409370EE11344E1429DC1072D053FA84C5047186E09E94152E8A8D8C7DB6156C707260A1B9C80B79C7F2916C52267925A04D24B91BCDA57189ADAF9ECA28CEB75C9DFAD092A6028FB6F9EC8B0AB1816B7446881E59BF1E5AD6A2446D0A74397534CDCB9F6B27C3846A43914FF88C1487878A608B0AF9736102EB8D561A1FB6AD0443C7EA50978129BE3013E627696E35F4A86AA2664EA6C760D8452D3D1329802051FF7D64750EF89D0FA7F52407D27DCAAD4FC9866AB3C648A6817C81ADD8E803604C3A907BB5B98D1545FED8E69542EB3CF8FD5DDE956416564946C4C731287922FA4E1E7E8F619F0A4F1BACE642D8D9C914D0A992C3F0C59D15488844223AC052BB01A3A547B2C378E11D1DBBC037F1A924A58BB3A7A5CC2E8827C0FCAB29E90F466E22AAA9E54C95D214517AE6824361CF6C4B1A1DA943E701475E1A25107B4585645A83CD942B8644C65111193DABC1D148C4CF6E182F29FEEFF9D59C9DFE325A86C127677EB619B69D8337A6924DC99541199F288095713AB9878E9CC9F459ED06E2E8AE08B3AA7059690AB7ABE9AF5E6B03F974C5B9761DF3B6A17C9223F17619A31CC7E751CA43F79FF97517A0379F9A0970BDD5D23B27C92B27A8349B9DC838C1060445D4A5F11036A1ECB731B4F1A8A2FEE68A2A9230932745154A632BE6E8BC02BDA70ACC63BA35CD5D9CB7FE8037C5E17B5A5EB24E22E303BAB86456FC00C414F274C138C4809B2C4BB1A60B824F22D13E49D8212A0EDCACC94A5DD9BA8862DEA437206E1D6D98A080FA6FC81AE907FE52632DF6D2E2D633407B98809F32B1EB0A4145385F9F81CE5F7B0BC2E1E6B0F809C983C1EECD541CDED8F7DCA5118B402156AC50F5130CC55E8A34740EF3BCEF445DE3DD4311EC37E750D62B5AA6BEBB6007D7FE652155B2B8CA52B59DC58BDF5E7F1CFDBAB1465C114335BCE962621B3B9F3E464817F7403031FB5558A2B4A514E9A35860460AD1096EBCBFF7E8C0A7086594C18BC0AA5F7CB23C1E40FF22FC133F09FE0839286B47089EA34D465C2089EC205B40604EE51725E697035DE58134B6A3CC048DB8E7F433B86C311D6226EA64BE2BA0137B818EF35BE98B3E900DA88ADB764B3E83DEE68022BD886D5CC1276B31FBBBEDA3186E5E1BDE2EEC1B8E8C9E83485A81ECBA2E8E2EF0B6A1DA4D730D863E32E2CA46FBB8415E9621799984F8269DC791F56AACAEF6CEA04C6C99450345D3D692A2F74E38B9CC161C182BB1BC23FBE8EE73A47376CD8799610ADA8509539B480BE3D9E280DB83CF2AF6DC9DB538E7AB66E8C93173DE7373DA4A616E458DF4196E140C8EFAABDF21B7D4BE9741FB0EA9CF94A19B0980A109E07D60FC009042940D79EB7A6C611FEEBD4E59049AAB0FA33B50D992508072E9AB22AA9267A52B220E9A1340A950DCA9884561A6D7", + "json": { + "Hashes": [ + "46CA85D119A4FDF7644339663A813131C791BD21472BB85C526E9E4072F87ABA", + "30E34F73546C514C1BD389E1A71FBC266DCED3FC1DB7A186F3F0FCF117484528", + "4EB7F83E4AE050D7C46744FC40C7C506E2D2468F520A1CF50325406231AB7BA0", + "D7349AEAE4337A11FDF98A60F01CEDD7CA38CE8835FE66CCA2E4B227928A2AF5", + "AA4CD783DE0238A741BE8C0FEFCBC37E8A07C75195919F617749D02ED8648581", + "9AB0D5C6ED47FBE7513253A012431D5BDEE6E9FE19D6E39A537C72212FDCBA53", + "BF4836310428AE05209D703DB7BA805DBD309959DDFB1A013666CADFB3B02C24", + "20D40412CA443B8972F7EFBD79D5ABA60A68744C75689A75050ECDE3106AE60F", + "E1A81A0AC6B45B488E4D2EEA57E9D04BBFB078B4B72837C8754599D25C4C8D3B", + "188FC5B7DC6F0CE992BA19ED4405BA8B3399FA69485CDEDE42F1BED9E4355447", + "EA8C6297E23798A28D4D03782CFF274C45832141D2A084C6D550251D40C6F5E3", + "B45FD83C30ACB19688E9F482C2BC5EA99242431DDFC761266FCE88FD5FFB4B22", + "142F2DC1D72345824169FC14971E40154268A393AC0CEC260AAB171E77992218", + "A464B55B61046CED55778A3F23AB49908EDC310223E74F4EFFAE195602655CDA", + "E1532980D3BAA96CBE31A35DA8D85C4187DD2DC9285168D0BEFAB23AB7AFCA6C", + "E57645836A7238265D735DCC3318446B513D95405759044EE367DA3933F902EB", + "71BCE9D7C1CFFE3E8843FF25A3DAD31384AB561A91F4A0F074F5210F5E6BCA77", + "03FD3FF1D27D5E4E9768B03A9438DC39827F34E559A82FD8FE3E0894ADF122A0", + "4E1F327BA0FAB0536C023A36ADC9226411FF4C7854CB73BA7230484D3B26A4FD", + "DF40D6608787FFB9B9324C3E40388500CAF4B3CFC4C1857A7806C421D86787B4", + "0A80A82CE9A46DBFA6423FFC1D6B0FE0E05ECA4DC379D4A24E5AB78EED5D8D54", + "E439B3FEF555EA03AFB0D518942C42EB546934B8DD57D8C531EA1A6EDCEF3D0E", + "2153861CFA542E2AE593B38F1793268138D0EB462E21D45F9A40A84A12F42D19", + "E1025986FB51465C5226BFF003F5C03D25C8C2D37A82D8A389EF536CF18F5815", + "0EF09ED9B4651FA200C7CFA76186B7F418B2F3C4838EEBC34AF679AC83F04DA5", + "E0798890BD8C632C9B184BAC912129A000C21D9C261238CFFD49B85231F8691E", + "0B666FF1E19633C145695FB01C8FC10EA5F51CB230ABD781F6306334D83DD4BC", + "BA16AECB9E2CF8D68D31675C4B17A7D5A3E12ABAB9C47EA48942FBEC8BE574D5", + "41EA17AFDD8A096C2C37AEB30D6C840E394E9B33F1A23B46A6E5A0B458E602E7", + "9B359B65D509275EF1F8F97C90CCA7F1538689968C94BA5F93353E57EB1B4DEF", + "8CEA4D27D9DAF3158A0DBB68FE7FB78CD9A01E880070EE5AD3F7C172B860F0CD", + "C80FD9750847F44BBFE0A36A6ADC1653F48EDA3DFF515ED45C5D1DB455686138", + "BE25E0085DDB6A18232448CE2CBFC4D9DADBC2613B026A334FE234811DCC2DCF", + "08A08EA0C5D1E13BCD5F7032C1E27F0856EA10BDCDC163DF0FB396FE2CABA31B", + "A68967254F794A6B6ADCF83A7FFA225276F3ADF81E23E2E9DBB48B103F4F287A", + "D901F12F3B934543752F66F62DE2F70261499879611F3536C4C9F38CFED34FA2", + "CF03209B2601483CCE4A08D000CE4B51B851CE8AC0E426675987BE5CF803C18C", + "BA6878132CFDF8C68056875B00E33EF08FCF7BEA2A62193C36DE3B1875EF3B9D", + "2A566045F7A7A243BAC56BE8EC924F02339E3C76F8DC7E046F7C11AA8C342B40", + "4AF051B5585F6222321E5F5B850353C03E0D63DAF5E143040471B444ABB72825", + "4A97A57376069E1257020D5501112A14CC01E9D0F05100C957C7BEDE339E50F8", + "37C67F8F6D16F25E5D4667934137D34723750B5D9FE83166A7D6D76B36C8C179", + "707E9AF5FBFB4F9C49CF86A9574F5D92E2A116A33BC9DE99718880289A0788D9", + "B42F73034086D2F0EBC448F712C593A03C4BEBFB8744D3BAD3E09A20F01828A3", + "0A2F54078737CFB3DC5926C59953D554A8694EF61B3636DAC92EBD43388889E2", + "70924BD1A23B8E5507FE9926CB313EA8B1DE0448A56745323431406CA16274FD", + "05575230980F40E49E56A104C14F1E782D1BEF983698F5D362B5F07DE0D428E8", + "FB1C5932BE70A69CCF6DBA76107FAA3BA1007ED13DFD1AAB17FC425A10EB1581", + "25FC22191B032F57E2FA33BE3479C2BDEA11CF9E2E2EFF708B27A87B1B523A89", + "211A029A97B7F8B3BBCA0E522BDF59E38E9A31D81BE9E992D29DE8F2D4E22A4F", + "3D854A51048D99BABF0007FBF7B689E3E098C30203BF6D1DA3D5E331EC14DD98", + "22393FDC141993373E998910AEBCC3A325208E6C2F5AF5F0EA89AF8E707035BB", + "48F51EB63247C8444E30FBF1C96C82732078EF017FD24E50F153701762D1C73C", + "1933B26C54C13B95D4F5BB231C5C6C815F1CC549BB566176C69685FAB89B2D30", + "18B0E6978BDA0F820ECCC2812D3BCBA26B5FCD82162BE4ECFBB2B37F4202022C", + "AE268627782C85AE0B585DEBB70A360A25325163E052DEFC778B12D6F5F3140D", + "A9D9251250BEA2908137E2BF02B5265489682CC0767ABDDEFEF2081AA400BE22", + "E3D86A55F1C1247128CBDD92DCD6825ABA2B1A0CF11E708D4B7A095691FA9212", + "66ACAE0C1C0131C3C3E44F97EE718CF06D477745BBC5C1E62B5FAA1E64755C40", + "64424CDBB7213281B55264AC5EDDBF8E3C08DC9F91AC522BC27F54E6927AEBF5", + "E99D194A57372231B580720BFF3FBF97C9012E0FE1F24B9B8B1D4B3F712E0FFD", + "28D45AD772BB438DB55ADC3F3E10A661C2386D530E7B818D66705830E642BACF", + "F4BD92C5D43BD66D9C88CD8F7155CD9D4239D4BFDEAE39A59D508C55E94D92C9", + "BB4569ABCAE972A58BBE235AA15804A1A5C338A88D58105EE156E691379FF6D6", + "700B19B81823E8F5772800F7E43049733D5B0704DD2D1FE381D698BB80DD7261", + "9EA049B105956ECD1848EFDBF1BA5AC77200254D3B200B59A520216BF52F8B33", + "E037EF16B6EC5247FF05EE5F2D3541E4F7DDB584FDB54369D89ED8C4F169A1BA", + "FB5B4ACAF66451AD44AFC25E52A8883AD8F7C2D92A399BD06728AB949A0C2543", + "28416DC35EC84994E022398FE3D33AA5ECFB32402F3ECE8AE5AD44795653D80F", + "9D65AA309D29A33CC11CF9A862764981A66ECAB7CD37857F7D088AF0E670117C", + "04855D0B5B7C7BF3BF670C2E2A623AF02AABE2F1DAC85B81B63D7BB980FAE348", + "819D06F863296E89D4B3D48E14A5B227EC89053F8CC9FE15029E22C34C912B92", + "7195B5FA6F039200B3F9A1C9E831502C9AA22794D8CEF4C35F72399B0C4B6F42", + "D17CFB13B3657BED7CB506DB48258ED0DC0ABE9B6D04C75030DF208BE07EECA0", + "8930F9420CF861AD268B206BFCAA3BAB1D28906E43B6C4F0297D1D6579D58109", + "131945F850C00D65D13712F64B41B1DFD7D6649AD78B3ADDCDC0AB51FB0A2FC2", + "811B87B783CD76B9E612B867B355FB8CC4ABDAE9FA302C532733C41B52AB5FFE", + "439CE2133E0C5DF12A0DB86AFF23D2D0CE8ADFEABDF2E2F3F568D58DA7A1C277", + "8CEC9F269F28CD00FE9B4811F841BEE5E3937AC81EA30D17207CEEC9832091FB", + "2EB319E8D384357255F300CC78E82FF7ECF84949A07D7043AE67645DD0D1708A", + "DC6F10FD8B4E1BAA925BD1919BE70DF251B192B72D5CBF1BD42C69B5F9D9E33A", + "7B2B336FB5149DB963FC0C84EEBD4271B7DC33A79FACCDB9CD3C98F821B8C11C", + "5F8B5CF6AEE7ADF8FA23122E2AF6BBAD77E50D077483B545A9B6EBF6ECF13FC5", + "0C43B20F1A457970C8CEFAC5C1642EA8996BBD70DD2109AAD84E4D33CD1A97F3", + "777E49B89E8C2D06ADF2F36817BB029F52A03469B71821F6EF77B6907611486B", + "D91A0474F4B64E36D374C2AF78ADF85BA5844EDF4E72056944015B3349532A29", + "77B609A5BC8BD805D581B06C2423E90C618C68484166632702DB08B0184C3B26", + "05E41DCF6ED8D6154EF0D6AC8FD7302561E69DB1A8938C0AF9CC947343D80DED", + "6C3AEB3E66F133591E6D20412B1816EAED5AF5643CB51D06188D36294AA9758C", + "DD76DE696BDFEC3F18EAA16C17B78E9C8C5B56FA0FEB224B579A589C983F549C", + "01502B404586D235FBB5FECB8D1CDE64F865AA1DA2A5EC877374DE717FEBF4A0", + "FAB3679AE0D51A0BCB4AF004228AF5DB6DBD42CD1B0415BE5A83D282F6B448A1", + "C7E8C01A3F5D0756F393CE2D1A14073EA0E4126D0170E04FE2F9CB97EF3A3C86", + "2A6419BD4F9111CC03F4B0EB459B902B69F0A685FCB20E5ACAE24903113FF218", + "6BF67DCAC27D3E57F3B85FECDC1C6E6C70CE26EF27136EF38C45E6794BE9A40F", + "2FD59B8594521D4F2DA9004B493BFA87C387694111C08BDFD66E69AEE4752BCF", + "887647702EBA5D91A05A5FAAF36A4793CF8B6292612A96539F80FEB5D67A6CAB", + "B613E7A7A119E99DFF72B7B6F67906B211F95FF679686F65EDF5E32047FEBC60", + "C010B145685BCBD6CF1CED7E42E8950910B9DD01D0D72BBE0EA9A52464386EC0", + "4564B180267F9DE53016E25E8D8AA20F9ACCAC4E76BAE60BACC8ACA70E766DD1", + "3CB16E7A33032CA80E0F935665304CEC1E61EE9BBB4B7A51E4B2708E6E7FAB7E", + "96BF360D0FE736D520582AE5574A9FB6D482810F37B12504C7754FB9B0368D10", + "F116AB72EBE3F85B7715599C6F88165550F88948883D293326D7A3A37607AD7F", + "338BFAD9143B14AACDF99F0F16C9819585903EE264DFCA996FC0E29644EB2616", + "4B7EB8D620A38925EB26EB7BFE1D0D8A9F7A3F542CA79F25443C051349F75597", + "D2F7152FC9E0B243A0AA33B1DF2B8AD0229D6BA08129A5E1BE79EF339017234E", + "3EA85163CFA860E378792CC9C0F97B7B6D1A67A3E10A1D19D506EF4A07784CE9", + "BBC59E15FEB70E5BA51B37DEADE73F386F3F6C32BF01E964C21ADEA961156A05", + "A783B441DABD6E8FB82617396E676039714CDD55530AEFE9FA1950DDFB12D19C", + "0983655D0A7A0A2EA9F47F4621098B62BD2350E3721F6126D852E14628F75B49", + "5E772F93AB27ED7ABC015E65DAC1636C4BEE65161B0C365E54C7B4E7253D085A", + "73850E9B09C456566B1233F7E3E99A6C8A8DAC815351CAA8647733AFCDA89242", + "EB2E44652F1D48125B2B1D765EEC7E666CD61774A09DC5062332975B1E5BB9CE", + "B067B4B47A15BC454EAF10F1528CDA034050E14ED6B4C7B8A69152252EB7EC59", + "61D7F81182F42A97B32AE2CA5FF818C6D6BD1252822C9DE96412CF9C44C26E52", + "84BAF99B7C240088590D793FB2AB3E0CFD7EC8A22771D78870FEAF902FBCA3CA", + "47ED82D33D08A086409370EE11344E1429DC1072D053FA84C5047186E09E9415", + "2E8A8D8C7DB6156C707260A1B9C80B79C7F2916C52267925A04D24B91BCDA571", + "89ADAF9ECA28CEB75C9DFAD092A6028FB6F9EC8B0AB1816B7446881E59BF1E5A", + "D6A2446D0A74397534CDCB9F6B27C3846A43914FF88C1487878A608B0AF97361", + "02EB8D561A1FB6AD0443C7EA50978129BE3013E627696E35F4A86AA2664EA6C7", + "60D8452D3D1329802051FF7D64750EF89D0FA7F52407D27DCAAD4FC9866AB3C6", + "48A6817C81ADD8E803604C3A907BB5B98D1545FED8E69542EB3CF8FD5DDE9564", + "16564946C4C731287922FA4E1E7E8F619F0A4F1BACE642D8D9C914D0A992C3F0", + "C59D15488844223AC052BB01A3A547B2C378E11D1DBBC037F1A924A58BB3A7A5", + "CC2E8827C0FCAB29E90F466E22AAA9E54C95D214517AE6824361CF6C4B1A1DA9", + "43E701475E1A25107B4585645A83CD942B8644C65111193DABC1D148C4CF6E18", + "2F29FEEFF9D59C9DFE325A86C127677EB619B69D8337A6924DC99541199F2880", + "95713AB9878E9CC9F459ED06E2E8AE08B3AA7059690AB7ABE9AF5E6B03F974C5", + "B9761DF3B6A17C9223F17619A31CC7E751CA43F79FF97517A0379F9A0970BDD5", + "D23B27C92B27A8349B9DC838C1060445D4A5F11036A1ECB731B4F1A8A2FEE68A", + "2A9230932745154A632BE6E8BC02BDA70ACC63BA35CD5D9CB7FE8037C5E17B5A", + "5EB24E22E303BAB86456FC00C414F274C138C4809B2C4BB1A60B824F22D13E49", + "D8212A0EDCACC94A5DD9BA8862DEA437206E1D6D98A080FA6FC81AE907FE5263", + "2DF6D2E2D633407B98809F32B1EB0A4145385F9F81CE5F7B0BC2E1E6B0F809C9", + "83C1EECD541CDED8F7DCA5118B402156AC50F5130CC55E8A34740EF3BCEF445D", + "E3DD4311EC37E750D62B5AA6BEBB6007D7FE652155B2B8CA52B59DC58BDF5E7F", + "1CFDBAB1465C114335BCE962621B3B9F3E464817F7403031FB5558A2B4A514E9", + "A35860460AD1096EBCBFF7E8C0A7086594C18BC0AA5F7CB23C1E40FF22FC133F", + "09FE0839286B47089EA34D465C2089EC205B40604EE51725E697035DE58134B6", + "A3CC048DB8E7F433B86C311D6226EA64BE2BA0137B818EF35BE98B3E900DA88A", + "DB764B3E83DEE68022BD886D5CC1276B31FBBBEDA3186E5E1BDE2EEC1B8E8C9E", + "83485A81ECBA2E8E2EF0B6A1DA4D730D863E32E2CA46FBB8415E9621799984F8", + "269DC791F56AACAEF6CEA04C6C99450345D3D692A2F74E38B9CC161C182BB1BC", + "23FBE8EE73A47376CD8799610ADA8509539B480BE3D9E280DB83CF2AF6DC9DB5", + "38E7AB66E8C93173DE7373DA4A616E458DF4196E140C8EFAABDF21B7D4BE9741", + "FB0EA9CF94A19B0980A109E07D60FC009042940D79EB7A6C611FEEBD4E59049A", + "AB0FA33B50D992508072E9AB22AA9267A52B220E9A1340A950DCA9884561A6D7" + ], + "LedgerEntryType": "LedgerHashes", + "Flags": 0, + "FirstLedgerSequence": 256, + "LastLedgerSequence": 37888 + } + }, + { + "binary": "110061220000000024000000012500000E9F2D00000000557B2A63FEEDB3A8ECCC0FF2A342C677804E75DCC525B1447AB36406AB0E0CFE48624000886C98B76000811475792AA81BDEBC1B7218F0F73E5BE962B9E374CE", + "json": { + "OwnerCount": 0, + "Account": "rB59DESmVnTwXd2SCy1G4ReVkP5UM7ZYcN", + "PreviousTxnLgrSeq": 3743, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "7B2A63FEEDB3A8ECCC0FF2A342C677804E75DCC525B1447AB36406AB0E0CFE48", + "Flags": 0, + "Sequence": 1, + "Balance": "150000000000000" + } + }, + { + "binary": "110072220003000025000045B1370000000000000000380000000000000000554E690ECBC944A7A3C57DFDC24D7CA1A0BDEFEB916901B7E35CDD4FE7E81848D66280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5038D7EA4C6800000000000000000000000000055534400000000002B6C42A95B3F7EE1971E4A10098E8F1B5F66AA0867D517A93C16344000000000000000000000000000555344000000000062FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 17841, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "100", + "issuer": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy" + }, + "PreviousTxnID": "4E690ECBC944A7A3C57DFDC24D7CA1A0BDEFEB916901B7E35CDD4FE7E81848D6", + "Flags": 196608, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "666", + "issuer": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j" + } + } + }, + { + "binary": "11007222000200002500003E9D37000000000000000038000000000000000055865A20F744FBB8673C684D6A310C1B2D59070FCB9979223A4E54018C22466946628000000000000000000000000000000000000000425443000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004254430000000000550FC62003E785DC231A1058A05E56E3F09CF4E667D4C38D7EA4C68000000000000000000000000000425443000000000080799D02FF2097DEB21F66472FCF477C36E7039F", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 16029, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0", + "issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV" + }, + "PreviousTxnID": "865A20F744FBB8673C684D6A310C1B2D59070FCB9979223A4E54018C22466946", + "Flags": 131072, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "10", + "issuer": "rU5KBPzSyPycRVW1HdgCKjYpU6W9PKQdE8" + } + } + }, + { + "binary": "1100612200000000240000000125000020DC2D0000000055217DF9EC25E736AF6D6A5F5DEECCD8F1E2B1CFDA65723AB6CC75D8C53D3CA98B624000B5E620F4800081145823936C23037AE05A77BEAF88D4684EA934C0E8", + "json": { + "OwnerCount": 0, + "Account": "r9ssnjg97d86PxMrjVsCAX1xE9qg8czZTu", + "PreviousTxnLgrSeq": 8412, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "217DF9EC25E736AF6D6A5F5DEECCD8F1E2B1CFDA65723AB6CC75D8C53D3CA98B", + "Flags": 0, + "Sequence": 1, + "Balance": "200000000000000" + } + }, + { + "binary": "1100612200000000240000000125000000362D000000005583F0BFD13373B83B76BDD3CB47F705791E57B43DB6D00675F9AB0C5B75698BAC6240000002540BE40081149CECBD6618C3CF4822DFE9C7B39217EF6E798B72", + "json": { + "OwnerCount": 0, + "Account": "rEJkrunCP8hpvk4ijxUgEWnxCE6iUiXxc2", + "PreviousTxnLgrSeq": 54, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "83F0BFD13373B83B76BDD3CB47F705791E57B43DB6D00675F9AB0C5B75698BAC", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11007222000100002500000B97557C63981F982844B6ABB31F0FE858CBE7528CA47BC76658855FE44A4ECEECEDD46280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D4C38D7EA4C68000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC6780000000000000000000000000000000000000004254430000000000A0B2F54B75C3D1EAC4FFC68EC03CCBB772CC1325", + "json": { + "PreviousTxnLgrSeq": 2967, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "10", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "7C63981F982844B6ABB31F0FE858CBE7528CA47BC76658855FE44A4ECEECEDD4", + "Flags": 65536, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rEe6VvCzzKU1ib9waLknXvEXywVjjUWFDN" + } + } + }, + { + "binary": "11006422000000005872D60CCD3905A3ABE19049B6EE76E8E0F3A2CBAC852625C757176F1B73EF617F8214FCD4E320A9A95903D3C787A6892B809C7F00BF02011320AB124EEAB087452070EC70D9DEA1A22C9766FFBBEE1025FD46495CC74148CCA8", + "json": { + "Owner": "rQsiKrEtzTFZkQjF9MrxzsXHCANZJSd1je", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "72D60CCD3905A3ABE19049B6EE76E8E0F3A2CBAC852625C757176F1B73EF617F", + "Indexes": ["AB124EEAB087452070EC70D9DEA1A22C9766FFBBEE1025FD46495CC74148CCA8"] + } + }, + { + "binary": "11007222000200002500006B1C55AAC46BD97B75B21F81B73BE6F81DF13AE4F9E83B6BD8C290894A095878158DEB6294838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001668000000000000000000000000000000000000000555344000000000058C742CF55C456DE367686CB9CED83750BD2497967D4C38D7EA4C6800000000000000000000000000055534400000000005EBBD3E507B0FB7C03D592FF4F27E08A28AA5C50", + "json": { + "PreviousTxnLgrSeq": 27420, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx" + }, + "PreviousTxnID": "AAC46BD97B75B21F81B73BE6F81DF13AE4F9E83B6BD8C290894A095878158DEB", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "-1", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "r9duXXmUuhSs6JxKpPCSh2tPUg9AGvE2cG" + } + } + }, + { + "binary": "1100612200000000240000000125000000382D00000000555D4529121A6F29A1390730EBF6C6DEF542A6B6B852786A4B75388DA0067EAEE46240000002540BE4008114D7C7722F339A77BB53038B1B916302DFFF1EA25C", + "json": { + "OwnerCount": 0, + "Account": "rLCAUzFMzKzcyRLa1B4LRqEMsUkYXX1LAs", + "PreviousTxnLgrSeq": 56, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "5D4529121A6F29A1390730EBF6C6DEF542A6B6B852786A4B75388DA0067EAEE4", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006422000000005877F65EFF930ED7E93C6CC839C421E394D6B1B6A47CEA8A140D63EC9C712F46F58214550FC62003E785DC231A1058A05E56E3F09CF4E60113804FFCC3F4D53FD3B5F488C8EB8E5D779F9028130F9160218020DA73CD5E6304546C4C3F1C6B9D76A6EF50F377E7C3991825694C604DBE0C1DD09362045EE4199726B894EE68470AD5AEEB55D5EBF936E6397CEE6957B93C56A2E7882CA9082873E87ABEF8B6CD737F3972FC7C0E633F85848A195E29401F50D9EF1087792EC610", + "json": { + "Owner": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "77F65EFF930ED7E93C6CC839C421E394D6B1B6A47CEA8A140D63EC9C712F46F5", + "Indexes": [ + "4FFCC3F4D53FD3B5F488C8EB8E5D779F9028130F9160218020DA73CD5E630454", + "6C4C3F1C6B9D76A6EF50F377E7C3991825694C604DBE0C1DD09362045EE41997", + "26B894EE68470AD5AEEB55D5EBF936E6397CEE6957B93C56A2E7882CA9082873", + "E87ABEF8B6CD737F3972FC7C0E633F85848A195E29401F50D9EF1087792EC610" + ] + } + }, + { + "binary": "1100642200000000588E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4821458C742CF55C456DE367686CB9CED83750BD2497901134044A3BC5DABBA84B9E1D64A61350F2FBB26EC70D1393B699CA2BB2CA1A0679A017D4325BE338A40BBCBCC1F351B3272EB3E76305A878E76603DE206A795871619", + "json": { + "Owner": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "8E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4", + "Indexes": [ + "44A3BC5DABBA84B9E1D64A61350F2FBB26EC70D1393B699CA2BB2CA1A0679A01", + "7D4325BE338A40BBCBCC1F351B3272EB3E76305A878E76603DE206A795871619" + ] + } + }, + { + "binary": "1100612200000000240000000125000000392D000000005566A8F5C59CC1C77A647CFFEE2B2CA3755E44EC02BA3BC9FDEE4A4403747B5B356240000002540BE4008114ED1E32FD10D9553CDD043AC0F4A62E04FBFC2E84", + "json": { + "OwnerCount": 0, + "Account": "r4cmKj1gK9EcNggeHMy1eqWakPBicwp69R", + "PreviousTxnLgrSeq": 57, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "66A8F5C59CC1C77A647CFFEE2B2CA3755E44EC02BA3BC9FDEE4A4403747B5B35", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100642200000000310000000000000002320000000000000001581F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C8214A82BB90BF7031413B42E2C890827EDC2399B7BFA011340E136A6E4D945A85C05C644B9420C2FB182ACD0E15086757A4EC609202ABDC46935FB1D334ECCD52B94253E7A33BA37C3D845E26F11FDEC08A56527C92907C3AC", + "json": { + "Owner": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa", + "IndexNext": "0000000000000002", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000001", + "Flags": 0, + "RootIndex": "1F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C", + "Indexes": [ + "E136A6E4D945A85C05C644B9420C2FB182ACD0E15086757A4EC609202ABDC469", + "35FB1D334ECCD52B94253E7A33BA37C3D845E26F11FDEC08A56527C92907C3AC" + ] + } + }, + { + "binary": "11007222000200002500007E6737000000000000000338000000000000000055A4152496C7C090B531A5DAD9F1FF8D6D842ECEDFC753D63B77434F35EA43797C6294838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001668000000000000000000000000000000000000000555344000000000058C742CF55C456DE367686CB9CED83750BD249796780000000000000000000000000000000000000005553440000000000A3E4374D5570FDC25AA9F856E2A6635C66E9CFA5", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000003", + "PreviousTxnLgrSeq": 32359, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx" + }, + "PreviousTxnID": "A4152496C7C090B531A5DAD9F1FF8D6D842ECEDFC753D63B77434F35EA43797C", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "-1", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rEA2XzkTXi6sWRzTVQVyUoSX4yJAzNxucd" + } + } + }, + { + "binary": "11006422000000005880AB25842B230D48027800213EB86023A3EAF4430E22C092D333795FFF1E52198214E4FE687C90257D3D2D694C8531CDEECBE84F336701132042E28285A82D01DCA856118A064C8AEEE1BF8167C08186DA5BFC678687E86F7C", + "json": { + "Owner": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "80AB25842B230D48027800213EB86023A3EAF4430E22C092D333795FFF1E5219", + "Indexes": ["42E28285A82D01DCA856118A064C8AEEE1BF8167C08186DA5BFC678687E86F7C"] + } + }, + { + "binary": "110061220000000024000000012500001F352D0000000055FE8A433C90ED67E78FB7F8B8DED39E1ECD8DEC17DC748DB3E2671695E141D38962400000049B936F808114B45254658C3762C24897587A13724DABF7F18415", + "json": { + "OwnerCount": 0, + "Account": "rHSTEtAcRZBg1SjcR4KKNQzJKF3y86MNxT", + "PreviousTxnLgrSeq": 7989, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "FE8A433C90ED67E78FB7F8B8DED39E1ECD8DEC17DC748DB3E2671695E141D389", + "Flags": 0, + "Sequence": 1, + "Balance": "19790000000" + } + }, + { + "binary": "11006122000000002400000001250000003C2D0000000055F26CE2140F7B9F9A0D9E17919F7E0DA9040FB5312D155E6CDA1FEDDD3FFB6F4D6240000002540BE40081142E68D54B3026C1EF1A70726EA031C9F0926E9CED", + "json": { + "OwnerCount": 0, + "Account": "rnNPCm97TBMPprUGbfwqp1VpkfHUqMeUm7", + "PreviousTxnLgrSeq": 60, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "F26CE2140F7B9F9A0D9E17919F7E0DA9040FB5312D155E6CDA1FEDDD3FFB6F4D", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006122000000002400000001250000003D2D00000000559D02BD8906820772FCF8A413A6BF603603A5489DE1CFE7775FDCF3691A473B646240000002540BE400811468C7AE9575D9931E496CC23F6DDE4CB570CABDD5", + "json": { + "OwnerCount": 0, + "Account": "rwZpVacRQHYArgN3NzUfuKEcRDfbdvqGMi", + "PreviousTxnLgrSeq": 61, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "9D02BD8906820772FCF8A413A6BF603603A5489DE1CFE7775FDCF3691A473B64", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110072220002000025000000F655E816716B912B1476D4DE80C872A52D148F1B0791EA7A2CEF1AF5632451FECCAD62800000000000000000000000000000000000000043414400000000000000000000000000000000000000000000000001668000000000000000000000000000000000000000434144000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC67D4C38D7EA4C680000000000000000000000000004341440000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA", + "json": { + "PreviousTxnLgrSeq": 246, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "CAD", + "value": "0", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "E816716B912B1476D4DE80C872A52D148F1B0791EA7A2CEF1AF5632451FECCAD", + "Flags": 131072, + "Balance": { + "currency": "CAD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "CAD", + "value": "10", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + } + } + }, + { + "binary": "110061220000000024000000012500000EAF2D0000000055E6FB5CDEC11A45AF7CD9B81E8B7A5D1E85A42B8DCD9D741786588214D82E0A8A62400009184E72A0008114AF9743438C773D077D6F483C3FE68896C289CD69", + "json": { + "OwnerCount": 0, + "Account": "rHrSTVSjMsZKeZMenkpeLgHGvY5svPkRvR", + "PreviousTxnLgrSeq": 3759, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "E6FB5CDEC11A45AF7CD9B81E8B7A5D1E85A42B8DCD9D741786588214D82E0A8A", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "1100722200010000250000316737000000000000000038000000000000000055B9EB652FCC0BEA72F8FDDDC5A8355938084E48E6CAA4744E09E5023D64D0199E6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4C38D7EA4C6800000000000000000000000000055534400000000008D3A0AEF277858BD4D9751ECECD16779C0CC86D06780000000000000000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 12647, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "10", + "issuer": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x" + }, + "PreviousTxnID": "B9EB652FCC0BEA72F8FDDDC5A8355938084E48E6CAA4744E09E5023D64D0199E", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" + } + } + }, + { + "binary": "110061220000000024000000012500007C362D000000005510C5A4DCEF6078D1DCD654DAB48F77A7073D32981CD514669CCEFA5F409852CA6240000002540BE4008114491A65349BED002808866FD1CC72F3B912BF27EE", + "json": { + "OwnerCount": 0, + "Account": "rfCXAzsmsnqDvyQj2TxDszTsbVj5cRTXGM", + "PreviousTxnLgrSeq": 31798, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "10C5A4DCEF6078D1DCD654DAB48F77A7073D32981CD514669CCEFA5F409852CA", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100642200000000588ADF3C5527CCF6D0B5863365EF40254171536C3901F1CBD9E2BC5F918A7D492A82141A5CD521A26A45FF77EA1343E25E71740344BB66011320BC10E40AFB79298004CDE51CB065DBDCABA86EC406E3A1CF02CE5F8A9628A2BD", + "json": { + "Owner": "rsQP8f9fLtd58hwjEArJz2evtrKULnCNif", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "8ADF3C5527CCF6D0B5863365EF40254171536C3901F1CBD9E2BC5F918A7D492A", + "Indexes": ["BC10E40AFB79298004CDE51CB065DBDCABA86EC406E3A1CF02CE5F8A9628A2BD"] + } + }, + { + "binary": "1100642200000000588BAA6F346307122FC5527291B4B2E88050CB66E4556360786B535C14BB0A450982147588B8DBDC8932DC410E8571045466C03F5A6B690113C0CAD951AB279A749AE648FD1DFF56C021BD66E36187022E772C31FE52106CB13BC683B5BB928F025F1E860D9D69D6C554C2202DE0D45877ADB3077DA4CB9E125C25DCAC87FBE4C3B66A1AFDE3C3F98E5A16333975C4FD46682F7497F27DFB97669A551971E78FE2FB80D930A77EA0BAC2139A49D6BEB98406427C79F52A347A09E87ABEF8B6CD737F3972FC7C0E633F85848A195E29401F50D9EF1087792EC61065492B9F30F1CBEA168509128EB8619BAE02A7A7A4725FF3F8DAA70FA707A26E", + "json": { + "Owner": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "8BAA6F346307122FC5527291B4B2E88050CB66E4556360786B535C14BB0A4509", + "Indexes": [ + "CAD951AB279A749AE648FD1DFF56C021BD66E36187022E772C31FE52106CB13B", + "C683B5BB928F025F1E860D9D69D6C554C2202DE0D45877ADB3077DA4CB9E125C", + "25DCAC87FBE4C3B66A1AFDE3C3F98E5A16333975C4FD46682F7497F27DFB9766", + "9A551971E78FE2FB80D930A77EA0BAC2139A49D6BEB98406427C79F52A347A09", + "E87ABEF8B6CD737F3972FC7C0E633F85848A195E29401F50D9EF1087792EC610", + "65492B9F30F1CBEA168509128EB8619BAE02A7A7A4725FF3F8DAA70FA707A26E" + ] + } + }, + { + "binary": "11006122000000002400000001250000003E2D00000000550C5C64EDBB27641A83F5DD135CD3ADFE86D311D3F466BACFEBB69EB8E8D8E60F6240000002540BE400811442151E1DA6C825C4D1078D44B9E63B6363371E49", + "json": { + "OwnerCount": 0, + "Account": "rfpQtAXgPpHNzfnAYykgT6aWa94xvTEYce", + "PreviousTxnLgrSeq": 62, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "0C5C64EDBB27641A83F5DD135CD3ADFE86D311D3F466BACFEBB69EB8E8D8E60F", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110064220000000031000000000000000532000000000000000458D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C38214E14829DB4C6419A8EFCAC1EC21D891A1A4339871011340CF1F8DF231AE06AE9D55C3B3367A9ED1E430FC0A6CA193EEA559C3ADF0A634FBCEA57059DECE8D5C6FC9FDB9ACE44278EC74A075CE8A5A7522B2F85F669245FF", + "json": { + "Owner": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui", + "IndexNext": "0000000000000005", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000004", + "Flags": 0, + "RootIndex": "D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C3", + "Indexes": [ + "CF1F8DF231AE06AE9D55C3B3367A9ED1E430FC0A6CA193EEA559C3ADF0A634FB", + "CEA57059DECE8D5C6FC9FDB9ACE44278EC74A075CE8A5A7522B2F85F669245FF" + ] + } + }, + { + "binary": "1100642200000000310000000000000001320000000000000003588E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4821458C742CF55C456DE367686CB9CED83750BD2497901134017B72685E9FBEFE18E0C1E8F07000E1B345A18ECD2D2BE9B27E69045248EF036F9830A2F94E5B611F6364893235E6D7F3521A8DE8AF936687B40C555E1282836", + "json": { + "Owner": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx", + "IndexNext": "0000000000000001", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000003", + "Flags": 0, + "RootIndex": "8E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4", + "Indexes": [ + "17B72685E9FBEFE18E0C1E8F07000E1B345A18ECD2D2BE9B27E69045248EF036", + "F9830A2F94E5B611F6364893235E6D7F3521A8DE8AF936687B40C555E1282836" + ] + } + }, + { + "binary": "1100612200000000240000000125000000552D0000000055EE8B75C4A4C54F61F1A3EE0D0BB9A712FCE18D5DFB0B8973F232EEED301ACD846240038D7EA4C680008114A920D83B36B75B2783EABED574D4D96AD1A8C03C", + "json": { + "OwnerCount": 0, + "Account": "rGRGYWLmSvPuhKm4rQV287PpJUgTB1VeD7", + "PreviousTxnLgrSeq": 85, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "EE8B75C4A4C54F61F1A3EE0D0BB9A712FCE18D5DFB0B8973F232EEED301ACD84", + "Flags": 0, + "Sequence": 1, + "Balance": "1000000000000000" + } + }, + { + "binary": "1100722200020000250000275255C7AECAF0E7ABC3868C37343B7F63BAEC317A53867ABD2CA6BAD1F335C1CA4D6F6294838D7EA4C680000000000000000000000000004D4541000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004D45410000000000D9CEEA2E2AD331A8D087C216284D58EBBC6780E867D4838D7EA4C680000000000000000000000000004D45410000000000F7FF2D5EA6BB5C26D85343656BEEE94D74B509E0", + "json": { + "PreviousTxnLgrSeq": 10066, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "MEA", + "value": "0", + "issuer": "rLiCWKQNUs8CQ81m2rBoFjshuVJviSRoaJ" + }, + "PreviousTxnID": "C7AECAF0E7ABC3868C37343B7F63BAEC317A53867ABD2CA6BAD1F335C1CA4D6F", + "Flags": 131072, + "Balance": { + "currency": "MEA", + "value": "-1", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "MEA", + "value": "1", + "issuer": "rPcHbQ26o4Xrwb2bu5gLc3gWUsS52yx1pG" + } + } + }, + { + "binary": "11006422000000005890F51238E58E7CA88F9754984B8B2328DCD4A7D699C04092BF58DD40D5660EDD821462FE474693228F7F9ED1C5EFADB3B6555FBEAFBE0113C06BC1677EB8218F6ECB37FB83723ED4FA4C3089D718A45D5F0BB4F4EC553CDF28A5C489C3780C320EC1C2CF5A2E22C2F393F91884DC14D18F5F5BED4EE3AFFE00263F16D626C701250AD1E9FF56C763132DF4E09B1EF0B2D0A838D265123FBBA85F22826818CC83448C9DF34939AB4019D3F80C70DEB8BDBDCF0496A36DC687195B7F148A8DDB4EB7386C9E75C4C1ED918DEDE5C52D5BA51B694D7271EF8BDB46600A398F57CAE44461B4C8C25DE12AC289F87ED125438440B33B97417FE3D82C", + "json": { + "Owner": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "90F51238E58E7CA88F9754984B8B2328DCD4A7D699C04092BF58DD40D5660EDD", + "Indexes": [ + "6BC1677EB8218F6ECB37FB83723ED4FA4C3089D718A45D5F0BB4F4EC553CDF28", + "A5C489C3780C320EC1C2CF5A2E22C2F393F91884DC14D18F5F5BED4EE3AFFE00", + "263F16D626C701250AD1E9FF56C763132DF4E09B1EF0B2D0A838D265123FBBA8", + "5F22826818CC83448C9DF34939AB4019D3F80C70DEB8BDBDCF0496A36DC68719", + "5B7F148A8DDB4EB7386C9E75C4C1ED918DEDE5C52D5BA51B694D7271EF8BDB46", + "600A398F57CAE44461B4C8C25DE12AC289F87ED125438440B33B97417FE3D82C" + ] + } + }, + { + "binary": "1100612200000000240000000525000023192D000000025521278AF0CC3A3E968367D064C61280B9723E85F8170F67D4FED0D255E92381D562400000025FF7A5D88114B544029B077F39117BD0C9FB2913FCE08F9345CF", + "json": { + "OwnerCount": 2, + "Account": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr", + "PreviousTxnLgrSeq": 8985, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "21278AF0CC3A3E968367D064C61280B9723E85F8170F67D4FED0D255E92381D5", + "Flags": 0, + "Sequence": 5, + "Balance": "10199999960" + } + }, + { + "binary": "1100612200000000240000000125000000412D0000000055024FF4B5506ABC1359DBFF40F783911D0E5547D29BDD857B9D8D78861CFC6BE76240000002540BE4008114E8923A64A22E8707AB3BC8A766337454B3094172", + "json": { + "OwnerCount": 0, + "Account": "r4U5AcSVABL6Ym85jB94KYnURnzkRDqh1Y", + "PreviousTxnLgrSeq": 65, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "024FF4B5506ABC1359DBFF40F783911D0E5547D29BDD857B9D8D78861CFC6BE7", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006122000000002400000001250000376A2D0000000055CBF0F1AC96D1E0AA8EBA6685E085CD389D83E60BA19F141618BFFA022165EDA26240000002540BE4008114BA62AC025B12D793C3E2FD57E820E8318EB9B1C9", + "json": { + "OwnerCount": 0, + "Account": "rHzWtXTBrArrGoLDixQAgcSD2dBisM19fF", + "PreviousTxnLgrSeq": 14186, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "CBF0F1AC96D1E0AA8EBA6685E085CD389D83E60BA19F141618BFFA022165EDA2", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000525000027532D000000025547F959E260E8EEBA242DF638229E3298C8988FEAC77041D7D419C6ED835EF4A86240000002540BE3D88114E8ACFC6B5EF4EA0601241525375162F43C2FF285", + "json": { + "OwnerCount": 2, + "Account": "r4DGz8SxHXLaqsA9M2oocXsrty6BMSQvw3", + "PreviousTxnLgrSeq": 10067, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "47F959E260E8EEBA242DF638229E3298C8988FEAC77041D7D419C6ED835EF4A8", + "Flags": 0, + "Sequence": 5, + "Balance": "9999999960" + } + }, + { + "binary": "110061220000000024000000012500000BEF2D0000000055AE5929DE1626787EF84680B4B9741D793E3294CC8DFE5C03B5C911AF7C39AD8C6240000002540BE4008114585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C1", + "json": { + "OwnerCount": 0, + "Account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "PreviousTxnLgrSeq": 3055, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "AE5929DE1626787EF84680B4B9741D793E3294CC8DFE5C03B5C911AF7C39AD8C", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006422000000003100000000000000013200000000000000025898082E695CAB618590BEEA0647A5F24D2B610A686ECD49310604FC7431FAAB0D8214B544029B077F39117BD0C9FB2913FCE08F9345CF011340A95EB2892EA15C8B7BCDAF6D1A8F1F21791192586EBD66B7DCBEC582BFAAA19852733E959FD0D25A72E188A26BC406768D91285883108AED061121408DAD4AF0", + "json": { + "Owner": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr", + "IndexNext": "0000000000000001", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000002", + "Flags": 0, + "RootIndex": "98082E695CAB618590BEEA0647A5F24D2B610A686ECD49310604FC7431FAAB0D", + "Indexes": [ + "A95EB2892EA15C8B7BCDAF6D1A8F1F21791192586EBD66B7DCBEC582BFAAA198", + "52733E959FD0D25A72E188A26BC406768D91285883108AED061121408DAD4AF0" + ] + } + }, + { + "binary": "110061220000000024000000032500007E672D0000000155A4152496C7C090B531A5DAD9F1FF8D6D842ECEDFC753D63B77434F35EA43797C62400000001DCD64EC8114A3E4374D5570FDC25AA9F856E2A6635C66E9CFA5", + "json": { + "OwnerCount": 1, + "Account": "rEA2XzkTXi6sWRzTVQVyUoSX4yJAzNxucd", + "PreviousTxnLgrSeq": 32359, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "A4152496C7C090B531A5DAD9F1FF8D6D842ECEDFC753D63B77434F35EA43797C", + "Flags": 0, + "Sequence": 3, + "Balance": "499999980" + } + }, + { + "binary": "11006122000000002400000001250000273B2D00000000554C6DC2D608C3B0781856F42042CD33B6053FB46C673A057FB192AB4319967A0C6240000002540BE400811406644B90F5D2A144A0758A6BE1F7F61700CC6B8A", + "json": { + "OwnerCount": 0, + "Account": "r2oU84CFuT4MgmrDejBaoyHNvovpMSPiA", + "PreviousTxnLgrSeq": 10043, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4C6DC2D608C3B0781856F42042CD33B6053FB46C673A057FB192AB4319967A0C", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006122000000002400000001250000685E2D00000000558439B6029A9E670D0980088928C3EE35A6C7B1D851F73E68FA7607E9C173AFA862400009184E72A0008114FA48C3F6D1B57BCFA72BAE7EA498673590E23693", + "json": { + "OwnerCount": 0, + "Account": "rPFPa8AjKofbPiYNtYqSWxYA4A9Eqrf9jG", + "PreviousTxnLgrSeq": 26718, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "8439B6029A9E670D0980088928C3EE35A6C7B1D851F73E68FA7607E9C173AFA8", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "110064220000000031000000000000000232000000000000000136531AA535D3D0C000588E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4011100000000000000000000000042544300000000000211E8ACFC6B5EF4EA0601241525375162F43C2FF28503110000000000000000000000005553440000000000041158C742CF55C456DE367686CB9CED83750BD24979011340D1CB738BD08AC36DCB77191DB87C6E40FA478B86503371ED497F30931D7F4F525CCE7ABDC737694A71B9B1BBD15D9408E8DC4439C9510D2BC2538D59F99B7515", + "json": { + "TakerPaysCurrency": "0000000000000000000000004254430000000000", + "ExchangeRate": "531AA535D3D0C000", + "TakerGetsCurrency": "0000000000000000000000005553440000000000", + "IndexNext": "0000000000000002", + "TakerGetsIssuer": "58C742CF55C456DE367686CB9CED83750BD24979", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000001", + "Flags": 0, + "RootIndex": "8E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4", + "Indexes": [ + "D1CB738BD08AC36DCB77191DB87C6E40FA478B86503371ED497F30931D7F4F52", + "5CCE7ABDC737694A71B9B1BBD15D9408E8DC4439C9510D2BC2538D59F99B7515" + ], + "TakerPaysIssuer": "E8ACFC6B5EF4EA0601241525375162F43C2FF285" + } + }, + { + "binary": "1100612200000000240000000125000000422D0000000055059D2DCA15ACF2DC3873C2A1ACF9E9309C4574FFC8DA0CEEAB6DCC746A02715762400000003B9ACA008114149B1B2B6B991AFC19AD3415BB6165438E3471EB", + "json": { + "OwnerCount": 0, + "Account": "rp1xKo4CWEzTuT2CmfHnYntKeZSf21KqKq", + "PreviousTxnLgrSeq": 66, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "059D2DCA15ACF2DC3873C2A1ACF9E9309C4574FFC8DA0CEEAB6DCC746A027157", + "Flags": 0, + "Sequence": 1, + "Balance": "1000000000" + } + }, + { + "binary": "11006122000000002400000001250000685A2D00000000551AC83C3910B20C2A8D7D9A14772F78A8F8CA87632F3EEA8EE2C9731937CF72926240002D79883D20008114AD44A3BD8E19E4DA9A92FEB5FF4688C84C52597B", + "json": { + "OwnerCount": 0, + "Account": "rGow3MKvbQJvuzPPP4vEoohGmLLZ5jXtcC", + "PreviousTxnLgrSeq": 26714, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "1AC83C3910B20C2A8D7D9A14772F78A8F8CA87632F3EEA8EE2C9731937CF7292", + "Flags": 0, + "Sequence": 1, + "Balance": "50000000000000" + } + }, + { + "binary": "110061220000000024000000012500000B842D000000005538C911C5DAF1615BAA58B7D2265590DE1DAD40C79B3F7597C47ECE8047E1E4F46240000000135F1B408114C2659C14642A6604CE305966307E5F21817A092D", + "json": { + "OwnerCount": 0, + "Account": "rJ51FBSh6hXSUkFdMxwmtcorjx9izrC1yj", + "PreviousTxnLgrSeq": 2948, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "38C911C5DAF1615BAA58B7D2265590DE1DAD40C79B3F7597C47ECE8047E1E4F4", + "Flags": 0, + "Sequence": 1, + "Balance": "325000000" + } + }, + { + "binary": "11007222000100002500004ED3370000000000000000380000000000000000553662FED78877C7E424BEF91C02B9ECA5E02AD3A8638F0A3B89C1EAC6C9CC92536280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5438D7EA4C6800000000000000000000000000055534400000000007588B8DBDC8932DC410E8571045466C03F5A6B696780000000000000000000000000000000000000005553440000000000D99223BCD7B2E92968DC60BC9C63D1D808191FB3", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 20179, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "1000", + "issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY" + }, + "PreviousTxnID": "3662FED78877C7E424BEF91C02B9ECA5E02AD3A8638F0A3B89C1EAC6C9CC9253", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rLqQ62u51KR3TFcewbEbJTQbCuTqsg82EY" + } + } + }, + { + "binary": "1100612200000000240000003C25000079CB2D0000000655D612015F70931DC1CE2D65713408F0C4EE6230911F52E08678898D24C888A43A62401D17DA2410CDB281147588B8DBDC8932DC410E8571045466C03F5A6B69", + "json": { + "OwnerCount": 6, + "Account": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY", + "PreviousTxnLgrSeq": 31179, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D612015F70931DC1CE2D65713408F0C4EE6230911F52E08678898D24C888A43A", + "Flags": 0, + "Sequence": 60, + "Balance": "8188999999999410" + } + }, + { + "binary": "1100722200030000250000231255F7A5BF798499FF7862D2E5F65798673AADB6E4248D057FE100DF9DFC98A7DED66280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D4838D7EA4C680000000000000000000000000004254430000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA67D4871AFD498D00000000000000000000000000004254430000000000B544029B077F39117BD0C9FB2913FCE08F9345CF", + "json": { + "PreviousTxnLgrSeq": 8978, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "1", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + }, + "PreviousTxnID": "F7A5BF798499FF7862D2E5F65798673AADB6E4248D057FE100DF9DFC98A7DED6", + "Flags": 196608, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "2", + "issuer": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr" + } + } + }, + { + "binary": "110072220002000025000000E855189DB8A6DB5160CFBD62AB7A21AFC5F4246E704A1A1B4B1DB5E23F7F4600D37E628000000000000000000000000000000000000000555344000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000005553440000000000C2659C14642A6604CE305966307E5F21817A092D67D4C38D7EA4C680000000000000000000000000005553440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 232, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rJ51FBSh6hXSUkFdMxwmtcorjx9izrC1yj" + }, + "PreviousTxnID": "189DB8A6DB5160CFBD62AB7A21AFC5F4246E704A1A1B4B1DB5E23F7F4600D37E", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "1100612200000000240000000125000000FA2D0000000055DA52CF235F41B229158DB77302966E7AB04B1DFE05E3B5F4E381BC9A19FE2A306240002D79883D2000811482BB55879A01141BA8021E710BD3925192A43206", + "json": { + "OwnerCount": 0, + "Account": "rUvEG9ahtFRcdZHi3nnJeFcJWhwXQoEkbi", + "PreviousTxnLgrSeq": 250, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "DA52CF235F41B229158DB77302966E7AB04B1DFE05E3B5F4E381BC9A19FE2A30", + "Flags": 0, + "Sequence": 1, + "Balance": "50000000000000" + } + }, + { + "binary": "110064220000000058A00CD19C13A5CFA3FECB409D42B38017C07A4AEAE05A7A00347DDA17199BA683821470EFFAAE000322A78E0D9DC9081564888C256C37011320E49318D6DF22411C3F35581B1D28297A36E47F68B45F36A587C156E6E43CE0A6", + "json": { + "Owner": "rBJwwXADHqbwsp6yhrqoyt2nmFx9FB83Th", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "A00CD19C13A5CFA3FECB409D42B38017C07A4AEAE05A7A00347DDA17199BA683", + "Indexes": ["E49318D6DF22411C3F35581B1D28297A36E47F68B45F36A587C156E6E43CE0A6"] + } + }, + { + "binary": "1100612200000000240000000125000068542D00000000558E78494EFF840F90FEAD186E3A4374D8A82F48B512EA105B415ED0705E59B11262400009184E72A00081145268777807E2032EB76D11C0D97151CE2E8CA5E7", + "json": { + "OwnerCount": 0, + "Account": "r3WjZU5LKLmjh8ff1q2RiaPLcUJeSU414x", + "PreviousTxnLgrSeq": 26708, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "8E78494EFF840F90FEAD186E3A4374D8A82F48B512EA105B415ED0705E59B112", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "110072220003000025000023195521278AF0CC3A3E968367D064C61280B9723E85F8170F67D4FED0D255E92381D56280000000000000000000000000000000000000004341440000000000000000000000000000000000000000000000000166D4C71AFD498D00000000000000000000000000004341440000000000B544029B077F39117BD0C9FB2913FCE08F9345CF67D4C38D7EA4C680000000000000000000000000004341440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 8985, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "CAD", + "value": "20", + "issuer": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr" + }, + "PreviousTxnID": "21278AF0CC3A3E968367D064C61280B9723E85F8170F67D4FED0D255E92381D5", + "Flags": 196608, + "Balance": { + "currency": "CAD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "CAD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110064220000000058A39F044D860C5B5846AA7E0FAAD44DC8897F0A62B2F628AA073B21B3EC146010821450FCCA71E98DFA43305149F9F0C7897DE5A9D18C011320CD34D8FF7C656B66E2298DB420C918FE27DFFF2186AC8D1785D8CBF2C6BC3488", + "json": { + "Owner": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "A39F044D860C5B5846AA7E0FAAD44DC8897F0A62B2F628AA073B21B3EC146010", + "Indexes": ["CD34D8FF7C656B66E2298DB420C918FE27DFFF2186AC8D1785D8CBF2C6BC3488"] + } + }, + { + "binary": "1100722200020000250000456937000000000000000038000000000000000055D0D1DC6636198949642D9125B5EEC51FF6AC02A47D32387CBB6AAB346FB3AFE36280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000042544300000000002B6C42A95B3F7EE1971E4A10098E8F1B5F66AA0867D4CBB9551FC24000000000000000000000000000425443000000000062FE474693228F7F9ED1C5EFADB3B6555FBEAFBE", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 17769, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy" + }, + "PreviousTxnID": "D0D1DC6636198949642D9125B5EEC51FF6AC02A47D32387CBB6AAB346FB3AFE3", + "Flags": 131072, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "33", + "issuer": "rwpRq4gQrb58N7PRJwYEQaoSui6Xd3FC7j" + } + } + }, + { + "binary": "1100612200000000240000000C2500005ABE2D0000000055070E052D8D38BCE690A69A25D164569315A20E29F07C9279E2F1231F4B97171162400000000BEBC19281149F17DCA26FE8C8A1B258898D533305B92DB75127", + "json": { + "OwnerCount": 0, + "Account": "rEWDpTUVU9fZZtzrywAUE6D6UcFzu6hFdE", + "PreviousTxnLgrSeq": 23230, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "070E052D8D38BCE690A69A25D164569315A20E29F07C9279E2F1231F4B971711", + "Flags": 0, + "Sequence": 12, + "Balance": "199999890" + } + }, + { + "binary": "110064220000000058A7E461C6DC98F472991FDE51FADDC0082D755F553F5849875D554B52624EF1C382149F17DCA26FE8C8A1B258898D533305B92DB75127011320116C6D5E5C6C59C9C5362B84CB9DD30BD3D4B7CB98CE993D49C068323BF19747", + "json": { + "Owner": "rEWDpTUVU9fZZtzrywAUE6D6UcFzu6hFdE", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "A7E461C6DC98F472991FDE51FADDC0082D755F553F5849875D554B52624EF1C3", + "Indexes": ["116C6D5E5C6C59C9C5362B84CB9DD30BD3D4B7CB98CE993D49C068323BF19747"] + } + }, + { + "binary": "110072220002000025000000E055058EC111AAF1F21207A3D87FC2AB7F841B02D95F73A37423F3119FDA65C031F7628000000000000000000000000000000000000000425443000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004254430000000000B544029B077F39117BD0C9FB2913FCE08F9345CF67D491C37937E080000000000000000000000000004254430000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 224, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr" + }, + "PreviousTxnID": "058EC111AAF1F21207A3D87FC2AB7F841B02D95F73A37423F3119FDA65C031F7", + "Flags": 131072, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "5", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110064220000000058AA539C8EECE0A0CFF0DBF3BFACD6B42CD4421715428AD90B034091BD3C7210388214A0B2F54B75C3D1EAC4FFC68EC03CCBB772CC132501132072307CB57E53604A0C50E653AB10E386F3835460B5585B70CB7F668C1E04AC8B", + "json": { + "Owner": "rEe6VvCzzKU1ib9waLknXvEXywVjjUWFDN", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "AA539C8EECE0A0CFF0DBF3BFACD6B42CD4421715428AD90B034091BD3C721038", + "Indexes": ["72307CB57E53604A0C50E653AB10E386F3835460B5585B70CB7F668C1E04AC8B"] + } + }, + { + "binary": "1100722200010000250000103C5599711CE5DC63B01502BB642B58450B8F60EA544DEE30B2FE4F87282E13DD13606280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D451C37937E08000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC6780000000000000000000000000000000000000004254430000000000FCD4E320A9A95903D3C787A6892B809C7F00BF02", + "json": { + "PreviousTxnLgrSeq": 4156, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0.5", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "99711CE5DC63B01502BB642B58450B8F60EA544DEE30B2FE4F87282E13DD1360", + "Flags": 65536, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rQsiKrEtzTFZkQjF9MrxzsXHCANZJSd1je" + } + } + }, + { + "binary": "1100612200000000240000000725000069252D0000000155D1DDAEDC74BC308B26BF3112D42F12E0D125F826506E0DB13654AD22115D3C316240000002541B2604811427E48F6D22BCB31D5F3D315CC512E60EFF80673D", + "json": { + "OwnerCount": 1, + "Account": "rhdAw3LiEfWWmSrbnZG3udsN7PoWKT56Qo", + "PreviousTxnLgrSeq": 26917, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D1DDAEDC74BC308B26BF3112D42F12E0D125F826506E0DB13654AD22115D3C31", + "Flags": 0, + "Sequence": 7, + "Balance": "10000999940" + } + }, + { + "binary": "110072220002000025000000DB559A9C9267E11734F53C6B25308F03B4F99ECD3CA4CCF330C94BD94F01EFC0E0C5628000000000000000000000000000000000000000555344000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000005553440000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA67D4C38D7EA4C680000000000000000000000000005553440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 219, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + }, + "PreviousTxnID": "9A9C9267E11734F53C6B25308F03B4F99ECD3CA4CCF330C94BD94F01EFC0E0C5", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110064220000000031000000000000000332000000000000000258D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C38214E14829DB4C6419A8EFCAC1EC21D891A1A4339871011340A2EFB4B11D6FDF01643DEE32792BA65BCCC5A98189A4955EB3C73911DDB648DBE136A6E4D945A85C05C644B9420C2FB182ACD0E15086757A4EC609202ABDC469", + "json": { + "Owner": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui", + "IndexNext": "0000000000000003", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000002", + "Flags": 0, + "RootIndex": "D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C3", + "Indexes": [ + "A2EFB4B11D6FDF01643DEE32792BA65BCCC5A98189A4955EB3C73911DDB648DB", + "E136A6E4D945A85C05C644B9420C2FB182ACD0E15086757A4EC609202ABDC469" + ] + } + }, + { + "binary": "1100612200000000240000000E2500007C3A2D0000000055FBF647E057F5C15EC277246AB843A5EB063646BEF2E3D3914D29456B32903262624000000BA43B737E81144A77EF82417FCC4B8D801A9D64C9070C0824E5E7", + "json": { + "OwnerCount": 0, + "Account": "rf8kg7r5Fc8cCszGdD2jeUZt2FrgQd76BS", + "PreviousTxnLgrSeq": 31802, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "FBF647E057F5C15EC277246AB843A5EB063646BEF2E3D3914D29456B32903262", + "Flags": 0, + "Sequence": 14, + "Balance": "49999999870" + } + }, + { + "binary": "1100612200000000240000000125000068592D00000000557957DD6BE9323DED70BAC7C43761E0EAAC4C6F5BA7DFC59CFE95100CA9B0B0D462400009184E72A000811473B3ED1EBFF63BECF6E84D03B6A0CAF1BE8A6050", + "json": { + "OwnerCount": 0, + "Account": "rBY8EZDiCNMjjhrC7SCfaGr2PzGWtSntNy", + "PreviousTxnLgrSeq": 26713, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "7957DD6BE9323DED70BAC7C43761E0EAAC4C6F5BA7DFC59CFE95100CA9B0B0D4", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "110064220000000058D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C38214E14829DB4C6419A8EFCAC1EC21D891A1A433987101134010BB331A6A794396B33DF7B975A57A3842AB68F3BC6C3B02928BA5399AAC9C8F6231CFA6BE243E92EC33050DC23C6E8EC972F22A111D96328873207A7CCCC7C7", + "json": { + "Owner": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C3", + "Indexes": [ + "10BB331A6A794396B33DF7B975A57A3842AB68F3BC6C3B02928BA5399AAC9C8F", + "6231CFA6BE243E92EC33050DC23C6E8EC972F22A111D96328873207A7CCCC7C7" + ] + } + }, + { + "binary": "1100612200000000240000000125000000462D000000005517C00ADB90EE2F481FEE456FF0AFD5A991D1C13D364ED48549680646F4BBE3FF6240000002540BE4008114056A94238EA9CFE9962E8FB5D5D3D14D50196BAF", + "json": { + "OwnerCount": 0, + "Account": "rVehB9r1dWghqrzJxY2y8qTiKxMgHFtQh", + "PreviousTxnLgrSeq": 70, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "17C00ADB90EE2F481FEE456FF0AFD5A991D1C13D364ED48549680646F4BBE3FF", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110064220000000058AF2CDC95233533BAB37A73BED86E950F8A9337F88A972F652762E6CD8E37CE148214169F404D62A8D2C8EE3935F230AA60BC07919E96011340F721E924498EE68BFF906CD856E8332073DD350BAC9E8977AC3F31860BA1E33A116C6D5E5C6C59C9C5362B84CB9DD30BD3D4B7CB98CE993D49C068323BF19747", + "json": { + "Owner": "rshceBo6ftSVYo8h5uNPzRWbdqk4W6g9va", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "AF2CDC95233533BAB37A73BED86E950F8A9337F88A972F652762E6CD8E37CE14", + "Indexes": [ + "F721E924498EE68BFF906CD856E8332073DD350BAC9E8977AC3F31860BA1E33A", + "116C6D5E5C6C59C9C5362B84CB9DD30BD3D4B7CB98CE993D49C068323BF19747" + ] + } + }, + { + "binary": "1100642200000000581F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C8214A82BB90BF7031413B42E2C890827EDC2399B7BFA011340D24FA4A3422BA1E91109B83D2A7545FC6369EAC13E7F4673F464BBBBC77AB2BE9BF3216E42575CA5A3CB4D0F2021EE81D0F7835BA2EDD78E05CAB44B655962BB", + "json": { + "Owner": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "1F71219BA652037B7064FC6E81EABD8F0B54F6AFE703F172E3999F48D0642F1C", + "Indexes": [ + "D24FA4A3422BA1E91109B83D2A7545FC6369EAC13E7F4673F464BBBBC77AB2BE", + "9BF3216E42575CA5A3CB4D0F2021EE81D0F7835BA2EDD78E05CAB44B655962BB" + ] + } + }, + { + "binary": "110072220002000025000022C0551DA779A65D0FAA515C2B463E353DC249EBF9A9258AF0ED668F478CF8185FDFD66280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166800000000000000000000000000000000000000055534400000000002ECC94F5447A3CC1348E94C84D4677442C9E422767D4C38D7EA4C680000000000000000000000000005553440000000000F8B331F4AEC7900AD1B990899C54F87633EBB741", + "json": { + "PreviousTxnLgrSeq": 8896, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rnGTwRTacmqZZBwPB6rh3H1W4GoTZCQtNA" + }, + "PreviousTxnID": "1DA779A65D0FAA515C2B463E353DC249EBF9A9258AF0ED668F478CF8185FDFD6", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rPgrEG6nMMwAM1VbTumL23dnEX4UmeUHk7" + } + } + }, + { + "binary": "1100612200000000240000000125000038562D0000000055C10DFDB026BBB12AC419EC8653AEB659C896FD5398F95F90FDADCBC256AA927C624000048C62D41A00811484B98A8D409C49401F237D6D988A0BAFD114F760", + "json": { + "OwnerCount": 0, + "Account": "rDa8TxBdCfokqZyyYEpGMsiKziraLtyPe8", + "PreviousTxnLgrSeq": 14422, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "C10DFDB026BBB12AC419EC8653AEB659C896FD5398F95F90FDADCBC256AA927C", + "Flags": 0, + "Sequence": 1, + "Balance": "5001000000000" + } + }, + { + "binary": "1100612200000000240000003F25000094F12D00000000553B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF62400000E484E2CC148114550FC62003E785DC231A1058A05E56E3F09CF4E6", + "json": { + "OwnerCount": 0, + "Account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV", + "PreviousTxnLgrSeq": 38129, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "3B1A4E1C9BB6A7208EB146BCDB86ECEA6068ED01466D933528CA2B4C64F753EF", + "Flags": 0, + "Sequence": 63, + "Balance": "981481999380" + } + }, + { + "binary": "110061220000000024000000032500005A9A2D00000001556C2C3126A945E8371F973DAC3DF4E899C2BC8AAC51DE6748E873BBE63FCD760762400000003C33606C81146A03714FE4B738A637A094271E0DE8414D904CFA", + "json": { + "OwnerCount": 1, + "Account": "rwCYkXihZPm7dWuPCXoS3WXap7vbnZ8uzB", + "PreviousTxnLgrSeq": 23194, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "6C2C3126A945E8371F973DAC3DF4E899C2BC8AAC51DE6748E873BBE63FCD7607", + "Flags": 0, + "Sequence": 3, + "Balance": "1009999980" + } + }, + { + "binary": "1100682200000000201A00000002201B000094F00213E03F056D7738851FBD18F97540878F241406CD9500C09FA122276F76EDAC1E1D2CACECC95BC5EB4C803BB1366ED330DB5B9147AD226C2F3FED211E90DE6CC0F71352509B11298E12037FCEF95D23CB114F2D915CF10517C6AD1BC3167FCE1DC7BDED6F9F019AF5B8E05139FE9CEE935CDC23CF20DBCF9F1158E1E768ABDD69F4A0A1A2C7F4879262338CC3DFE555F95BDEAE83D7D794AA366E0F4763517BC97CE699D6FF9995F80CF77C328078F683131701DCC785432BA2667714E631B63925C9CB96E2641030BE3125203138BFB5462F6FC92301A7FEDFA4E9BE1524C88EC518680EADB49F36BD52EFC108C5A391BB6BB476ED49567890E6905F34E33DEE148BD252E148694FFF4EEB299200479B890AC44391EFA22A8B8457A284531324A7ADE096FD3D3F92D86A80587412830744FCE541D784D3B8D7A02370BF69CCC766247BA1DBD3008FC62317DCC4032DB4A977EBB6FCAB97DF52707D37B8095927A007E7E2CBE8FAFA93037CD348B557D38C522E089C2FE3630D28456393A6107B1A0318718908DB484B0240B13658A74B9C37AC5EC598259CF53EC60FA572EE5C1F498BCF4382954F0BAF03E4F41FE36F8AD27420A617DE2B83FF0E8C0A64FC657C0116F08A893C5F6BD8357E91AAEF0A5FE43CAD434ABAC2C50E12D0D1A2E8FD6AA50FAB0FA33B50D992508072E9AB22AA9267A52B220E9A1340A950DCA9884561A6D7E9AE4FCD90FCB2EC3ACDC586FF0745F088589FF3ED2304C2207A9AC90C53281D7E62332FDAB4CC24C84F65EFF2C56E2DFF46FAF06F1C2D06097DB371BDA055BA5DA7F596B35B93F3744C24FC24D38EB1F7C1DEE0B82D623BBABB3C7B755E84F4DB1815DC49AEC22B8D113A31C49F21E2C2E16AF90382782DB88FED3314A5852F5D325E10EC62FEEC8A81DEA67B9F6C063CC1C6AC4D9B3D50ECCF0871D7C1F77D35F2C0C7481F746806534FE07AD0079113BE3E66950E6C8E6F4EC936D0FA0A6798690C9DA76E98D1AC64016FB22409305CA8FA80DB017F21D320735FCFA5E5C2527BA3AD471083EDBADF1E16D0AAE66C523A2CA046269F5C247A0D0CF79B49D0D5425FF294972BE11A257450D0D50BF13B921468FB3CEB97F3CF72934C5D0E559B7C77B98A5F5123977EEFE8D8379E8B452EADA4C53118307D166BABFB1F67BF33D66153DFB3A5303D33D02944740E7357E9E0215B97B8BBE413174353B1814517A5E7A6F002129A90068D1C7B70EBB9FFCA5E2F5E265DAC1042C5E0270C36153DD7AE830D991E6E3D8A7CC9D01C19D1C08CC8CCEBF867125D4CB453C50378036B39622CD91CE6080C4233730E5E67AAA4F374ECFC9C9FB227DF182E3A59DC4455C43A1AE5F2F70ACEB0FA9CF4EF7C2CA9C1B3EA55D6FD2F79AA170F8BC2EDF394865D4CA990E82D2B5D3166AEC38DFE10E4EB6B52CFD17326591056D5DEAF68D34B27A33D0E9C7897AF7FE64136986EA66094796144F4ECD42ECDD517A51FAFA741432A6BDD7803DDEECD58344D760404FDEC50ADA38CA9E68AC8D1AEAAC445EFA19B907FAA27C601659A82F0F26B94AC040F88E4F7B9A684BBAE8162E3C34AD7D533B00FC59F9858D886DBFF337B6D03ABFA325397F0A522266D7837381D58C10E1A8F1D0C6E37DBCB88D98BD6F380BB62E335ECA96CA364B8D4C6EC9EC9ED1FE534EB25642A007EF9F970E0CD3B8C0C9999C37BA970389238F320764EF6B1D43B5A8D55553D40B5A98E7EA0DD650EBAB18EDA2CD6BE11EC8B21CC330CC1B75D843AC4F2D4A3ADB8485275D344D4A87BC7956F6CED377B361FB2C31EB439302C784EF08739DBDE46ED4DF42941B01591757AFCD176D9FF18C29D1FCC99F0767B1B767F8BD9882AEE899E3102433B8CAE6375564A624D4C94883CFBD0776A8EA3CBFB78A3255C4A679C42CBDEB5387D1460DE5E53FB82B1D2EFECB41ECD1A9F0188EB6FF2CBE5599599C2E963BADE3B22BD3CD39C76D59CB0FCCA2FBAD9CA3D7F45DBB723564112648198DE968DA7F517789D8F8C7D2C787EAA9A7BDC7FA5DB92EC7B02E82D4FC924A2CF62A13DB68035BD27B29ABA7E02872A2E3364691B5B2B9A177AA5092513DD872484C34393462531304E65C3D12E9208C430502953AE08E7DE50D3CC7950D189648CAE50320FDDF9E28B3B7382A84EC2F2C750F69BA97392B6F3FBB2FE83679AADAF8BE84A3352A1041B87F5DAAAA04D58D02470D41EE11403585E6FB5DE6192C919E10F81107BC5B5715684E15D8A4394DC59E9B43D9774DCC8E937CC57B3517D25409831FF31C48E4028C4E25FB5B15D51051A8215B3255E243A7F7D7DE9022C5F61D58702E99D574EC46219CC2C615BCE173AD429E6871C2A2DA2E41C9D7F7FA5250B2A4650F37446463A85E88A18922C8F1F90607D597E3A3EBD5F498C45106C21526E67A1491CA7EC83536C14815038FF8ADFC96FD6D6060009BE5D2FC0C04F2B78224F322311993F2A2B135B0E4CE49C02B50C1F99F6D38DB112CA3AAB7984F57EDD6C1946820FA96271EC1E7084233D5034D9028B140ECEA84849D936E2F2C6120C8B13CBC4B97C8840C6F62491B619F9928A954EF6772EB72B74F4ABF5A377398EF4B83EBB1DC14753F40B24CBF2E458347AB6ECB4F5CAE7671E9442EF45AE118A2FB668DAA1D4DB7784D120BFDC40355B5AA05A877C96CD89FA4F32C9E0B0844F5C3A3D5621F9A6C1C1E32D7440A5D4C59DA58C28C144DCBF337DDAB2CEB1398C523A1D27CA237AA31AC7AF2DA24A2AFA4E85DAF7919222E8825BA3B75F0565B2576753FD121EFF68DA6CEC721AF88A9C8721DD00E5922193FF0EDACCFA861C3895C69D5B4078363228FDF2FB79369A88B5F400334A708DF3F512634F61D783ED5F5869513EAD4A6FEE0C3F3EF2FD063FBCBC5F6F7EBB4F2FD246934700F320DF96EC29E2BAD58955C8BED79FE88C893CFA2229B8ACA74DC538B69EECBC9890505704544BFA19770E9B276D5657C8869FA0C5FF54EC678FEB98484F59040412045651E7E8776BB3933F3664BDD4442B0DCAFDCED05409F5F2F0E1C7F9B590286332CBAACC8CFDE9FB65C92C6885A3A09E63478395B56880BD65DCA3356EC96CEBDBEB9EECFA35484B51EE543C66A8D0E06F277281C5F3F61893980CAFCCCF3A595F50D03E275345E81D47AD19F5656795924534E16B6AF3F930610C0C98DA713ED5A77738E1C187295163ADCDF50DFAFC5B6DAF4401FBFFB4E6BA792DBD76DCE26E691B784BD7A27ABAA23B27538A9352582E055CEE9A4B996818C1FCC3954AC9E3FF5A904984D3844685BF08A51E8A532065745C1C30EC71476511A325E61A72622A61AFE77689D4267C37B0522005008FEFD4A2DEB0C071ECF5B7007D67935486DCC1BB0C21EC54D194538F8F96C6CCEE433BB3C65E7DCCB6A2C29BA7BE979747FF26C2C5C3DC1261840ED5A931AEA04680284791B7C98765E879D4F9653983E5A66815F094255E1C27F7C7327BC884C19BC36C37D41BB45D9E6F0DD32171B1E03153A51C60BEE164A8BC18DA7D46030DE3210646F17D30B1C33AA82B3A911E8204FB37CA441C5D6A05DB693B5CEF6BA6847A6F1088D3B30F93E898220F0805AD342EB1CE2E1B79D0E74C533F3354E2157FC9F9EB4DDE0E2B78044F95EC0BFD850AA7E1ECCB038BB724CA0BDA92F17A37FB06BF2E7943B107D958717068E0F0ABCA3844C24069CB9B651036E5279244202FB2EC022BBE9115CB1B5C9FFCFAB714D5D9D107359AC9FE6EDD4C5AA30C8F3BCD0C1292EA381B96F2163BA3F2C7BCD1BB6737557D6D773D6473B321069F198CA32CEEB91982EDD48C975E14D1B6BB6F9D89E50401EBBE44B859F92BAED3F3712F39EC62D4D31C3B5CBABA20B760F211848D2B91DBC81093E65476CB2A3993F365DBEE85C7180F9C5C9DE1484BEC463129DD9F3D8423EE5F71C695B9D3BE884F38C573238771A20912C888D0F5E78F605802D3284F0BC44A88312AE9AFA05444194A8C0FBE4C67873DE00943C6BFE46BF0BADC0E599193B9B280B3E4A89364152236B2EC26A12C3507BBB73000F45C66D99EAB36C71CCE074406B3DE2AAB55229C3EDBBC3C5A18E4700DE363D5E6B62872911EB287BC72F28C294BBD867A6792F50EE5B0BF99D86609240B360F841748C207B167D8DD883119D212738690AF083C1200A655E50C8CED5E050131508AE11239B9A54964487AC1A4C37D00EB65D241D53788A52018C2E64631486C47F33E0BD18F175790D5A75CD7104AC4B940BFB007D8BCCC5395A11AD09B2E082A47E89A9452F26C1448A7D2F9477EBF007A53836BF05E781C00092A1182FE8CF481F51086BE939CA03BE98108FBB70F20C703ACC725A403B511FA46DC93B469682F0720773F7795A7D502793BFF4C14EA367E45014AED8A6657B0B6CA7C8C90EA27561F5B1200D296FEF2B8D72821E4B2C7CF7EE683A29CF5D00B9BD9D0FA0D33220B1267F5F2B4AB068052EB3B437CF10403DA941B01177D7B99719DB51139CC95C818A196B4F651F274951B323BFDDD04E4766DDDF1D677B72F5D9DACD348C4E9CEC9155A4C7C1DC1200097D936010A521AFFBADD2699D37CCB574C7B497A213C9E1BA6BB137BEEFC18149959A01EA1B20EAF970E1C31EE2D50FC8A6116AEEAF7E076AA0F532532E7E00F2FB1C97766F2EA4C621E3E6E7EA2D537C4BC16289C851F45385772054EB8E480FA8FF4F1C73B15B1D4BCFEDCA38B86CA12D5253407DA51DB84F52E173F9F50CD4C33A029B1F6265B67070DA3D5B5922AF42B8525F41B70B0D500CB11E0A9A65FF7979855A327A074EB669791E0AC9FC44DF8782E97276EB844936BB8BA7EF16D52E02BB4DB0340C9F915B255DF51D876B1F3B50FBB73D5BD029DD6607D7AB223CAF21AD8D1BAD0D0FDA0029952FE27437F1B2BCB376FF19D29E0C43BBB3FD38122EB4D193AB6E7D96F7DA38C84044720A5D25CFABF3B83377A46B6F5EE32EB37EAB9628F4797E500E42FAC740E896D8696AC2C6C7C6CB6AC3C66DCACE88987B59B1CB7A0E4AA12757BC03F5D14ED09D6CD4CB8CA51EB1BBE2250FB941465D037C733BC88E978C39B6355A007386E66E8868C50A6CB2C2FE73360E47B083BCAFD9A692B6BFEBA01683DBFC2F170B7E740D423D9728969E1313E9C9BB24424A849C045206AAE3B5457747904177BBE8EB93F869D1A0234E54EE9492C15CB796B66703C0F7BA49D87BD0DF2C0CA450FF2CB0983602C0F31837F14D18DC238C0291BE45BAB61F9CC515869B6B0308C04A2EA597A51EDE1F919AD42C34744E32A12D46D8972EE7E44B63E5EE63B300CF7B5D433DCC4EDB982796A3B68C23EFB1DB8164C74A9A21B1AEF0A56ABC0632AE52285B8B270C483C7C5EFC9C290A2F6F2B7E4D7486102E722CC9664AE6C47D7FE35ECFF08919EF30B2876520B947935D00CAFBDEB2B8ED1A626F8EBA8D7BD0F9767AF2FFC62F5A64831AF9858C6AF7D4220127F39B8508389B73BAF31CB0C3193D8F2B9DB44C2168DD2A0E2632A38F4A2317900C8E0199D5410E3D8B355232E53F0F29ADB4A0D2BECAA0ABDB9C4F76C7B49D5133332C2C06D92B690626D0D6B54FAB461C4A36736F8BDF95364DBC4D594735E226649EB05458AD604B587E6C0F3C7DF04C44C99AF993AD21526AE686FC3365901DA8999C42CA4CC833B6CDDF9F38779913AA285EE0104F3E283170CA0FC78F2653FC1D3666C10AB0FE6B44818EFE913618BC30EF7E62D22829B10F0EC84F14B8DEBD5F2554E57352057AB9D4436946106493CD78CDC9BA9F9C28AC08364305E41D4891A6818ED5CB8851EBCF0A410D083BE68895CA36F7C334C28A84C2EF75CF365815DB0C5DA29E3EDBB46B9719E07B484622F17B7158503E842F3EB7700BD2EA056F77C2308BCDEB868158A277F40C954E9E22D2D206265BBCC4FCBC900A5312247DCA036128D0A8F6725393E747E5D63792F6528D17DEFB88A250528B6E09C3961809EC40ABF6E35F6ABB9F0FC1E5BBACA8E94CB6A339BB039D724A4FBF5BADCF6E0A49853B33797E9EF0D5AD3741AAAA4070731259DD46F3510B2BAA53CA03EF0C7EC2D793A8EA13651B67041E43954FD0D2BCFBE54B2D4BF5F2436A58E747AB9C5977EE0029CE8F49B3A8881D3A80A996DBE4CB6DE93C8AA6C495C6702981E16D4A483E4C74BB06CC28ABEB79EDED2A5343D52FF75E1653E98185C7398277E9B64861DAFFFE0B50B26ACD98C9183D0CD16FE210F7E9A77F7313F630A9F372924DBCA02CF84210BA9CB5E648F4C8F21D9CC2CBE72D683DFC30735D578604D2EED6338C258BF565F30320F5CE89215D1BBD028DFD315981F7420672F84D0CF35A6D02CA4D2EE3931DC193ADD06318BC435076C056004CF40390A2F762D5E203AE991B49F0ADAD787389406BC027778E6B66D8B8ADF6E68045675AA3D0BCB859422280AEAE5BB17C736E651C2DE1C59662CC592305814B5D25C69D501D13E07F0BF82526FE2681664E8F719528ACCD0785BDA00CE60001066F8E7EAF25B74C3E406E6E88E2C363BEB0B3FE8796719E0D0F35AF6B1382977A60BB5275642CC09CE9B54B3D4FB6B90F34E96C9EA29C7C19667A41475FFA614846434D56A4D224E5102EFE617DE0A3B8524535CACAE5B2C4B05DCBA6AE54802C4B6B20977608014426CA0E4EC14AA3FDFA1D9B1FFC9B2CC9CDCFFA798B6A46CCCA472B6BB700E8C4F4150208BDAD37478A894C201DEA05937B7B508AE8B3C32A02CB916439DBCE58CA63DABB611C544CA7BC3FABFCA577DDD94C15A9F9A82DFE9959B5D19301D28E08546249B948EA79E7324A5E4B8B02304C9208609C2A36D3F70EF8115743167E7EA1039C4733D7D907BB2018976D740C4383C938DCFACFC397F21653C8D73A57FB7573C7FAB2420437C6E283036F6AC8B729F238CBCC61FEC0EB5CB95EF8741BCBD27236F7BABE8DD45041E6CE9941A9F5C4D826C9A7FA04E6122418C47D4306E4C8883052C44AF37B03CAE2C0CCDA926CE080A86FDDD7BBFA79934FF895B0AEEF492000C7EDAE27EE7A656B511EACB20E92D69E6F1D307FC9314F87DE9282E791413F818791AABA4FF5129DB2C93EE68F0AF326FABCB85AA07AADB6793BBEA4F59D354E862E3823E89F56FDF1496C4D2D310130DE1DE3F9959F9C30D27C16E59FE80F0BBC2FF93D4D2FC97084ACDEE0289EA8915C5A27492594447875D8A4FD54E7AA10AC1AB16A156ED2871C2B89FB847E907F5361C89D503254D8D3D9497E8D446BB403982202D0F004CF5B8686AB36F47F27FA6F4866D4B90DB81D8E3BBD239AC8800C262718EAD1DCF7A8A193C6C9EC936EBE963E1D24EEE35BF0F061E7BCF1D35DA97818F83781C351B3CCA2D06D47798102486F33D335C0218D6FF2BA1312C969EA4106794773A59BC5596A3595BBC1693AB10BE7AED59096E9288F658CB7C8877D00E08E0E28ED9F53A0711384A13967FAA980AEE5C6812EC6D3B78AFE8B385BB343F5D97056B31D3A07B02BF02FACBFC8DDE93EFD28BBAFA00DB0EBF0F98EBA436D633C7EC029ACCAD3C10BA420CE53BEA14245DC2A30A79BE43A305EB603E780506208009FDCE2B64F4831436EFAF7F5A98B20D5676695C84B108A4B67872A70C38916704F7644B384A60794142A8C6CE6159B72A7137BF32EBA473C05E0DFD602D4FD632B4E6E14EE35B90C961E87FD85A0D79FB729D33378C0DFE46C772C7C241540A573C18FDEF97B591947988C9D6C9C1EAABB21144860D171A235F4A2C9F311D7BC3CAB11E9419E2EF09B145367D077BC65C20AA4B0728DE4D720CDBFA2127E545F29FFC264332865B962F6CEFEC9AC1EC453FE7C8541715E078EDC66774F9987E4FF2104405763F827F7A04C02684033EF0881A55218FDCD75749A7E5E4A6214893E710016BC81C46D6A93911F0C68FA129C593DA258AEC0292B3FDBF94B220D8A682F7476A00ACA23FEA85D627C05B56260AE0CBBA664AB4CD582AF997F77A0EFD9E7EE75FDFD6C262BB1366FF411E7957317696F9FEA3FA77DEEBFA2693D2C1D57BAA64CB43874EDEC4119D4436E0154B34DAF7166A2ACA9B4F5EA62E405FDC9C170BC06539BC5AFD5950598633A2270F3EBF7A583C84685CCD787E97E1F9BB6567AA7AB0572AB0244B88A784967DA23BB624952A9EA0C7C388789CD5DE174A6E0F0CC6001BC6FCC4A7B8A35B76709F373E236D3D100F777099A42263CB49FEE9896E83955A9CC87EFE254C825F25F0631760A52F8864F5A286C8787763ADA89CE5200E765E47D3C9C7BA606162D60DF71A3091894A27C850B05B33B9F7CF8DCDAD6358E0B353C083CC3922C81B4DAF4A175574C1184F530ED6C3185C9524C4905778C2B780970F8FBDA80B6D1C79FF2B9989A6368245640D92AE1A567D56DFE71C719AF4F92720101BF381A6121F8C5944E36495CFA22998367D8E168EABA03CB34535E807584491F439E5E622BC54729DFD0073F9AFB55641CAAC1DE5F0AAF0568F1319BED1A6E6234A6B6BEFBC5BCB6F39C07A8B794F84CF3F18732593E9D9F33326FE921325B30D39F26EFD93739D4D3D46B29FA77C21FFD415C52B595D8B7FD494D2EED53D929006910031144ED21F25ADDEC1F83D2CAD96445B6C4100EB1A3F77DE533167E459BE7240987F26BE436C05D7501AE3B47AF91AA80AB0905CED76219D208D262183168A84758ACCAC0173312BDFEECFEAB63748FF8B922D41A15207167D290656E1FE29EE1689F6D24AD0D3935F81902106C78EF5CC5FE2AFEEB9C20062970886FD6B6F92E309240AC25A8794E13D1FE433ECF81400230C6241689491572F2BE8BD1ADA201C755AA09FBD58B3F01C2EA9F6C38FBC30B14D0B82D30352B920416D821BE00C391A807013F678C07866B595A9AE738A8D92FB2055B39D6FF7682B02B592A670E0A0D908AC27270137D3DDBD0FF8AC0F4FFA9DB1383819C6CE28196ED5F3EDBE7A1FA94445E00C672AAC61BD41336794C6C0190BEBD34311C076BD9686BA0B1D7A043184FC37F379563ABE97A1CB7ED1E0E1101D1D35DFFD64054C91CD82925FA434C8BCBE05600DFC73AFE56518FE049B578D75BCF9C41E6F5C87BE54F76A1AE329C129D1D4FD219150B76BC704C4BDA02A86CB74ED364942FD0F3599F953BD646FA6025D5DC090C8900CADC94983F5CE315FD3F9DA2FBC43455441E5B9F43B2A852FF539818BC4D1004677EFF67EB26942E57B59A1C816DD7B023974A820CF39E5111E323B64714B71E513F32D4ADFC4020F18B45515E28DC63AEE2C3E803F3CDE5BB52DC5290D95D701A57BCC2E1AE4D627BF109801536717DCA33D1B393736F34BD413067C576A78240A96ABC7F144501F1D58899F7E6C3A846436427DA09D0958A11EF83A5E9741752B9534B1EF64034F640E653A0558B25C5B457271A496CFE0F624763DA87362626396F4BBDCDDA1988F4B0022213D725ACF50E26ED43257EDAA080E8DBAE8A6DFC1683CEFCBB1312083CAC0826CD72F17F122E8F4F53E9C4266CD2D0D10C94876CE38FDBE914590BDAE1C9E21661FC7A65FEAEEE951A73F695BDF8CA0E82FF811CD588D4769495651FDD51FE5F200713A5F9AE81B5F7089066F14642FC492CBE6568AE81B321B8C44C9BDA8AACCD0E6F2B123749CEA414AA3D569135EF8FDDB60B23A43C3F2A36AD1FBF7F0617DBBF4C10B01906A17BA83657B604945A57AF17E79FAA5ADBBB2B14726E4F2C84130D098109E4D8723BAFC4A9F08384553EC904B5A7798C9C8A1A71AF0E102EBD7DD93D412CE63E386D7221BEAB9770C22BBBC051EADE759A44C6622A0F8D2105573CD2A6494C9B983FEE0D3B02977639EE575BCD2F4EF3D4193B79BAF0D3163D935066393FEF8C2EB5543B5DCFA8FE465F85E396982E197733FE4FB9171E4649CCA3D59A391548265046059BE3788B3FF39CBF92FA677048E0DF5E7953DF0FEE769AC70228E9513CF6B1F4EBD86A1A64C6ADCCD0E66005871FE43B8F38E62A4757616C70CEE02EB2FCF9F45E4E1A43EE4C8022ABDB97333B1C891795E8244A5EA1D43139906E5CD6D9FEC177EB3D5CFD598B99599C0FCB8436D81C724EDEA609E5C5572369DCC7FD1D0986C7B82B42CC6E2D1E80A3FE8F9F96F400AA10A6BFF484F5E07D6D697152E35E3CEB3FE0ED9886E1607B77B1048D8F3BBE219C82DF2CC1800393EAD3D929299BC75BACDF70380C6C6067A33238D11A28E4ED96F099C0CE07707FECB985D4828EC48795D365B78DFABEC653071C0BF745538F694DC743AB6E9F0CF0C05833E57F6600D3DB3F247FC45C0224AD521A6CFDE6DFA90BD8F64F8649ABD6D4EE608BC3F50B42C37CE5708EF1A815211623DD2503BCFCD803762316DFEE6AD32D56CAAC1D96CB58677E893DE386D9B512AD3E4EF842488E8CD8C7AB209282D213329D2BC80AD3C1D8676AE7BC1A85B4564A95568508C40782DDC028E42FEEB78F67E5C188BD35098CC58CCDD43FB4BA51AB47EE1AC2A77EB0862FC5E6CA4319F95AA960679B572A476F8282C38F1C2823D3639C7CD3568BF877D40754C585A6A4A0CA1A57F3B8CB144A01CA1DE18E4489F85F3265799D576F322295A6229A705D212BB25C82B639A47D037B0817B883475D0C8EFBBA6764313900DCA6EEB64B195D25F089224B8DB461B306D461A6966856CC290527E7C357B2E5A0F9178C6BE1B0B566FD513C814F27A664A62827DC634658A6CCC84998154349F6080B21AB1FF1BDAF9D9C7B24984BA5E6CD06BEDD7BA7AACD9E8F6BAE1913EAAD4B52F5FEFDB3F6C69317C36658749AF39120810F566C90927A201F9281C4D652EC4DD0676A179C1B731B59FE062D6BE1198AA1483E41C38D24962B4DDB2711539404CBDD97A7393EAEC52A81E03FEB7D159D9290996BC5BFE6AA81E2AF93E3A066F2D7818A40E51FEDF35CE848023BE3330D52484130D7739C48A8EBAEFCF19F631FC06BB3C7FB5E7F00462BD6D58F5C1027B0BCDEB594FC1B97A8D4E414A0B982E3251A62419396A9D1FA8114A1A777BFD8A6CE15D9EA895737B7B3EBEFAD289AA36873E7F11EBE74D4A014B1AF8EBDC159803B911063A0E7A191BDB5F7E7F0A55F69C60BBFC262521134E71198C7EDF1465F294FDB518A9C02DEBCF75B1A24EA924059630B6634467C5AD6CCBEDB0077EF9AFFFEE695C90FC5FD33F9DDED79B78540D5FF1D4C9B2A18D2AFD5D3E0A2E7FF5F00A05554B63D5CB16236D4307C1DA72CD51DCCD1F8D454256BE6361F3B848C4423F56AAA6117797578DA5047D9C500F56FC1CD3BFCE0CB052F6EB6BC5D6BD932C29F2BB50ABE7AB7DCF1ACA12EB06E38C2B3B2C54B753A05B81F3B5E18CDE1F164F3FFCA49DD813109E8BDC408360E80BFD9CF945501BA3152FDAB719B2989946560145910EC78E8775AC2925611B580E043098816E2D8391ECF6C7333D1D31FDEF1BEFBB364250F2E73783579D3229A75B98B91582933D402914C2FFA2A95F8D358BC77E1F64DC0B37DAFF2738A915E049D5240499E46920B92564B532C67873CCFCFCFE96806BDB98DDB651A4C9C80749C79215ED8ED26BEDD10E49AB91631CCAB5A63401E5B2E5D3A53EB0891088A5F2D9364BBB6CE5B37A337D2C0660DAF9C4175E", + "json": { + "Hashes": [ + "056D7738851FBD18F97540878F241406CD9500C09FA122276F76EDAC1E1D2CAC", + "ECC95BC5EB4C803BB1366ED330DB5B9147AD226C2F3FED211E90DE6CC0F71352", + "509B11298E12037FCEF95D23CB114F2D915CF10517C6AD1BC3167FCE1DC7BDED", + "6F9F019AF5B8E05139FE9CEE935CDC23CF20DBCF9F1158E1E768ABDD69F4A0A1", + "A2C7F4879262338CC3DFE555F95BDEAE83D7D794AA366E0F4763517BC97CE699", + "D6FF9995F80CF77C328078F683131701DCC785432BA2667714E631B63925C9CB", + "96E2641030BE3125203138BFB5462F6FC92301A7FEDFA4E9BE1524C88EC51868", + "0EADB49F36BD52EFC108C5A391BB6BB476ED49567890E6905F34E33DEE148BD2", + "52E148694FFF4EEB299200479B890AC44391EFA22A8B8457A284531324A7ADE0", + "96FD3D3F92D86A80587412830744FCE541D784D3B8D7A02370BF69CCC766247B", + "A1DBD3008FC62317DCC4032DB4A977EBB6FCAB97DF52707D37B8095927A007E7", + "E2CBE8FAFA93037CD348B557D38C522E089C2FE3630D28456393A6107B1A0318", + "718908DB484B0240B13658A74B9C37AC5EC598259CF53EC60FA572EE5C1F498B", + "CF4382954F0BAF03E4F41FE36F8AD27420A617DE2B83FF0E8C0A64FC657C0116", + "F08A893C5F6BD8357E91AAEF0A5FE43CAD434ABAC2C50E12D0D1A2E8FD6AA50F", + "AB0FA33B50D992508072E9AB22AA9267A52B220E9A1340A950DCA9884561A6D7", + "E9AE4FCD90FCB2EC3ACDC586FF0745F088589FF3ED2304C2207A9AC90C53281D", + "7E62332FDAB4CC24C84F65EFF2C56E2DFF46FAF06F1C2D06097DB371BDA055BA", + "5DA7F596B35B93F3744C24FC24D38EB1F7C1DEE0B82D623BBABB3C7B755E84F4", + "DB1815DC49AEC22B8D113A31C49F21E2C2E16AF90382782DB88FED3314A5852F", + "5D325E10EC62FEEC8A81DEA67B9F6C063CC1C6AC4D9B3D50ECCF0871D7C1F77D", + "35F2C0C7481F746806534FE07AD0079113BE3E66950E6C8E6F4EC936D0FA0A67", + "98690C9DA76E98D1AC64016FB22409305CA8FA80DB017F21D320735FCFA5E5C2", + "527BA3AD471083EDBADF1E16D0AAE66C523A2CA046269F5C247A0D0CF79B49D0", + "D5425FF294972BE11A257450D0D50BF13B921468FB3CEB97F3CF72934C5D0E55", + "9B7C77B98A5F5123977EEFE8D8379E8B452EADA4C53118307D166BABFB1F67BF", + "33D66153DFB3A5303D33D02944740E7357E9E0215B97B8BBE413174353B18145", + "17A5E7A6F002129A90068D1C7B70EBB9FFCA5E2F5E265DAC1042C5E0270C3615", + "3DD7AE830D991E6E3D8A7CC9D01C19D1C08CC8CCEBF867125D4CB453C5037803", + "6B39622CD91CE6080C4233730E5E67AAA4F374ECFC9C9FB227DF182E3A59DC44", + "55C43A1AE5F2F70ACEB0FA9CF4EF7C2CA9C1B3EA55D6FD2F79AA170F8BC2EDF3", + "94865D4CA990E82D2B5D3166AEC38DFE10E4EB6B52CFD17326591056D5DEAF68", + "D34B27A33D0E9C7897AF7FE64136986EA66094796144F4ECD42ECDD517A51FAF", + "A741432A6BDD7803DDEECD58344D760404FDEC50ADA38CA9E68AC8D1AEAAC445", + "EFA19B907FAA27C601659A82F0F26B94AC040F88E4F7B9A684BBAE8162E3C34A", + "D7D533B00FC59F9858D886DBFF337B6D03ABFA325397F0A522266D7837381D58", + "C10E1A8F1D0C6E37DBCB88D98BD6F380BB62E335ECA96CA364B8D4C6EC9EC9ED", + "1FE534EB25642A007EF9F970E0CD3B8C0C9999C37BA970389238F320764EF6B1", + "D43B5A8D55553D40B5A98E7EA0DD650EBAB18EDA2CD6BE11EC8B21CC330CC1B7", + "5D843AC4F2D4A3ADB8485275D344D4A87BC7956F6CED377B361FB2C31EB43930", + "2C784EF08739DBDE46ED4DF42941B01591757AFCD176D9FF18C29D1FCC99F076", + "7B1B767F8BD9882AEE899E3102433B8CAE6375564A624D4C94883CFBD0776A8E", + "A3CBFB78A3255C4A679C42CBDEB5387D1460DE5E53FB82B1D2EFECB41ECD1A9F", + "0188EB6FF2CBE5599599C2E963BADE3B22BD3CD39C76D59CB0FCCA2FBAD9CA3D", + "7F45DBB723564112648198DE968DA7F517789D8F8C7D2C787EAA9A7BDC7FA5DB", + "92EC7B02E82D4FC924A2CF62A13DB68035BD27B29ABA7E02872A2E3364691B5B", + "2B9A177AA5092513DD872484C34393462531304E65C3D12E9208C430502953AE", + "08E7DE50D3CC7950D189648CAE50320FDDF9E28B3B7382A84EC2F2C750F69BA9", + "7392B6F3FBB2FE83679AADAF8BE84A3352A1041B87F5DAAAA04D58D02470D41E", + "E11403585E6FB5DE6192C919E10F81107BC5B5715684E15D8A4394DC59E9B43D", + "9774DCC8E937CC57B3517D25409831FF31C48E4028C4E25FB5B15D51051A8215", + "B3255E243A7F7D7DE9022C5F61D58702E99D574EC46219CC2C615BCE173AD429", + "E6871C2A2DA2E41C9D7F7FA5250B2A4650F37446463A85E88A18922C8F1F9060", + "7D597E3A3EBD5F498C45106C21526E67A1491CA7EC83536C14815038FF8ADFC9", + "6FD6D6060009BE5D2FC0C04F2B78224F322311993F2A2B135B0E4CE49C02B50C", + "1F99F6D38DB112CA3AAB7984F57EDD6C1946820FA96271EC1E7084233D5034D9", + "028B140ECEA84849D936E2F2C6120C8B13CBC4B97C8840C6F62491B619F9928A", + "954EF6772EB72B74F4ABF5A377398EF4B83EBB1DC14753F40B24CBF2E458347A", + "B6ECB4F5CAE7671E9442EF45AE118A2FB668DAA1D4DB7784D120BFDC40355B5A", + "A05A877C96CD89FA4F32C9E0B0844F5C3A3D5621F9A6C1C1E32D7440A5D4C59D", + "A58C28C144DCBF337DDAB2CEB1398C523A1D27CA237AA31AC7AF2DA24A2AFA4E", + "85DAF7919222E8825BA3B75F0565B2576753FD121EFF68DA6CEC721AF88A9C87", + "21DD00E5922193FF0EDACCFA861C3895C69D5B4078363228FDF2FB79369A88B5", + "F400334A708DF3F512634F61D783ED5F5869513EAD4A6FEE0C3F3EF2FD063FBC", + "BC5F6F7EBB4F2FD246934700F320DF96EC29E2BAD58955C8BED79FE88C893CFA", + "2229B8ACA74DC538B69EECBC9890505704544BFA19770E9B276D5657C8869FA0", + "C5FF54EC678FEB98484F59040412045651E7E8776BB3933F3664BDD4442B0DCA", + "FDCED05409F5F2F0E1C7F9B590286332CBAACC8CFDE9FB65C92C6885A3A09E63", + "478395B56880BD65DCA3356EC96CEBDBEB9EECFA35484B51EE543C66A8D0E06F", + "277281C5F3F61893980CAFCCCF3A595F50D03E275345E81D47AD19F565679592", + "4534E16B6AF3F930610C0C98DA713ED5A77738E1C187295163ADCDF50DFAFC5B", + "6DAF4401FBFFB4E6BA792DBD76DCE26E691B784BD7A27ABAA23B27538A935258", + "2E055CEE9A4B996818C1FCC3954AC9E3FF5A904984D3844685BF08A51E8A5320", + "65745C1C30EC71476511A325E61A72622A61AFE77689D4267C37B0522005008F", + "EFD4A2DEB0C071ECF5B7007D67935486DCC1BB0C21EC54D194538F8F96C6CCEE", + "433BB3C65E7DCCB6A2C29BA7BE979747FF26C2C5C3DC1261840ED5A931AEA046", + "80284791B7C98765E879D4F9653983E5A66815F094255E1C27F7C7327BC884C1", + "9BC36C37D41BB45D9E6F0DD32171B1E03153A51C60BEE164A8BC18DA7D46030D", + "E3210646F17D30B1C33AA82B3A911E8204FB37CA441C5D6A05DB693B5CEF6BA6", + "847A6F1088D3B30F93E898220F0805AD342EB1CE2E1B79D0E74C533F3354E215", + "7FC9F9EB4DDE0E2B78044F95EC0BFD850AA7E1ECCB038BB724CA0BDA92F17A37", + "FB06BF2E7943B107D958717068E0F0ABCA3844C24069CB9B651036E527924420", + "2FB2EC022BBE9115CB1B5C9FFCFAB714D5D9D107359AC9FE6EDD4C5AA30C8F3B", + "CD0C1292EA381B96F2163BA3F2C7BCD1BB6737557D6D773D6473B321069F198C", + "A32CEEB91982EDD48C975E14D1B6BB6F9D89E50401EBBE44B859F92BAED3F371", + "2F39EC62D4D31C3B5CBABA20B760F211848D2B91DBC81093E65476CB2A3993F3", + "65DBEE85C7180F9C5C9DE1484BEC463129DD9F3D8423EE5F71C695B9D3BE884F", + "38C573238771A20912C888D0F5E78F605802D3284F0BC44A88312AE9AFA05444", + "194A8C0FBE4C67873DE00943C6BFE46BF0BADC0E599193B9B280B3E4A8936415", + "2236B2EC26A12C3507BBB73000F45C66D99EAB36C71CCE074406B3DE2AAB5522", + "9C3EDBBC3C5A18E4700DE363D5E6B62872911EB287BC72F28C294BBD867A6792", + "F50EE5B0BF99D86609240B360F841748C207B167D8DD883119D212738690AF08", + "3C1200A655E50C8CED5E050131508AE11239B9A54964487AC1A4C37D00EB65D2", + "41D53788A52018C2E64631486C47F33E0BD18F175790D5A75CD7104AC4B940BF", + "B007D8BCCC5395A11AD09B2E082A47E89A9452F26C1448A7D2F9477EBF007A53", + "836BF05E781C00092A1182FE8CF481F51086BE939CA03BE98108FBB70F20C703", + "ACC725A403B511FA46DC93B469682F0720773F7795A7D502793BFF4C14EA367E", + "45014AED8A6657B0B6CA7C8C90EA27561F5B1200D296FEF2B8D72821E4B2C7CF", + "7EE683A29CF5D00B9BD9D0FA0D33220B1267F5F2B4AB068052EB3B437CF10403", + "DA941B01177D7B99719DB51139CC95C818A196B4F651F274951B323BFDDD04E4", + "766DDDF1D677B72F5D9DACD348C4E9CEC9155A4C7C1DC1200097D936010A521A", + "FFBADD2699D37CCB574C7B497A213C9E1BA6BB137BEEFC18149959A01EA1B20E", + "AF970E1C31EE2D50FC8A6116AEEAF7E076AA0F532532E7E00F2FB1C97766F2EA", + "4C621E3E6E7EA2D537C4BC16289C851F45385772054EB8E480FA8FF4F1C73B15", + "B1D4BCFEDCA38B86CA12D5253407DA51DB84F52E173F9F50CD4C33A029B1F626", + "5B67070DA3D5B5922AF42B8525F41B70B0D500CB11E0A9A65FF7979855A327A0", + "74EB669791E0AC9FC44DF8782E97276EB844936BB8BA7EF16D52E02BB4DB0340", + "C9F915B255DF51D876B1F3B50FBB73D5BD029DD6607D7AB223CAF21AD8D1BAD0", + "D0FDA0029952FE27437F1B2BCB376FF19D29E0C43BBB3FD38122EB4D193AB6E7", + "D96F7DA38C84044720A5D25CFABF3B83377A46B6F5EE32EB37EAB9628F4797E5", + "00E42FAC740E896D8696AC2C6C7C6CB6AC3C66DCACE88987B59B1CB7A0E4AA12", + "757BC03F5D14ED09D6CD4CB8CA51EB1BBE2250FB941465D037C733BC88E978C3", + "9B6355A007386E66E8868C50A6CB2C2FE73360E47B083BCAFD9A692B6BFEBA01", + "683DBFC2F170B7E740D423D9728969E1313E9C9BB24424A849C045206AAE3B54", + "57747904177BBE8EB93F869D1A0234E54EE9492C15CB796B66703C0F7BA49D87", + "BD0DF2C0CA450FF2CB0983602C0F31837F14D18DC238C0291BE45BAB61F9CC51", + "5869B6B0308C04A2EA597A51EDE1F919AD42C34744E32A12D46D8972EE7E44B6", + "3E5EE63B300CF7B5D433DCC4EDB982796A3B68C23EFB1DB8164C74A9A21B1AEF", + "0A56ABC0632AE52285B8B270C483C7C5EFC9C290A2F6F2B7E4D7486102E722CC", + "9664AE6C47D7FE35ECFF08919EF30B2876520B947935D00CAFBDEB2B8ED1A626", + "F8EBA8D7BD0F9767AF2FFC62F5A64831AF9858C6AF7D4220127F39B8508389B7", + "3BAF31CB0C3193D8F2B9DB44C2168DD2A0E2632A38F4A2317900C8E0199D5410", + "E3D8B355232E53F0F29ADB4A0D2BECAA0ABDB9C4F76C7B49D5133332C2C06D92", + "B690626D0D6B54FAB461C4A36736F8BDF95364DBC4D594735E226649EB05458A", + "D604B587E6C0F3C7DF04C44C99AF993AD21526AE686FC3365901DA8999C42CA4", + "CC833B6CDDF9F38779913AA285EE0104F3E283170CA0FC78F2653FC1D3666C10", + "AB0FE6B44818EFE913618BC30EF7E62D22829B10F0EC84F14B8DEBD5F2554E57", + "352057AB9D4436946106493CD78CDC9BA9F9C28AC08364305E41D4891A6818ED", + "5CB8851EBCF0A410D083BE68895CA36F7C334C28A84C2EF75CF365815DB0C5DA", + "29E3EDBB46B9719E07B484622F17B7158503E842F3EB7700BD2EA056F77C2308", + "BCDEB868158A277F40C954E9E22D2D206265BBCC4FCBC900A5312247DCA03612", + "8D0A8F6725393E747E5D63792F6528D17DEFB88A250528B6E09C3961809EC40A", + "BF6E35F6ABB9F0FC1E5BBACA8E94CB6A339BB039D724A4FBF5BADCF6E0A49853", + "B33797E9EF0D5AD3741AAAA4070731259DD46F3510B2BAA53CA03EF0C7EC2D79", + "3A8EA13651B67041E43954FD0D2BCFBE54B2D4BF5F2436A58E747AB9C5977EE0", + "029CE8F49B3A8881D3A80A996DBE4CB6DE93C8AA6C495C6702981E16D4A483E4", + "C74BB06CC28ABEB79EDED2A5343D52FF75E1653E98185C7398277E9B64861DAF", + "FFE0B50B26ACD98C9183D0CD16FE210F7E9A77F7313F630A9F372924DBCA02CF", + "84210BA9CB5E648F4C8F21D9CC2CBE72D683DFC30735D578604D2EED6338C258", + "BF565F30320F5CE89215D1BBD028DFD315981F7420672F84D0CF35A6D02CA4D2", + "EE3931DC193ADD06318BC435076C056004CF40390A2F762D5E203AE991B49F0A", + "DAD787389406BC027778E6B66D8B8ADF6E68045675AA3D0BCB859422280AEAE5", + "BB17C736E651C2DE1C59662CC592305814B5D25C69D501D13E07F0BF82526FE2", + "681664E8F719528ACCD0785BDA00CE60001066F8E7EAF25B74C3E406E6E88E2C", + "363BEB0B3FE8796719E0D0F35AF6B1382977A60BB5275642CC09CE9B54B3D4FB", + "6B90F34E96C9EA29C7C19667A41475FFA614846434D56A4D224E5102EFE617DE", + "0A3B8524535CACAE5B2C4B05DCBA6AE54802C4B6B20977608014426CA0E4EC14", + "AA3FDFA1D9B1FFC9B2CC9CDCFFA798B6A46CCCA472B6BB700E8C4F4150208BDA", + "D37478A894C201DEA05937B7B508AE8B3C32A02CB916439DBCE58CA63DABB611", + "C544CA7BC3FABFCA577DDD94C15A9F9A82DFE9959B5D19301D28E08546249B94", + "8EA79E7324A5E4B8B02304C9208609C2A36D3F70EF8115743167E7EA1039C473", + "3D7D907BB2018976D740C4383C938DCFACFC397F21653C8D73A57FB7573C7FAB", + "2420437C6E283036F6AC8B729F238CBCC61FEC0EB5CB95EF8741BCBD27236F7B", + "ABE8DD45041E6CE9941A9F5C4D826C9A7FA04E6122418C47D4306E4C8883052C", + "44AF37B03CAE2C0CCDA926CE080A86FDDD7BBFA79934FF895B0AEEF492000C7E", + "DAE27EE7A656B511EACB20E92D69E6F1D307FC9314F87DE9282E791413F81879", + "1AABA4FF5129DB2C93EE68F0AF326FABCB85AA07AADB6793BBEA4F59D354E862", + "E3823E89F56FDF1496C4D2D310130DE1DE3F9959F9C30D27C16E59FE80F0BBC2", + "FF93D4D2FC97084ACDEE0289EA8915C5A27492594447875D8A4FD54E7AA10AC1", + "AB16A156ED2871C2B89FB847E907F5361C89D503254D8D3D9497E8D446BB4039", + "82202D0F004CF5B8686AB36F47F27FA6F4866D4B90DB81D8E3BBD239AC8800C2", + "62718EAD1DCF7A8A193C6C9EC936EBE963E1D24EEE35BF0F061E7BCF1D35DA97", + "818F83781C351B3CCA2D06D47798102486F33D335C0218D6FF2BA1312C969EA4", + "106794773A59BC5596A3595BBC1693AB10BE7AED59096E9288F658CB7C8877D0", + "0E08E0E28ED9F53A0711384A13967FAA980AEE5C6812EC6D3B78AFE8B385BB34", + "3F5D97056B31D3A07B02BF02FACBFC8DDE93EFD28BBAFA00DB0EBF0F98EBA436", + "D633C7EC029ACCAD3C10BA420CE53BEA14245DC2A30A79BE43A305EB603E7805", + "06208009FDCE2B64F4831436EFAF7F5A98B20D5676695C84B108A4B67872A70C", + "38916704F7644B384A60794142A8C6CE6159B72A7137BF32EBA473C05E0DFD60", + "2D4FD632B4E6E14EE35B90C961E87FD85A0D79FB729D33378C0DFE46C772C7C2", + "41540A573C18FDEF97B591947988C9D6C9C1EAABB21144860D171A235F4A2C9F", + "311D7BC3CAB11E9419E2EF09B145367D077BC65C20AA4B0728DE4D720CDBFA21", + "27E545F29FFC264332865B962F6CEFEC9AC1EC453FE7C8541715E078EDC66774", + "F9987E4FF2104405763F827F7A04C02684033EF0881A55218FDCD75749A7E5E4", + "A6214893E710016BC81C46D6A93911F0C68FA129C593DA258AEC0292B3FDBF94", + "B220D8A682F7476A00ACA23FEA85D627C05B56260AE0CBBA664AB4CD582AF997", + "F77A0EFD9E7EE75FDFD6C262BB1366FF411E7957317696F9FEA3FA77DEEBFA26", + "93D2C1D57BAA64CB43874EDEC4119D4436E0154B34DAF7166A2ACA9B4F5EA62E", + "405FDC9C170BC06539BC5AFD5950598633A2270F3EBF7A583C84685CCD787E97", + "E1F9BB6567AA7AB0572AB0244B88A784967DA23BB624952A9EA0C7C388789CD5", + "DE174A6E0F0CC6001BC6FCC4A7B8A35B76709F373E236D3D100F777099A42263", + "CB49FEE9896E83955A9CC87EFE254C825F25F0631760A52F8864F5A286C87877", + "63ADA89CE5200E765E47D3C9C7BA606162D60DF71A3091894A27C850B05B33B9", + "F7CF8DCDAD6358E0B353C083CC3922C81B4DAF4A175574C1184F530ED6C3185C", + "9524C4905778C2B780970F8FBDA80B6D1C79FF2B9989A6368245640D92AE1A56", + "7D56DFE71C719AF4F92720101BF381A6121F8C5944E36495CFA22998367D8E16", + "8EABA03CB34535E807584491F439E5E622BC54729DFD0073F9AFB55641CAAC1D", + "E5F0AAF0568F1319BED1A6E6234A6B6BEFBC5BCB6F39C07A8B794F84CF3F1873", + "2593E9D9F33326FE921325B30D39F26EFD93739D4D3D46B29FA77C21FFD415C5", + "2B595D8B7FD494D2EED53D929006910031144ED21F25ADDEC1F83D2CAD96445B", + "6C4100EB1A3F77DE533167E459BE7240987F26BE436C05D7501AE3B47AF91AA8", + "0AB0905CED76219D208D262183168A84758ACCAC0173312BDFEECFEAB63748FF", + "8B922D41A15207167D290656E1FE29EE1689F6D24AD0D3935F81902106C78EF5", + "CC5FE2AFEEB9C20062970886FD6B6F92E309240AC25A8794E13D1FE433ECF814", + "00230C6241689491572F2BE8BD1ADA201C755AA09FBD58B3F01C2EA9F6C38FBC", + "30B14D0B82D30352B920416D821BE00C391A807013F678C07866B595A9AE738A", + "8D92FB2055B39D6FF7682B02B592A670E0A0D908AC27270137D3DDBD0FF8AC0F", + "4FFA9DB1383819C6CE28196ED5F3EDBE7A1FA94445E00C672AAC61BD41336794", + "C6C0190BEBD34311C076BD9686BA0B1D7A043184FC37F379563ABE97A1CB7ED1", + "E0E1101D1D35DFFD64054C91CD82925FA434C8BCBE05600DFC73AFE56518FE04", + "9B578D75BCF9C41E6F5C87BE54F76A1AE329C129D1D4FD219150B76BC704C4BD", + "A02A86CB74ED364942FD0F3599F953BD646FA6025D5DC090C8900CADC94983F5", + "CE315FD3F9DA2FBC43455441E5B9F43B2A852FF539818BC4D1004677EFF67EB2", + "6942E57B59A1C816DD7B023974A820CF39E5111E323B64714B71E513F32D4ADF", + "C4020F18B45515E28DC63AEE2C3E803F3CDE5BB52DC5290D95D701A57BCC2E1A", + "E4D627BF109801536717DCA33D1B393736F34BD413067C576A78240A96ABC7F1", + "44501F1D58899F7E6C3A846436427DA09D0958A11EF83A5E9741752B9534B1EF", + "64034F640E653A0558B25C5B457271A496CFE0F624763DA87362626396F4BBDC", + "DDA1988F4B0022213D725ACF50E26ED43257EDAA080E8DBAE8A6DFC1683CEFCB", + "B1312083CAC0826CD72F17F122E8F4F53E9C4266CD2D0D10C94876CE38FDBE91", + "4590BDAE1C9E21661FC7A65FEAEEE951A73F695BDF8CA0E82FF811CD588D4769", + "495651FDD51FE5F200713A5F9AE81B5F7089066F14642FC492CBE6568AE81B32", + "1B8C44C9BDA8AACCD0E6F2B123749CEA414AA3D569135EF8FDDB60B23A43C3F2", + "A36AD1FBF7F0617DBBF4C10B01906A17BA83657B604945A57AF17E79FAA5ADBB", + "B2B14726E4F2C84130D098109E4D8723BAFC4A9F08384553EC904B5A7798C9C8", + "A1A71AF0E102EBD7DD93D412CE63E386D7221BEAB9770C22BBBC051EADE759A4", + "4C6622A0F8D2105573CD2A6494C9B983FEE0D3B02977639EE575BCD2F4EF3D41", + "93B79BAF0D3163D935066393FEF8C2EB5543B5DCFA8FE465F85E396982E19773", + "3FE4FB9171E4649CCA3D59A391548265046059BE3788B3FF39CBF92FA677048E", + "0DF5E7953DF0FEE769AC70228E9513CF6B1F4EBD86A1A64C6ADCCD0E66005871", + "FE43B8F38E62A4757616C70CEE02EB2FCF9F45E4E1A43EE4C8022ABDB97333B1", + "C891795E8244A5EA1D43139906E5CD6D9FEC177EB3D5CFD598B99599C0FCB843", + "6D81C724EDEA609E5C5572369DCC7FD1D0986C7B82B42CC6E2D1E80A3FE8F9F9", + "6F400AA10A6BFF484F5E07D6D697152E35E3CEB3FE0ED9886E1607B77B1048D8", + "F3BBE219C82DF2CC1800393EAD3D929299BC75BACDF70380C6C6067A33238D11", + "A28E4ED96F099C0CE07707FECB985D4828EC48795D365B78DFABEC653071C0BF", + "745538F694DC743AB6E9F0CF0C05833E57F6600D3DB3F247FC45C0224AD521A6", + "CFDE6DFA90BD8F64F8649ABD6D4EE608BC3F50B42C37CE5708EF1A815211623D", + "D2503BCFCD803762316DFEE6AD32D56CAAC1D96CB58677E893DE386D9B512AD3", + "E4EF842488E8CD8C7AB209282D213329D2BC80AD3C1D8676AE7BC1A85B4564A9", + "5568508C40782DDC028E42FEEB78F67E5C188BD35098CC58CCDD43FB4BA51AB4", + "7EE1AC2A77EB0862FC5E6CA4319F95AA960679B572A476F8282C38F1C2823D36", + "39C7CD3568BF877D40754C585A6A4A0CA1A57F3B8CB144A01CA1DE18E4489F85", + "F3265799D576F322295A6229A705D212BB25C82B639A47D037B0817B883475D0", + "C8EFBBA6764313900DCA6EEB64B195D25F089224B8DB461B306D461A6966856C", + "C290527E7C357B2E5A0F9178C6BE1B0B566FD513C814F27A664A62827DC63465", + "8A6CCC84998154349F6080B21AB1FF1BDAF9D9C7B24984BA5E6CD06BEDD7BA7A", + "ACD9E8F6BAE1913EAAD4B52F5FEFDB3F6C69317C36658749AF39120810F566C9", + "0927A201F9281C4D652EC4DD0676A179C1B731B59FE062D6BE1198AA1483E41C", + "38D24962B4DDB2711539404CBDD97A7393EAEC52A81E03FEB7D159D9290996BC", + "5BFE6AA81E2AF93E3A066F2D7818A40E51FEDF35CE848023BE3330D52484130D", + "7739C48A8EBAEFCF19F631FC06BB3C7FB5E7F00462BD6D58F5C1027B0BCDEB59", + "4FC1B97A8D4E414A0B982E3251A62419396A9D1FA8114A1A777BFD8A6CE15D9E", + "A895737B7B3EBEFAD289AA36873E7F11EBE74D4A014B1AF8EBDC159803B91106", + "3A0E7A191BDB5F7E7F0A55F69C60BBFC262521134E71198C7EDF1465F294FDB5", + "18A9C02DEBCF75B1A24EA924059630B6634467C5AD6CCBEDB0077EF9AFFFEE69", + "5C90FC5FD33F9DDED79B78540D5FF1D4C9B2A18D2AFD5D3E0A2E7FF5F00A0555", + "4B63D5CB16236D4307C1DA72CD51DCCD1F8D454256BE6361F3B848C4423F56AA", + "A6117797578DA5047D9C500F56FC1CD3BFCE0CB052F6EB6BC5D6BD932C29F2BB", + "50ABE7AB7DCF1ACA12EB06E38C2B3B2C54B753A05B81F3B5E18CDE1F164F3FFC", + "A49DD813109E8BDC408360E80BFD9CF945501BA3152FDAB719B2989946560145", + "910EC78E8775AC2925611B580E043098816E2D8391ECF6C7333D1D31FDEF1BEF", + "BB364250F2E73783579D3229A75B98B91582933D402914C2FFA2A95F8D358BC7", + "7E1F64DC0B37DAFF2738A915E049D5240499E46920B92564B532C67873CCFCFC", + "FE96806BDB98DDB651A4C9C80749C79215ED8ED26BEDD10E49AB91631CCAB5A6", + "3401E5B2E5D3A53EB0891088A5F2D9364BBB6CE5B37A337D2C0660DAF9C4175E" + ], + "LedgerEntryType": "LedgerHashes", + "Flags": 0, + "FirstLedgerSequence": 2, + "LastLedgerSequence": 38128 + } + }, + { + "binary": "1100612200000000240000000125000000472D000000005583124A90968261C4EC33F29A5F1D2B2941AC7A52D74639C1F8B94E36C13FA3F96240000002540BE40081141A5CD521A26A45FF77EA1343E25E71740344BB66", + "json": { + "OwnerCount": 0, + "Account": "rsQP8f9fLtd58hwjEArJz2evtrKULnCNif", + "PreviousTxnLgrSeq": 71, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "83124A90968261C4EC33F29A5F1D2B2941AC7A52D74639C1F8B94E36C13FA3F9", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000000592D0000000055BFB2829F8C2549B420539853A3A03A6F29803D6E885465F11E6BBED711DD013A6240000002540BE4008114E3AC9945731DF31ED7AECBEC6406DC30CCE36A36", + "json": { + "OwnerCount": 0, + "Account": "rMkq9vs7zfJyQSPPkS2JgD8hXpDR5djrTA", + "PreviousTxnLgrSeq": 89, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "BFB2829F8C2549B420539853A3A03A6F29803D6E885465F11E6BBED711DD013A", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000000ED2D00000000551EE8602B929B4690E2F3D318AFA25F5248607F82831930EB3280690F66F981476240002D79883D20008114721E29387C4B08E3DFD66E17DAA4CA12A5D63169", + "json": { + "OwnerCount": 0, + "Account": "rBQQwVbHrkf8TEcW4h4MtE6EUyPQedmtof", + "PreviousTxnLgrSeq": 237, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "1EE8602B929B4690E2F3D318AFA25F5248607F82831930EB3280690F66F98147", + "Flags": 0, + "Sequence": 1, + "Balance": "50000000000000" + } + }, + { + "binary": "1100612200000000240000000125000037E62D0000000055F00540C7D38779DEEE08CA90584D3A3D5A43E3ADAA97902B1541DD84D31D3CA76240000002540BE4008114EF991ABBD1A8C4165EA623911A940920CA6BBB91", + "json": { + "OwnerCount": 0, + "Account": "r4q1ujKY4hwBpgFNFx43629f2LuViU4LfA", + "PreviousTxnLgrSeq": 14310, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "F00540C7D38779DEEE08CA90584D3A3D5A43E3ADAA97902B1541DD84D31D3CA7", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006122000000002400000001250000005A2D00000000554D4C38E4A5BD7D7864F7C28CAD712D6CD1E85804C7645ABF6F4C5B2FE54720356240000002540BE40081142332A8767CAD430E9EF644CC2177BD04545C0C60", + "json": { + "OwnerCount": 0, + "Account": "rhDfLV1hUCanViHnjJaq3gF1R2mo6PDCSC", + "PreviousTxnLgrSeq": 90, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "4D4C38E4A5BD7D7864F7C28CAD712D6CD1E85804C7645ABF6F4C5B2FE5472035", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000020DA2D000000005577A1280E1103759D7C77C6B4F4759AF005AFC58F84988C1893A74D47E3E0568A624000B5E620F4800081140B417F9DCA89CB7F5BA50C7BE64359BAB19588BD", + "json": { + "OwnerCount": 0, + "Account": "rppWupV826yJUFd2zcpRGSjQHnAHXqe7Ny", + "PreviousTxnLgrSeq": 8410, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "77A1280E1103759D7C77C6B4F4759AF005AFC58F84988C1893A74D47E3E0568A", + "Flags": 0, + "Sequence": 1, + "Balance": "200000000000000" + } + }, + { + "binary": "1100612200000000240000000125000068562D0000000055663F8A73611629DCE63205AEA8C698770607CE929E1D015407D8D352E9DCEF8E62400009184E72A0008114E7D17BE3C17D850BDE314C04C20036A1E7DB2AA0", + "json": { + "OwnerCount": 0, + "Account": "r43ksW5oFnW7FMjQXDqpYGJfUwmLan9dGo", + "PreviousTxnLgrSeq": 26710, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "663F8A73611629DCE63205AEA8C698770607CE929E1D015407D8D352E9DCEF8E", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "110072220001000025000022C85545CA59F7752331A307FF9BCF016C3243267D8506D0D0FA51965D322F7D59DF366280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D498DE76816D800000000000000000000000000055534400000000002ECC94F5447A3CC1348E94C84D4677442C9E42276780000000000000000000000000000000000000005553440000000000712B799C79D1EEE3094B59EF9920C7FEB3CE4499", + "json": { + "PreviousTxnLgrSeq": 8904, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "7", + "issuer": "rnGTwRTacmqZZBwPB6rh3H1W4GoTZCQtNA" + }, + "PreviousTxnID": "45CA59F7752331A307FF9BCF016C3243267D8506D0D0FA51965D322F7D59DF36", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rBKPS4oLSaV2KVVuHH8EpQqMGgGefGFQs7" + } + } + }, + { + "binary": "110061220000000024000000012500005A372D00000000557FA58DF8A1A2D639D65AA889553EE19C44C770B33859E53BD7CE18272A81C06B62400000000BEBC20081148E4F82DCBBADCD6946C3AAA1DF6578E3FE8DB322", + "json": { + "OwnerCount": 0, + "Account": "rDy7Um1PmjPgkyhJzUWo1G8pzcDan9drox", + "PreviousTxnLgrSeq": 23095, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "7FA58DF8A1A2D639D65AA889553EE19C44C770B33859E53BD7CE18272A81C06B", + "Flags": 0, + "Sequence": 1, + "Balance": "200000000" + } + }, + { + "binary": "110072220001000025000007EA55A39F6B89F50033153C9CC1233BB175BE52685A31AE038A58BEC1A88898E834206280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D551C37937E0800000000000000000000000000055534400000000000B8D970A5E6BB9C50EC063E9318C7218D65ECE4367800000000000000000000000000000000000000055534400000000001A5CD521A26A45FF77EA1343E25E71740344BB66", + "json": { + "PreviousTxnLgrSeq": 2026, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "5000", + "issuer": "rphasxS8Q5p5TLTpScQCBhh5HfJfPbM2M8" + }, + "PreviousTxnID": "A39F6B89F50033153C9CC1233BB175BE52685A31AE038A58BEC1A88898E83420", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rsQP8f9fLtd58hwjEArJz2evtrKULnCNif" + } + } + }, + { + "binary": "110061220000000024000000022500003E9D2D0000000155865A20F744FBB8673C684D6A310C1B2D59070FCB9979223A4E54018C224669466240000002540BE3F6811480799D02FF2097DEB21F66472FCF477C36E7039F", + "json": { + "OwnerCount": 1, + "Account": "rU5KBPzSyPycRVW1HdgCKjYpU6W9PKQdE8", + "PreviousTxnLgrSeq": 16029, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "865A20F744FBB8673C684D6A310C1B2D59070FCB9979223A4E54018C22466946", + "Flags": 0, + "Sequence": 2, + "Balance": "9999999990" + } + }, + { + "binary": "11006122000000002400000001250000103C2D000000005599711CE5DC63B01502BB642B58450B8F60EA544DEE30B2FE4F87282E13DD1360624000000265ED87008114FCD4E320A9A95903D3C787A6892B809C7F00BF02", + "json": { + "OwnerCount": 0, + "Account": "rQsiKrEtzTFZkQjF9MrxzsXHCANZJSd1je", + "PreviousTxnLgrSeq": 4156, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "99711CE5DC63B01502BB642B58450B8F60EA544DEE30B2FE4F87282E13DD1360", + "Flags": 0, + "Sequence": 1, + "Balance": "10300000000" + } + }, + { + "binary": "110061220000000024000000012500000EA72D0000000055D99B7B1D66C09166319920116CAAE2B7209FB1C44C352EAA18862EA0D49D68D3624000246139CA800081141A7B9B708797D20498853B2BB3C7D613A6312DF4", + "json": { + "OwnerCount": 0, + "Account": "rsRpe4UHx6HB32kJJ3FjB6Q1wUdY2wi3xi", + "PreviousTxnLgrSeq": 3751, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D99B7B1D66C09166319920116CAAE2B7209FB1C44C352EAA18862EA0D49D68D3", + "Flags": 0, + "Sequence": 1, + "Balance": "40000000000000" + } + }, + { + "binary": "110072220001000025000045223700000000000000003800000000000000005519CDDD9E0DE5F269E1EAFC09E0C2D3E54BEDD7C67F890D020E883B69A653A4BA6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D50AA87BEE53800000000000000000000000000055534400000000002B6C42A95B3F7EE1971E4A10098E8F1B5F66AA08678000000000000000000000000000000000000000555344000000000087057DF0267E7A0ED8E1197ADC0EF8C4471A90A8", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 17698, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "300", + "issuer": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy" + }, + "PreviousTxnID": "19CDDD9E0DE5F269E1EAFC09E0C2D3E54BEDD7C67F890D020E883B69A653A4BA", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rDJvoVn8PyhwvHAWuTdtqkH4fuMLoWsZKG" + } + } + }, + { + "binary": "11006122000000002400000001250000005D2D00000000553FAF5E34874473A4D9707124DA8A4D6AF6820EBA718F588A07B9C021CC5CAF016240000002540BE400811465168339D44E757DE639F93C5B96960A49CFBBC8", + "json": { + "OwnerCount": 0, + "Account": "rwDWD2WoU7npQKKeYd6tyiLkmr7DuyRgsz", + "PreviousTxnLgrSeq": 93, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "3FAF5E34874473A4D9707124DA8A4D6AF6820EBA718F588A07B9C021CC5CAF01", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110061220000000024000000012500000EA12D000000005530DF2860F25D582699D4C802C7650A65DC54A07FA0D60ACCEB9A7A66B4454D5A624000886C98B7600081148ADF9C14E258B4AF221A7900CF6136EE070D4AC0", + "json": { + "OwnerCount": 0, + "Account": "rDCJ39V8yW39Ar3Pod7umxnrp24jATE1rt", + "PreviousTxnLgrSeq": 3745, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "30DF2860F25D582699D4C802C7650A65DC54A07FA0D60ACCEB9A7A66B4454D5A", + "Flags": 0, + "Sequence": 1, + "Balance": "150000000000000" + } + }, + { + "binary": "110061220000000024000000012500000E9D2D0000000055BEF9DFDEA6B289FF343E83734A125D87C3B60E6007D1C3A499DA47CE1F909FFD624000886C98B76000811446ECDEAC5FC86A4A4F11766567CCF769B3C7A848", + "json": { + "OwnerCount": 0, + "Account": "rf7phSp1ABzXhBvEwgSA7nRzWv2F7K5VM7", + "PreviousTxnLgrSeq": 3741, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "BEF9DFDEA6B289FF343E83734A125D87C3B60E6007D1C3A499DA47CE1F909FFD", + "Flags": 0, + "Sequence": 1, + "Balance": "150000000000000" + } + }, + { + "binary": "11006122000000002400000001250000685F2D00000000553F8E7146B8BF4A208C01135EF1688E38FE4D2EB72DCEC472330B278660F5C25162400009184E72A00081146B788C7732603A7EF853921BBB18FF93F3F5B8B1", + "json": { + "OwnerCount": 0, + "Account": "rwoE5PxARitChLgu6VrMxWBHN7j11Jt18x", + "PreviousTxnLgrSeq": 26719, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "3F8E7146B8BF4A208C01135EF1688E38FE4D2EB72DCEC472330B278660F5C251", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "11007222000100002500004EE0370000000000000000380000000000000000554E4AAF8C25F0A436FECEA7D1CB8C36FCE33F1117B81B712B9CF30B400C226C3F6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5438D7EA4C6800000000000000000000000000055534400000000007588B8DBDC8932DC410E8571045466C03F5A6B696780000000000000000000000000000000000000005553440000000000B5F762798A53D543A014CAF8B297CFF8F2F937E8", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 20192, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "1000", + "issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY" + }, + "PreviousTxnID": "4E4AAF8C25F0A436FECEA7D1CB8C36FCE33F1117B81B712B9CF30B400C226C3F", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" + } + } + }, + { + "binary": "110064220000000058C6F93F7D81C5B659EA2BF067FA390CDE1A5D5084486FA0B1A7EAEF77937D54D882144A77EF82417FCC4B8D801A9D64C9070C0824E5E70113405CCE7ABDC737694A71B9B1BBD15D9408E8DC4439C9510D2BC2538D59F99B751544A3BC5DABBA84B9E1D64A61350F2FBB26EC70D1393B699CA2BB2CA1A0679A01", + "json": { + "Owner": "rf8kg7r5Fc8cCszGdD2jeUZt2FrgQd76BS", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "C6F93F7D81C5B659EA2BF067FA390CDE1A5D5084486FA0B1A7EAEF77937D54D8", + "Indexes": [ + "5CCE7ABDC737694A71B9B1BBD15D9408E8DC4439C9510D2BC2538D59F99B7515", + "44A3BC5DABBA84B9E1D64A61350F2FBB26EC70D1393B699CA2BB2CA1A0679A01" + ] + } + }, + { + "binary": "11006122000000002400000001250000005F2D0000000055189228C5636A8B16F1A811F0533953E71F2B8B1009D343888B72A23A83FAFB3F624000000011E1A30081143676E3F66EFC8DDE76646A0B689BCDBCD12B89AF", + "json": { + "OwnerCount": 0, + "Account": "rnxyvrF2mUhK6HubgPxUfWExERAwZXMhVL", + "PreviousTxnLgrSeq": 95, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "189228C5636A8B16F1A811F0533953E71F2B8B1009D343888B72A23A83FAFB3F", + "Flags": 0, + "Sequence": 1, + "Balance": "300000000" + } + }, + { + "binary": "1100612200000000240000000125000001062D00000000555014D1C3606B264324998AB36403FFD4A70F1E942F7586EA217DBE9336F962DA6240002D79883D20008114C93D6E551A8127AA283100B232C70E6C113F4AB2", + "json": { + "OwnerCount": 0, + "Account": "rKMhQik9qdyq8TDCYT92xPPRnFtuq8wvQK", + "PreviousTxnLgrSeq": 262, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "5014D1C3606B264324998AB36403FFD4A70F1E942F7586EA217DBE9336F962DA", + "Flags": 0, + "Sequence": 1, + "Balance": "50000000000000" + } + }, + { + "binary": "11006122000000002400000009250000104E2D0000000855D890893A91DC745BE221820C17EC3E8AF4CC119A93AA8AB8FD42C16D264521FA6240000001EBBD0230811436D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC", + "json": { + "OwnerCount": 8, + "Account": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm", + "PreviousTxnLgrSeq": 4174, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D890893A91DC745BE221820C17EC3E8AF4CC119A93AA8AB8FD42C16D264521FA", + "Flags": 0, + "Sequence": 9, + "Balance": "8249999920" + } + }, + { + "binary": "11007222000200002500006859370000000000000000380000000000000000557957DD6BE9323DED70BAC7C43761E0EAAC4C6F5BA7DFC59CFE95100CA9B0B0D462800000000000000000000000000000000000000055534400000000000000000000000000000000000000000000000001668000000000000000000000000000000000000000555344000000000073B3ED1EBFF63BECF6E84D03B6A0CAF1BE8A605067D5438D7EA4C6800000000000000000000000000055534400000000007588B8DBDC8932DC410E8571045466C03F5A6B69", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 26713, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rBY8EZDiCNMjjhrC7SCfaGr2PzGWtSntNy" + }, + "PreviousTxnID": "7957DD6BE9323DED70BAC7C43761E0EAAC4C6F5BA7DFC59CFE95100CA9B0B0D4", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "1000", + "issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY" + } + } + }, + { + "binary": "110064220000000031000000000000000232000000000000000158433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840821436D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC011340CEA57059DECE8D5C6FC9FDB9ACE44278EC74A075CE8A5A7522B2F85F669245FF10BB331A6A794396B33DF7B975A57A3842AB68F3BC6C3B02928BA5399AAC9C8F", + "json": { + "Owner": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm", + "IndexNext": "0000000000000002", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000001", + "Flags": 0, + "RootIndex": "433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840", + "Indexes": [ + "CEA57059DECE8D5C6FC9FDB9ACE44278EC74A075CE8A5A7522B2F85F669245FF", + "10BB331A6A794396B33DF7B975A57A3842AB68F3BC6C3B02928BA5399AAC9C8F" + ] + } + }, + { + "binary": "1100722200030000250000455F370000000000000000380000000000000000553B76C257B746C298DCD945AD900B05A17BA44C74E298A1B70C75A89AD588D0066280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4838D7EA4C68000000000000000000000000000555344000000000050FCCA71E98DFA43305149F9F0C7897DE5A9D18C67D4838D7EA4C6800000000000000000000000000055534400000000008D3A0AEF277858BD4D9751ECECD16779C0CC86D0", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 17759, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "1", + "issuer": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH" + }, + "PreviousTxnID": "3B76C257B746C298DCD945AD900B05A17BA44C74E298A1B70C75A89AD588D006", + "Flags": 196608, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "1", + "issuer": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x" + } + } + }, + { + "binary": "110061220000000024000000012500000EB12D0000000055D156CB5A5736E5B4383DEF61D4E4F934442077A4B4E291903A4B082E7E48FD4262400009184E72A0008114DDE7AF8EB7CFABA51FCBDE40601AC0973C42D0DE", + "json": { + "OwnerCount": 0, + "Account": "rMNKtUq5Z5TB5C4MJnwzUZ3YP7qmMGog3y", + "PreviousTxnLgrSeq": 3761, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "D156CB5A5736E5B4383DEF61D4E4F934442077A4B4E291903A4B082E7E48FD42", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "11007222000300002500000B89553319CF0238A16958E2F5B26CFB90B05A2B16A290B933F105F66929DA16A09E6E6280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D4838D7EA4C68000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC67D4838D7EA4C680000000000000000000000000004254430000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 2953, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "1", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "3319CF0238A16958E2F5B26CFB90B05A2B16A290B933F105F66929DA16A09E6E", + "Flags": 196608, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "1", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110072220002000025000000EA55DEEB01071843D07939A19BD97128604F2B788C744D01AF82D631DF5023F4C7C0628000000000000000000000000000000000000000425443000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004254430000000000C2659C14642A6604CE305966307E5F21817A092D67D4838D7EA4C680000000000000000000000000004254430000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 234, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rJ51FBSh6hXSUkFdMxwmtcorjx9izrC1yj" + }, + "PreviousTxnID": "DEEB01071843D07939A19BD97128604F2B788C744D01AF82D631DF5023F4C7C0", + "Flags": 131072, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "1", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110064220000000031000000000000000132000000000000000558D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C38214E14829DB4C6419A8EFCAC1EC21D891A1A4339871011340AC2875C846CBD37CAF8409A623F3AA7D62916A0E043E02C909C9EF3A7B06F8CF142355A88F0729A5014DB835C24DA05F062293A439151A0BE9ACB80F20B2CDC5", + "json": { + "Owner": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui", + "IndexNext": "0000000000000001", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000005", + "Flags": 0, + "RootIndex": "D0CAC45692858D395B16D52A0B44ADCB7EF178617C05BAE3C36FF5574BA012C3", + "Indexes": [ + "AC2875C846CBD37CAF8409A623F3AA7D62916A0E043E02C909C9EF3A7B06F8CF", + "142355A88F0729A5014DB835C24DA05F062293A439151A0BE9ACB80F20B2CDC5" + ] + } + }, + { + "binary": "11006F2200000000240000000625000027643300000000000000003400000000000000015558BF1AC5F8ADDB088913149E0440EF41430F2E62A0BE200674F6F5F28979F1F85010F774E0321809251174AC85531606FB46B75EEF9F842F9697531AA535D3D0C00064D49AA535D3D0C0000000000000000000000000004254430000000000E8ACFC6B5EF4EA0601241525375162F43C2FF28565D5038D7EA4C68000000000000000000000000000555344000000000058C742CF55C456DE367686CB9CED83750BD24979811458C742CF55C456DE367686CB9CED83750BD24979", + "json": { + "TakerPays": { + "currency": "BTC", + "value": "7.5", + "issuer": "r4DGz8SxHXLaqsA9M2oocXsrty6BMSQvw3" + }, + "Account": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx", + "PreviousTxnLgrSeq": 10084, + "BookDirectory": "F774E0321809251174AC85531606FB46B75EEF9F842F9697531AA535D3D0C000", + "LedgerEntryType": "Offer", + "OwnerNode": "0000000000000001", + "PreviousTxnID": "58BF1AC5F8ADDB088913149E0440EF41430F2E62A0BE200674F6F5F28979F1F8", + "TakerGets": { + "currency": "USD", + "value": "100", + "issuer": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx" + }, + "Flags": 0, + "Sequence": 6, + "BookNode": "0000000000000000" + } + }, + { + "binary": "110061220000000024000000012500000B972D00000000557C63981F982844B6ABB31F0FE858CBE7528CA47BC76658855FE44A4ECEECEDD4624000000277CF2A008114A0B2F54B75C3D1EAC4FFC68EC03CCBB772CC1325", + "json": { + "OwnerCount": 0, + "Account": "rEe6VvCzzKU1ib9waLknXvEXywVjjUWFDN", + "PreviousTxnLgrSeq": 2967, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "7C63981F982844B6ABB31F0FE858CBE7528CA47BC76658855FE44A4ECEECEDD4", + "Flags": 0, + "Sequence": 1, + "Balance": "10600000000" + } + }, + { + "binary": "1100722200010000250000010C559C88635839AF1B4142FC8A03E733DCB62ADAE7D2407F13365A05B94A5A153D596280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4C38D7EA4C680000000000000000000000000005553440000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA6780000000000000000000000000000000000000005553440000000000B544029B077F39117BD0C9FB2913FCE08F9345CF", + "json": { + "PreviousTxnLgrSeq": 268, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "10", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + }, + "PreviousTxnID": "9C88635839AF1B4142FC8A03E733DCB62ADAE7D2407F13365A05B94A5A153D59", + "Flags": 65536, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "0", + "issuer": "rHXS898sKZX6RY3WYPo5hW6UGnpBCnDzfr" + } + } + }, + { + "binary": "110064220000000031000000000000000432000000000000000358433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840821436D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC01134085469362B15032D6213572E63175C87C321601E1DDBB588C9CBD08CDB3F276AC2F1F54C50845EBD434A06639160F77CEB7C99C0606A3624F64C3678A9129F08D", + "json": { + "Owner": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm", + "IndexNext": "0000000000000004", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000003", + "Flags": 0, + "RootIndex": "433FE9D880C1A0D1901BAE63BB255312119826D6ADF8571F04736C409A77B840", + "Indexes": [ + "85469362B15032D6213572E63175C87C321601E1DDBB588C9CBD08CDB3F276AC", + "2F1F54C50845EBD434A06639160F77CEB7C99C0606A3624F64C3678A9129F08D" + ] + } + }, + { + "binary": "110064220000000058D4A00D9B3452C7F93C5F0531FA8FFB4599FEEC405CA803FBEFE0FA22137D863D821487057DF0267E7A0ED8E1197ADC0EF8C4471A90A8011320C1C5FB39D6C15C581D822DBAF725EF7EDE40BEC9F93C52398CF5CE9F64154D6C", + "json": { + "Owner": "rDJvoVn8PyhwvHAWuTdtqkH4fuMLoWsZKG", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "D4A00D9B3452C7F93C5F0531FA8FFB4599FEEC405CA803FBEFE0FA22137D863D", + "Indexes": ["C1C5FB39D6C15C581D822DBAF725EF7EDE40BEC9F93C52398CF5CE9F64154D6C"] + } + }, + { + "binary": "110064220000000058D4B68B54869E428428078E1045B8BB66C24DD101DB3FCCBB099929B3B63BCB408214D99223BCD7B2E92968DC60BC9C63D1D808191FB30113209A551971E78FE2FB80D930A77EA0BAC2139A49D6BEB98406427C79F52A347A09", + "json": { + "Owner": "rLqQ62u51KR3TFcewbEbJTQbCuTqsg82EY", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "D4B68B54869E428428078E1045B8BB66C24DD101DB3FCCBB099929B3B63BCB40", + "Indexes": ["9A551971E78FE2FB80D930A77EA0BAC2139A49D6BEB98406427C79F52A347A09"] + } + }, + { + "binary": "1100612200000000240000000225000027512D0000000155AF65070E6664E619813067C49BC64CBDB9A7543042DA7741DA5446859BDD782C62400000025A01C4F68114F7FF2D5EA6BB5C26D85343656BEEE94D74B509E0", + "json": { + "OwnerCount": 1, + "Account": "rPcHbQ26o4Xrwb2bu5gLc3gWUsS52yx1pG", + "PreviousTxnLgrSeq": 10065, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "AF65070E6664E619813067C49BC64CBDB9A7543042DA7741DA5446859BDD782C", + "Flags": 0, + "Sequence": 2, + "Balance": "10099999990" + } + }, + { + "binary": "110064220000000058D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA6352048214B5F762798A53D543A014CAF8B297CFF8F2F937E80113408A2B79E75D1012CB89DBF27A0CE4750B398C353D679F5C1E22F8FAC6F87AE13CC683B5BB928F025F1E860D9D69D6C554C2202DE0D45877ADB3077DA4CB9E125C", + "json": { + "Owner": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "D8120FC732737A2CF2E9968FDF3797A43B457F2A81AA06D2653171A1EA635204", + "Indexes": [ + "8A2B79E75D1012CB89DBF27A0CE4750B398C353D679F5C1E22F8FAC6F87AE13C", + "C683B5BB928F025F1E860D9D69D6C554C2202DE0D45877ADB3077DA4CB9E125C" + ] + } + }, + { + "binary": "1100612200000000240000000125000000652D0000000055972A6B3107761A267F54B48A337B5145BC59A565E9E36E970525877CB053678C6240000002540BE40081146DB2F1BE3E0DE1750C4F4ECD9CC2F6099344CC07", + "json": { + "OwnerCount": 0, + "Account": "rBrspBLnwBRXEeszToxcDUHs4GbWtGrhdE", + "PreviousTxnLgrSeq": 101, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "972A6B3107761A267F54B48A337B5145BC59A565E9E36E970525877CB053678C", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "11006122000000002400000001250000376E2D0000000055458F1F8CC965A0677BC7B0761AFE57D47845B501B9E5293C49736A100E2E92926240000002540BE4008114D786692D656E042B3FD6459C3ED9EF54347E7A46", + "json": { + "OwnerCount": 0, + "Account": "rLebJGqYffmcTbFwBzWJRiv5fo2ccmmvsB", + "PreviousTxnLgrSeq": 14190, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "458F1F8CC965A0677BC7B0761AFE57D47845B501B9E5293C49736A100E2E9292", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100612200000000240000000125000000662D00000000552DA2B33FFAE8CDCC011C86E52904A665256AD3F9D0A1D85A6637C461925E3FB06240000002540BE4008114F6FE4B8E0AE3139FF83919611A3562BF3BF50F4E", + "json": { + "OwnerCount": 0, + "Account": "rPWyiv5PXyKWitakbaKne4cnCQppRvDc5B", + "PreviousTxnLgrSeq": 102, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "2DA2B33FFAE8CDCC011C86E52904A665256AD3F9D0A1D85A6637C461925E3FB0", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110064220000000058DD23E2C60C9BC58180AC6EA7C668233EC51A0947E42FD1FAD4F5FBAED9698D958214D9CEEA2E2AD331A8D087C216284D58EBBC6780E8011320908D554AA0D29F660716A3EE65C61DD886B744DDF60DE70E6B16EADB770635DB", + "json": { + "Owner": "rLiCWKQNUs8CQ81m2rBoFjshuVJviSRoaJ", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "DD23E2C60C9BC58180AC6EA7C668233EC51A0947E42FD1FAD4F5FBAED9698D95", + "Indexes": ["908D554AA0D29F660716A3EE65C61DD886B744DDF60DE70E6B16EADB770635DB"] + } + }, + { + "binary": "1100612200000000240000000125000000572D00000000558D247FF7A5195A888A36C0CA56DD2B70C2AB5BBD04F3045CD3B9CAF34BBF81436240038D7EA4C680008114838627B98B8D25DF362F111B570A5CC5EA2F3CA9", + "json": { + "OwnerCount": 0, + "Account": "rUzSNPtxrmeSTpnjsvaTuQvF2SQFPFSvLn", + "PreviousTxnLgrSeq": 87, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "8D247FF7A5195A888A36C0CA56DD2B70C2AB5BBD04F3045CD3B9CAF34BBF8143", + "Flags": 0, + "Sequence": 1, + "Balance": "1000000000000000" + } + }, + { + "binary": "1100612200000000240000000925000045922D0000000155821706FDED3BED3D146CED9896683E2D4131B0D1238F44ED53C5C40E07DD659A624000000255A7E070811450FCCA71E98DFA43305149F9F0C7897DE5A9D18C", + "json": { + "OwnerCount": 1, + "Account": "r3PDtZSa5LiYp1Ysn1vMuMzB59RzV3W9QH", + "PreviousTxnLgrSeq": 17810, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "821706FDED3BED3D146CED9896683E2D4131B0D1238F44ED53C5C40E07DD659A", + "Flags": 0, + "Sequence": 9, + "Balance": "10026999920" + } + }, + { + "binary": "1100612200000000240000000125000000682D000000005581066DCA6C77C2C8A2F39EB03DCC720652AA0FA0EDF6E99A92202ACB5414A1B56240000004A817C8008114DE08239AAD0DFD1939741D086C1C4E7D72C9AA44", + "json": { + "OwnerCount": 0, + "Account": "rMNzmamctjEDqgwyBKbYfEzHbMeSkLQfaS", + "PreviousTxnLgrSeq": 104, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "81066DCA6C77C2C8A2F39EB03DCC720652AA0FA0EDF6E99A92202ACB5414A1B5", + "Flags": 0, + "Sequence": 1, + "Balance": "20000000000" + } + }, + { + "binary": "1100612200000000240000000125000000692D0000000055711B2ED1072F60EA84B05BA99C594D56BB10701BD83BBA68EC0D55CD4C4FDC506240000002540BE4008114B50D5FCBBD0125DD2085CFB29B82FF0DC7EA2D48", + "json": { + "OwnerCount": 0, + "Account": "rHWKKygGWPon9WSj4SzTH7vS4ict1QWKo9", + "PreviousTxnLgrSeq": 105, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "711B2ED1072F60EA84B05BA99C594D56BB10701BD83BBA68EC0D55CD4C4FDC50", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110072220002000025000000E355AED7737870A63125C3255323624846181E422B8E6E5CB7F1740EB364B89AC1F0628000000000000000000000000000000000000000434144000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004341440000000000A82BB90BF7031413B42E2C890827EDC2399B7BFA67D4C38D7EA4C680000000000000000000000000004341440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 227, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "CAD", + "value": "0", + "issuer": "rGLUu9LfpKyZyeTtSRXpU15e2FfrdvtADa" + }, + "PreviousTxnID": "AED7737870A63125C3255323624846181E422B8E6E5CB7F1740EB364B89AC1F0", + "Flags": 131072, + "Balance": { + "currency": "CAD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "CAD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110072220002000025000022BF55EB662576200A6F79B29F58B4C08605A391151199A551D0B2A7231013FD722D9C628000000000000000000000000000000000000000555344000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000005553440000000000BF3389DD51B5B8CC5AEA410403036A2990896C7067D4C38D7EA4C680000000000000000000000000005553440000000000F8B331F4AEC7900AD1B990899C54F87633EBB741", + "json": { + "PreviousTxnLgrSeq": 8895, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rJRyob8LPaA3twGEQDPU2gXevWhpSgD8S6" + }, + "PreviousTxnID": "EB662576200A6F79B29F58B4C08605A391151199A551D0B2A7231013FD722D9C", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rPgrEG6nMMwAM1VbTumL23dnEX4UmeUHk7" + } + } + }, + { + "binary": "110064220000000031000000000000000132000000000000000158E1DE1C68686261E5D06A3C89B78B4C254366AE9F166B56ED02DA545FF1954B298214C2659C14642A6604CE305966307E5F21817A092D0113409C3784EB4832563535522198D9D14E91D2760644174813689EE6A03AD43C6E4CED54FC8E215EFAE23E396D27099387D6687BDB63F7F282111BB0F567F8D1D649", + "json": { + "Owner": "rJ51FBSh6hXSUkFdMxwmtcorjx9izrC1yj", + "IndexNext": "0000000000000001", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000001", + "Flags": 0, + "RootIndex": "E1DE1C68686261E5D06A3C89B78B4C254366AE9F166B56ED02DA545FF1954B29", + "Indexes": [ + "9C3784EB4832563535522198D9D14E91D2760644174813689EE6A03AD43C6E4C", + "ED54FC8E215EFAE23E396D27099387D6687BDB63F7F282111BB0F567F8D1D649" + ] + } + }, + { + "binary": "1100612200000000240000000F2500005A2D2D00000000556F4331FB011A35EDBD5B17BF5D09764E9F5AA21984F7DDEB1C79E72E684BAD356240000001EE00D7748114D0A1812F500329A007707A05F5F7A8794E9200C0", + "json": { + "OwnerCount": 0, + "Account": "rLp9pST1aAndXTeUYFkpLtkmtZVNcMs2Hc", + "PreviousTxnLgrSeq": 23085, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "6F4331FB011A35EDBD5B17BF5D09764E9F5AA21984F7DDEB1C79E72E684BAD35", + "Flags": 0, + "Sequence": 15, + "Balance": "8287999860" + } + }, + { + "binary": "11006122000000002400000001250000006A2D000000005594057E3093D47E7A5286F1ABBF11780FBE644AE9DE530C15B1EDCF512AAC42EA624000000077359400811476E579FE6002C8EBBD229066CD5CD449DFFC85BD", + "json": { + "OwnerCount": 0, + "Account": "rBqCdAqw7jLH3EDx1Gkw4gUAbFqF7Gap4c", + "PreviousTxnLgrSeq": 106, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "94057E3093D47E7A5286F1ABBF11780FBE644AE9DE530C15B1EDCF512AAC42EA", + "Flags": 0, + "Sequence": 1, + "Balance": "2000000000" + } + }, + { + "binary": "110064220000000058E2EC9E1BC7B4667B7A5F2F68857F6E6A478A09B5BB4F99E09F694437C4152DED8214C2225C8CBC2B6D0E53C763B53A9AC66C658A4EEC01132065492B9F30F1CBEA168509128EB8619BAE02A7A7A4725FF3F8DAA70FA707A26E", + "json": { + "Owner": "rJ6VE6L87yaVmdyxa9jZFXSAdEFSoTGPbE", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "E2EC9E1BC7B4667B7A5F2F68857F6E6A478A09B5BB4F99E09F694437C4152DED", + "Indexes": ["65492B9F30F1CBEA168509128EB8619BAE02A7A7A4725FF3F8DAA70FA707A26E"] + } + }, + { + "binary": "1100612200000000240000000125000043032D0000000055DE5907B8533B8D3C40D1BA268D54E34D74F03348DAB54837F0000F9182A6BE036240000002540BE4008114A6473D67D54E36ED960CC45526D78345C96FAE5A", + "json": { + "OwnerCount": 0, + "Account": "rGwUWgN5BEg3QGNY3RX2HfYowjUTZdid3E", + "PreviousTxnLgrSeq": 17155, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "DE5907B8533B8D3C40D1BA268D54E34D74F03348DAB54837F0000F9182A6BE03", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "1100722200010000250000009355981BC0B7C0BD6686453A9DC15A98E32E77834B55CA5D177916C03A09F8B896396280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D4C8E1BC9BF04000000000000000000000000000425443000000000036D16F18B3AAC1868C1E3E8FA8EB7DDFD8ECCCAC678000000000000000000000000000000000000000425443000000000070EFFAAE000322A78E0D9DC9081564888C256C37", + "json": { + "PreviousTxnLgrSeq": 147, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "25", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "981BC0B7C0BD6686453A9DC15A98E32E77834B55CA5D177916C03A09F8B89639", + "Flags": 65536, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "0", + "issuer": "rBJwwXADHqbwsp6yhrqoyt2nmFx9FB83Th" + } + } + }, + { + "binary": "110061220000000024000000012500000E962D000000005548632A870D75AD35F30022A6E1406DE55D7807ACED8376BB9B6A99FCAD7242C66240038D7EA4C6800081149330CB93BA6421F2A782C45FBDF9CFAC001CE831", + "json": { + "OwnerCount": 0, + "Account": "rNRG8YAUqgsqoE5HSNPHTYqEGoKzMd7DJr", + "PreviousTxnLgrSeq": 3734, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "48632A870D75AD35F30022A6E1406DE55D7807ACED8376BB9B6A99FCAD7242C6", + "Flags": 0, + "Sequence": 1, + "Balance": "1000000000000000" + } + }, + { + "binary": "1100722200020000250000720737000000000000000038000000000000000055782876BCD6E5A40816DA9C300D06BF1736E7938CDF72C3A5F65AD50EABB956EB628000000000000000000000000000000000000000555344000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000005553440000000000550FC62003E785DC231A1058A05E56E3F09CF4E667D5438D7EA4C6800000000000000000000000000055534400000000007588B8DBDC8932DC410E8571045466C03F5A6B69", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 29191, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV" + }, + "PreviousTxnID": "782876BCD6E5A40816DA9C300D06BF1736E7938CDF72C3A5F65AD50EABB956EB", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "1000", + "issuer": "rB5TihdPbKgMrkFqrqUC3yLdE8hhv4BdeY" + } + } + }, + { + "binary": "1100612200000000240000000325000000962D0000000055F85D6E7FBA9FEB513B4A3FD80A5EB8A7245A0D93F3BDB86DC0D00CAD928C7F2062411C35669CC63CAC81140898C7B111B44A37A45C00A62C1B0C0FE904F2EB", + "json": { + "OwnerCount": 0, + "Account": "r8TR1AeB1RDQFabM6i8UoFsRF5basqoHJ", + "PreviousTxnLgrSeq": 150, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "F85D6E7FBA9FEB513B4A3FD80A5EB8A7245A0D93F3BDB86DC0D00CAD928C7F20", + "Flags": 0, + "Sequence": 3, + "Balance": "79997608218999980" + } + }, + { + "binary": "110061220000000024000000012500000EAD2D0000000055EC842B3F83593784A904FB1C43FF0677DEE59399E11286D426A52A1976856AB862400012309CE540008114107454A4BD1F14D5948783B7F2DE1E9E3819EAC4", + "json": { + "OwnerCount": 0, + "Account": "rpWrw1a5rQjZba1VySn2jichsPuB4GVnoC", + "PreviousTxnLgrSeq": 3757, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "EC842B3F83593784A904FB1C43FF0677DEE59399E11286D426A52A1976856AB8", + "Flags": 0, + "Sequence": 1, + "Balance": "20000000000000" + } + }, + { + "binary": "110072220002000025000000E955F859E3DE1B110C73CABAB950FEB0D734DE68B3E6028AC831A5E3EA65271953FD628000000000000000000000000000000000000000434144000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000004341440000000000C2659C14642A6604CE305966307E5F21817A092D67D4C38D7EA4C680000000000000000000000000004341440000000000E14829DB4C6419A8EFCAC1EC21D891A1A4339871", + "json": { + "PreviousTxnLgrSeq": 233, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "CAD", + "value": "0", + "issuer": "rJ51FBSh6hXSUkFdMxwmtcorjx9izrC1yj" + }, + "PreviousTxnID": "F859E3DE1B110C73CABAB950FEB0D734DE68B3E6028AC831A5E3EA65271953FD", + "Flags": 131072, + "Balance": { + "currency": "CAD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "CAD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + } + }, + { + "binary": "110061220000000024000000022500005A372D00000000557FA58DF8A1A2D639D65AA889553EE19C44C770B33859E53BD7CE18272A81C06B6240000002482021F68114BF02028C1FC2BA3B3AE4D5B82BE5FCE1FFF2BD6C", + "json": { + "OwnerCount": 0, + "Account": "rJQx7JpaHUBgk7C56T2MeEAu1JZcxDekgH", + "PreviousTxnLgrSeq": 23095, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "7FA58DF8A1A2D639D65AA889553EE19C44C770B33859E53BD7CE18272A81C06B", + "Flags": 0, + "Sequence": 2, + "Balance": "9799999990" + } + }, + { + "binary": "110061220000000024000000052500001EEF2D0000000055898B68513DA6B1680A114474E0327C076FDA4F1280721657FF639AAAD60669B16240000002540BE3D881146E981A51F3A195FB2C2EA7263B08093FFD6D8A4C", + "json": { + "OwnerCount": 0, + "Account": "rBnmYPdB5ModK8NyDUad1mxuQjHVp6tAbk", + "PreviousTxnLgrSeq": 7919, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "898B68513DA6B1680A114474E0327C076FDA4F1280721657FF639AAAD60669B1", + "Flags": 0, + "Sequence": 5, + "Balance": "9999999960" + } + }, + { + "binary": "11006122000000002400000001250000685B2D00000000557E9190820F32DF1CAE924C9257B610F46003794229030F314E2075E4101B09DF62400009184E72A0008114ADAF33BE117C38CB64B508AE83B5CA81252800BA", + "json": { + "OwnerCount": 0, + "Account": "rGqM8S5GnGwiEdZ6QRm1GThiTAa89tS86E", + "PreviousTxnLgrSeq": 26715, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "7E9190820F32DF1CAE924C9257B610F46003794229030F314E2075E4101B09DF", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "1100642200000000310000000000000003320000000000000002588E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4821458C742CF55C456DE367686CB9CED83750BD2497901132073E075E64CA5E7CE60FFCD5359C1D730EDFFEE7C4D992760A87DF7EA0A34E40F", + "json": { + "Owner": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx", + "IndexNext": "0000000000000003", + "LedgerEntryType": "DirectoryNode", + "IndexPrevious": "0000000000000002", + "Flags": 0, + "RootIndex": "8E92E688A132410427806A734DF6154B7535E439B72DECA5E4BC7CE17135C5A4", + "Indexes": ["73E075E64CA5E7CE60FFCD5359C1D730EDFFEE7C4D992760A87DF7EA0A34E40F"] + } + }, + { + "binary": "11006122000000002400000001250000006E2D000000005560CA81BF12767F5E9159DFDA983525E2B45776567ACA83D19FDBC28353F25D9F624000000277CF2A008114C0A76C462911CBC082751F5D8324F0648FE37381", + "json": { + "OwnerCount": 0, + "Account": "rJZCJ2jcohxtTzssBPeTGHLstMNEj5D96n", + "PreviousTxnLgrSeq": 110, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "60CA81BF12767F5E9159DFDA983525E2B45776567ACA83D19FDBC28353F25D9F", + "Flags": 0, + "Sequence": 1, + "Balance": "10600000000" + } + }, + { + "binary": "1100612200000000240000001125000069422D000000005562EFA3F14D0DE4136D444B65371C6EFE842C9B4782437D6DE81784329E04001262401508355D263D608114C06883DA16983A0289DFAC996C491D8F8E8FD996", + "json": { + "OwnerCount": 0, + "Account": "rJYMACXJd1eejwzZA53VncYmiK2kZSBxyD", + "PreviousTxnLgrSeq": 26946, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "62EFA3F14D0DE4136D444B65371C6EFE842C9B4782437D6DE81784329E040012", + "Flags": 0, + "Sequence": 17, + "Balance": "5919999799999840" + } + }, + { + "binary": "110064220000000058E1DE1C68686261E5D06A3C89B78B4C254366AE9F166B56ED02DA545FF1954B298214C2659C14642A6604CE305966307E5F21817A092D011340CF1F8DF231AE06AE9D55C3B3367A9ED1E430FC0A6CA193EEA559C3ADF0A634FB353D47B7B033F5EC041BD4E367437C9EDA160D14BFBC3EF43B3335259AA5D5D5", + "json": { + "Owner": "rJ51FBSh6hXSUkFdMxwmtcorjx9izrC1yj", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "E1DE1C68686261E5D06A3C89B78B4C254366AE9F166B56ED02DA545FF1954B29", + "Indexes": [ + "CF1F8DF231AE06AE9D55C3B3367A9ED1E430FC0A6CA193EEA559C3ADF0A634FB", + "353D47B7B033F5EC041BD4E367437C9EDA160D14BFBC3EF43B3335259AA5D5D5" + ] + } + }, + { + "binary": "1100612200000000240000000125000068612D0000000055AFD4F8E6EAB0CBE97A842576D6CEB158A54B209B6C28E5106E8445F0C599EF5362400009184E72A0008114D256C27AAE7F92B7E798EFA70891AD4B1B8E431F", + "json": { + "OwnerCount": 0, + "Account": "rLBwqTG5ErivwPXGaAGLQzJ2rr7ZTpjMx7", + "PreviousTxnLgrSeq": 26721, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "AFD4F8E6EAB0CBE97A842576D6CEB158A54B209B6C28E5106E8445F0C599EF53", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "1100612200000000240000000125000068632D0000000055256238C32D954F1DBE93C7FDF50D24226238DB495ED6A3205803915A27A138C262400009184E72A00081142ADE77C62E7CA974C2A211935FAD3686630FB451", + "json": { + "OwnerCount": 0, + "Account": "rhuCtPvq6jJeYF1S7aEmAcE5iM8LstSrrP", + "PreviousTxnLgrSeq": 26723, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "256238C32D954F1DBE93C7FDF50D24226238DB495ED6A3205803915A27A138C2", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "11007222000300002500005ADC37000000000000000038000000000000000055F9ED6C634DE09655F9F7C8E088B9157BB785571CAA4305A6DD7BC876BD57671D6280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D4C71AFD498D00000000000000000000000000005553440000000000169F404D62A8D2C8EE3935F230AA60BC07919E9667D4C71AFD498D000000000000000000000000000055534400000000006A03714FE4B738A637A094271E0DE8414D904CFA", + "json": { + "HighNode": "0000000000000000", + "LowNode": "0000000000000000", + "PreviousTxnLgrSeq": 23260, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "20", + "issuer": "rshceBo6ftSVYo8h5uNPzRWbdqk4W6g9va" + }, + "PreviousTxnID": "F9ED6C634DE09655F9F7C8E088B9157BB785571CAA4305A6DD7BC876BD57671D", + "Flags": 196608, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "20", + "issuer": "rwCYkXihZPm7dWuPCXoS3WXap7vbnZ8uzB" + } + } + }, + { + "binary": "110064220000000036531AA535D3D0C00058F774E0321809251174AC85531606FB46B75EEF9F842F9697531AA535D3D0C000011100000000000000000000000042544300000000000211E8ACFC6B5EF4EA0601241525375162F43C2FF28503110000000000000000000000005553440000000000041158C742CF55C456DE367686CB9CED83750BD24979011320D1CB738BD08AC36DCB77191DB87C6E40FA478B86503371ED497F30931D7F4F52", + "json": { + "TakerPaysCurrency": "0000000000000000000000004254430000000000", + "ExchangeRate": "531AA535D3D0C000", + "TakerGetsCurrency": "0000000000000000000000005553440000000000", + "TakerGetsIssuer": "58C742CF55C456DE367686CB9CED83750BD24979", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "F774E0321809251174AC85531606FB46B75EEF9F842F9697531AA535D3D0C000", + "Indexes": ["D1CB738BD08AC36DCB77191DB87C6E40FA478B86503371ED497F30931D7F4F52"], + "TakerPaysIssuer": "E8ACFC6B5EF4EA0601241525375162F43C2FF285" + } + }, + { + "binary": "110064220000000058F8327E8AFE1AF09A5B13D6384F055CCC475A5757308AA243A7A1A2CB00A6CB7E8214E8ACFC6B5EF4EA0601241525375162F43C2FF28501134017B72685E9FBEFE18E0C1E8F07000E1B345A18ECD2D2BE9B27E69045248EF036F9830A2F94E5B611F6364893235E6D7F3521A8DE8AF936687B40C555E1282836", + "json": { + "Owner": "r4DGz8SxHXLaqsA9M2oocXsrty6BMSQvw3", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "F8327E8AFE1AF09A5B13D6384F055CCC475A5757308AA243A7A1A2CB00A6CB7E", + "Indexes": [ + "17B72685E9FBEFE18E0C1E8F07000E1B345A18ECD2D2BE9B27E69045248EF036", + "F9830A2F94E5B611F6364893235E6D7F3521A8DE8AF936687B40C555E1282836" + ] + } + }, + { + "binary": "110072220002000025000022C5558D7F42ED0621FBCFAE55CC6F2A9403A2AFB205708CCBA3109BB61DB8DDA261B4628000000000000000000000000000000000000000555344000000000000000000000000000000000000000000000000016680000000000000000000000000000000000000005553440000000000712B799C79D1EEE3094B59EF9920C7FEB3CE449967D498DE76816D80000000000000000000000000005553440000000000BF3389DD51B5B8CC5AEA410403036A2990896C70", + "json": { + "PreviousTxnLgrSeq": 8901, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rBKPS4oLSaV2KVVuHH8EpQqMGgGefGFQs7" + }, + "PreviousTxnID": "8D7F42ED0621FBCFAE55CC6F2A9403A2AFB205708CCBA3109BB61DB8DDA261B4", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "7", + "issuer": "rJRyob8LPaA3twGEQDPU2gXevWhpSgD8S6" + } + } + }, + { + "binary": "1100612200000000240000000125000000E52D00000000556B10BDDABEB8C1D5F8E0CD7A54C08FEAD32260BDFFAC9692EE79B95E6E022A2762400009184E72A0008114E965F6EB72FBBB66B6A5A366F79B964D50115D4C", + "json": { + "OwnerCount": 0, + "Account": "r4HabKLiKYtCbwnGG3Ev4HqncmXWsCtF9F", + "PreviousTxnLgrSeq": 229, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "6B10BDDABEB8C1D5F8E0CD7A54C08FEAD32260BDFFAC9692EE79B95E6E022A27", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000000" + } + }, + { + "binary": "110064220000000058F95F6D3A1EF7981E5CA4D5AEC4DA63392B126C76469735BCCA26150A1AF6D9C3821473B3ED1EBFF63BECF6E84D03B6A0CAF1BE8A6050011320CAD951AB279A749AE648FD1DFF56C021BD66E36187022E772C31FE52106CB13B", + "json": { + "Owner": "rBY8EZDiCNMjjhrC7SCfaGr2PzGWtSntNy", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "F95F6D3A1EF7981E5CA4D5AEC4DA63392B126C76469735BCCA26150A1AF6D9C3", + "Indexes": ["CAD951AB279A749AE648FD1DFF56C021BD66E36187022E772C31FE52106CB13B"] + } + }, + { + "binary": "1100722200030000250000274255FE8A112AD2C27440245F120388EB00C8208833B3737A6DE6CB8D3AF3840ECEAD6280000000000000000000000000000000000000004254430000000000000000000000000000000000000000000000000166D4D1C37937E08000000000000000000000000000425443000000000058C742CF55C456DE367686CB9CED83750BD2497967D4D1C37937E080000000000000000000000000004254430000000000E8ACFC6B5EF4EA0601241525375162F43C2FF285", + "json": { + "PreviousTxnLgrSeq": 10050, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "BTC", + "value": "50", + "issuer": "r9aRw8p1jHtR9XhDAE22TjtM7PdupNXhkx" + }, + "PreviousTxnID": "FE8A112AD2C27440245F120388EB00C8208833B3737A6DE6CB8D3AF3840ECEAD", + "Flags": 196608, + "Balance": { + "currency": "BTC", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "BTC", + "value": "50", + "issuer": "r4DGz8SxHXLaqsA9M2oocXsrty6BMSQvw3" + } + } + }, + { + "binary": "11006122000000002400000001250000006F2D0000000055F1414803262CA34694E60B7D1EFBD6F7400966BFE1C7EEF2C564599730D792CC6240000002540BE4008114C88385F33698DEA709EAD494EB4E60343B36A466", + "json": { + "OwnerCount": 0, + "Account": "rKHD6m92oprEVdi1FwGfTzxbgKt8eQfUYL", + "PreviousTxnLgrSeq": 111, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "F1414803262CA34694E60B7D1EFBD6F7400966BFE1C7EEF2C564599730D792CC", + "Flags": 0, + "Sequence": 1, + "Balance": "10000000000" + } + }, + { + "binary": "110064220000000058FD46CE0EEBB1C52878ECA415AB73DF378CE04452AE67873B8BEF0356F89B35CE82148D3A0AEF277858BD4D9751ECECD16779C0CC86D00113408A2B79E75D1012CB89DBF27A0CE4750B398C353D679F5C1E22F8FAC6F87AE13CCD34D8FF7C656B66E2298DB420C918FE27DFFF2186AC8D1785D8CBF2C6BC3488", + "json": { + "Owner": "rD1jovjQeEpvaDwn9wKaYokkXXrqo4D23x", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "FD46CE0EEBB1C52878ECA415AB73DF378CE04452AE67873B8BEF0356F89B35CE", + "Indexes": [ + "8A2B79E75D1012CB89DBF27A0CE4750B398C353D679F5C1E22F8FAC6F87AE13C", + "CD34D8FF7C656B66E2298DB420C918FE27DFFF2186AC8D1785D8CBF2C6BC3488" + ] + } + }, + { + "binary": "1100612200000000240000000925000045B22D00000002550A7E6FD67D2EB7B7AD0E10DAC33B595B26E8E0DFD9F06183FB430A46779B66346240000000D09DC2B081142B6C42A95B3F7EE1971E4A10098E8F1B5F66AA08", + "json": { + "OwnerCount": 2, + "Account": "rhxbkK9jGqPVLZSWPvCEmmf15xHBfJfCEy", + "PreviousTxnLgrSeq": 17842, + "LedgerEntryType": "AccountRoot", + "PreviousTxnID": "0A7E6FD67D2EB7B7AD0E10DAC33B595B26E8E0DFD9F06183FB430A46779B6634", + "Flags": 0, + "Sequence": 9, + "Balance": "3499999920" + } + }, + { + "binary": "110064220000000058FFA9A0BE95FAC1E9843396C0791EADA3CBFEE551D900BA126E4AD107EC71008C82142ECC94F5447A3CC1348E94C84D4677442C9E4227011340B15AB125CC1D8CACDC22B76E5AABF74A6BB620A5C223BE81ECB71EF17F1C3489B82A83B063FF08369F9BDEDC73074352FE37733E8373F6EDBFFC872489B57D93", + "json": { + "Owner": "rnGTwRTacmqZZBwPB6rh3H1W4GoTZCQtNA", + "LedgerEntryType": "DirectoryNode", + "Flags": 0, + "RootIndex": "FFA9A0BE95FAC1E9843396C0791EADA3CBFEE551D900BA126E4AD107EC71008C", + "Indexes": [ + "B15AB125CC1D8CACDC22B76E5AABF74A6BB620A5C223BE81ECB71EF17F1C3489", + "B82A83B063FF08369F9BDEDC73074352FE37733E8373F6EDBFFC872489B57D93" + ] + } + } + ], + "transactions": [{ + "binary": "1200002200000000240000003E6140000002540BE40068400000000000000A7321034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E74473045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F17962646398114550FC62003E785DC231A1058A05E56E3F09CF4E68314D4CC8AB5B21D86A82C3E9E8D0ECF2404B77FECBA", + "json": { + "Account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV", + "Destination": "rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj", + "TransactionType": "Payment", + "TxnSignature": "3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639", + "SigningPubKey": "034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E", + "Amount": "10000000000", + "Fee": "10", + "Flags": 0, + "Sequence": 62 + } + }], + "ledgerData": [{ + "binary": "01E91435016340767BF1C4A3EACEB081770D8ADE216C85445DD6FB002C6B5A2930F2DECE006DA18150CB18F6DD33F6F0990754C962A7CCE62F332FF9C13939B03B864117F0BDA86B6E9B4F873B5C3E520634D343EF5D9D9A4246643D64DAD278BA95DC0EAC6EB5350CF970D521276CDE21276CE60A00", + "json": { + "account_hash": "3B5C3E520634D343EF5D9D9A4246643D64DAD278BA95DC0EAC6EB5350CF970D5", + "close_flags": 0, + "close_time": 556231910, + "close_time_resolution": 10, + "ledger_index": 32052277, + "parent_close_time": 556231902, + "parent_hash": "EACEB081770D8ADE216C85445DD6FB002C6B5A2930F2DECE006DA18150CB18F6", + "total_coins": "99994494362043555", + "transaction_hash": "DD33F6F0990754C962A7CCE62F332FF9C13939B03B864117F0BDA86B6E9B4F87" + } + }] +} diff --git a/src/core/test_data/data-driven-tests.json b/src/core/test_data/data-driven-tests.json new file mode 100644 index 00000000..5f27bf2f --- /dev/null +++ b/src/core/test_data/data-driven-tests.json @@ -0,0 +1,3951 @@ +{ + "types": [ + { + "name": "UInt16", + "ordinal": 1 + }, + { + "name": "UInt32", + "ordinal": 2 + }, + { + "name": "UInt64", + "ordinal": 3 + }, + { + "name": "Hash128", + "ordinal": 4 + }, + { + "name": "Hash256", + "ordinal": 5 + }, + { + "name": "Amount", + "ordinal": 6 + }, + { + "name": "Blob", + "ordinal": 7 + }, + { + "name": "AccountID", + "ordinal": 8 + }, + { + "name": "SerializedDict", + "ordinal": 14 + }, + { + "name": "SerializedList", + "ordinal": 15 + }, + { + "name": "UInt8", + "ordinal": 16 + }, + { + "name": "Hash160", + "ordinal": 17 + }, + { + "name": "PathSet", + "ordinal": 18 + }, + { + "name": "Vector256", + "ordinal": 19 + }, + { + "name": "Transaction", + "ordinal": 10001 + }, + { + "name": "LedgerEntry", + "ordinal": 10002 + }, + { + "name": "Validation", + "ordinal": 10003 + } + ], + "fields_tests": [ + { + "type_name": "UInt16", + "name": "LedgerEntryType", + "nth_of_type": 1, + "type": 1, + "expected_hex": "11" + }, + { + "type_name": "UInt16", + "name": "TransactionType", + "nth_of_type": 2, + "type": 1, + "expected_hex": "12" + }, + { + "type_name": "UInt32", + "name": "Flags", + "nth_of_type": 2, + "type": 2, + "expected_hex": "22" + }, + { + "type_name": "UInt32", + "name": "SourceTag", + "nth_of_type": 3, + "type": 2, + "expected_hex": "23" + }, + { + "type_name": "UInt32", + "name": "Sequence", + "nth_of_type": 4, + "type": 2, + "expected_hex": "24" + }, + { + "type_name": "UInt32", + "name": "PreviousTxnLgrSeq", + "nth_of_type": 5, + "type": 2, + "expected_hex": "25" + }, + { + "type_name": "UInt32", + "name": "LedgerSequence", + "nth_of_type": 6, + "type": 2, + "expected_hex": "26" + }, + { + "type_name": "UInt32", + "name": "CloseTime", + "nth_of_type": 7, + "type": 2, + "expected_hex": "27" + }, + { + "type_name": "UInt32", + "name": "ParentCloseTime", + "nth_of_type": 8, + "type": 2, + "expected_hex": "28" + }, + { + "type_name": "UInt32", + "name": "SigningTime", + "nth_of_type": 9, + "type": 2, + "expected_hex": "29" + }, + { + "type_name": "UInt32", + "name": "Expiration", + "nth_of_type": 10, + "type": 2, + "expected_hex": "2A" + }, + { + "type_name": "UInt32", + "name": "TransferRate", + "nth_of_type": 11, + "type": 2, + "expected_hex": "2B" + }, + { + "type_name": "UInt32", + "name": "WalletSize", + "nth_of_type": 12, + "type": 2, + "expected_hex": "2C" + }, + { + "type_name": "UInt32", + "name": "OwnerCount", + "nth_of_type": 13, + "type": 2, + "expected_hex": "2D" + }, + { + "type_name": "UInt32", + "name": "DestinationTag", + "nth_of_type": 14, + "type": 2, + "expected_hex": "2E" + }, + { + "type_name": "UInt32", + "name": "HighQualityIn", + "nth_of_type": 16, + "type": 2, + "expected_hex": "2010" + }, + { + "type_name": "UInt32", + "name": "HighQualityOut", + "nth_of_type": 17, + "type": 2, + "expected_hex": "2011" + }, + { + "type_name": "UInt32", + "name": "LowQualityIn", + "nth_of_type": 18, + "type": 2, + "expected_hex": "2012" + }, + { + "type_name": "UInt32", + "name": "LowQualityOut", + "nth_of_type": 19, + "type": 2, + "expected_hex": "2013" + }, + { + "type_name": "UInt32", + "name": "QualityIn", + "nth_of_type": 20, + "type": 2, + "expected_hex": "2014" + }, + { + "type_name": "UInt32", + "name": "QualityOut", + "nth_of_type": 21, + "type": 2, + "expected_hex": "2015" + }, + { + "type_name": "UInt32", + "name": "StampEscrow", + "nth_of_type": 22, + "type": 2, + "expected_hex": "2016" + }, + { + "type_name": "UInt32", + "name": "BondAmount", + "nth_of_type": 23, + "type": 2, + "expected_hex": "2017" + }, + { + "type_name": "UInt32", + "name": "LoadFee", + "nth_of_type": 24, + "type": 2, + "expected_hex": "2018" + }, + { + "type_name": "UInt32", + "name": "OfferSequence", + "nth_of_type": 25, + "type": 2, + "expected_hex": "2019" + }, + { + "type_name": "UInt32", + "name": "FirstLedgerSequence", + "nth_of_type": 26, + "type": 2, + "expected_hex": "201A" + }, + { + "type_name": "UInt32", + "name": "LastLedgerSequence", + "nth_of_type": 27, + "type": 2, + "expected_hex": "201B" + }, + { + "type_name": "UInt32", + "name": "TransactionIndex", + "nth_of_type": 28, + "type": 2, + "expected_hex": "201C" + }, + { + "type_name": "UInt32", + "name": "OperationLimit", + "nth_of_type": 29, + "type": 2, + "expected_hex": "201D" + }, + { + "type_name": "UInt32", + "name": "ReferenceFeeUnits", + "nth_of_type": 30, + "type": 2, + "expected_hex": "201E" + }, + { + "type_name": "UInt32", + "name": "ReserveBase", + "nth_of_type": 31, + "type": 2, + "expected_hex": "201F" + }, + { + "type_name": "UInt32", + "name": "ReserveIncrement", + "nth_of_type": 32, + "type": 2, + "expected_hex": "2020" + }, + { + "type_name": "UInt32", + "name": "SetFlag", + "nth_of_type": 33, + "type": 2, + "expected_hex": "2021" + }, + { + "type_name": "UInt32", + "name": "ClearFlag", + "nth_of_type": 34, + "type": 2, + "expected_hex": "2022" + }, + { + "type_name": "UInt32", + "name": "SignerQuorum", + "nth_of_type": 35, + "type": 2, + "expected_hex": "2023" + }, + { + "type_name": "UInt32", + "name": "CancelAfter", + "nth_of_type": 36, + "type": 2, + "expected_hex": "2024" + }, + { + "type_name": "UInt32", + "name": "FinishAfter", + "nth_of_type": 37, + "type": 2, + "expected_hex": "2025" + }, + { + "type_name": "UInt64", + "name": "IndexNext", + "nth_of_type": 1, + "type": 3, + "expected_hex": "31" + }, + { + "type_name": "UInt64", + "name": "IndexPrevious", + "nth_of_type": 2, + "type": 3, + "expected_hex": "32" + }, + { + "type_name": "UInt64", + "name": "BookNode", + "nth_of_type": 3, + "type": 3, + "expected_hex": "33" + }, + { + "type_name": "UInt64", + "name": "OwnerNode", + "nth_of_type": 4, + "type": 3, + "expected_hex": "34" + }, + { + "type_name": "UInt64", + "name": "BaseFee", + "nth_of_type": 5, + "type": 3, + "expected_hex": "35" + }, + { + "type_name": "UInt64", + "name": "ExchangeRate", + "nth_of_type": 6, + "type": 3, + "expected_hex": "36" + }, + { + "type_name": "UInt64", + "name": "LowNode", + "nth_of_type": 7, + "type": 3, + "expected_hex": "37" + }, + { + "type_name": "UInt64", + "name": "HighNode", + "nth_of_type": 8, + "type": 3, + "expected_hex": "38" + }, + { + "type_name": "Hash128", + "name": "EmailHash", + "nth_of_type": 1, + "type": 4, + "expected_hex": "41" + }, + { + "type_name": "Hash256", + "name": "LedgerHash", + "nth_of_type": 1, + "type": 5, + "expected_hex": "51" + }, + { + "type_name": "Hash256", + "name": "ParentHash", + "nth_of_type": 2, + "type": 5, + "expected_hex": "52" + }, + { + "type_name": "Hash256", + "name": "TransactionHash", + "nth_of_type": 3, + "type": 5, + "expected_hex": "53" + }, + { + "type_name": "Hash256", + "name": "AccountHash", + "nth_of_type": 4, + "type": 5, + "expected_hex": "54" + }, + { + "type_name": "Hash256", + "name": "PreviousTxnID", + "nth_of_type": 5, + "type": 5, + "expected_hex": "55" + }, + { + "type_name": "Hash256", + "name": "LedgerIndex", + "nth_of_type": 6, + "type": 5, + "expected_hex": "56" + }, + { + "type_name": "Hash256", + "name": "WalletLocator", + "nth_of_type": 7, + "type": 5, + "expected_hex": "57" + }, + { + "type_name": "Hash256", + "name": "RootIndex", + "nth_of_type": 8, + "type": 5, + "expected_hex": "58" + }, + { + "type_name": "Hash256", + "name": "AccountTxnID", + "nth_of_type": 9, + "type": 5, + "expected_hex": "59" + }, + { + "type_name": "Hash256", + "name": "BookDirectory", + "nth_of_type": 16, + "type": 5, + "expected_hex": "5010" + }, + { + "type_name": "Hash256", + "name": "InvoiceID", + "nth_of_type": 17, + "type": 5, + "expected_hex": "5011" + }, + { + "type_name": "Hash256", + "name": "Nickname", + "nth_of_type": 18, + "type": 5, + "expected_hex": "5012" + }, + { + "type_name": "Hash256", + "name": "Amendment", + "nth_of_type": 19, + "type": 5, + "expected_hex": "5013" + }, + { + "type_name": "Hash256", + "name": "TicketID", + "nth_of_type": 20, + "type": 5, + "expected_hex": "5014" + }, + { + "type_name": "Hash256", + "name": "Digest", + "nth_of_type": 21, + "type": 5, + "expected_hex": "5015" + }, + { + "type_name": "Amount", + "name": "Amount", + "nth_of_type": 1, + "type": 6, + "expected_hex": "61" + }, + { + "type_name": "Amount", + "name": "Balance", + "nth_of_type": 2, + "type": 6, + "expected_hex": "62" + }, + { + "type_name": "Amount", + "name": "LimitAmount", + "nth_of_type": 3, + "type": 6, + "expected_hex": "63" + }, + { + "type_name": "Amount", + "name": "TakerPays", + "nth_of_type": 4, + "type": 6, + "expected_hex": "64" + }, + { + "type_name": "Amount", + "name": "TakerGets", + "nth_of_type": 5, + "type": 6, + "expected_hex": "65" + }, + { + "type_name": "Amount", + "name": "LowLimit", + "nth_of_type": 6, + "type": 6, + "expected_hex": "66" + }, + { + "type_name": "Amount", + "name": "HighLimit", + "nth_of_type": 7, + "type": 6, + "expected_hex": "67" + }, + { + "type_name": "Amount", + "name": "Fee", + "nth_of_type": 8, + "type": 6, + "expected_hex": "68" + }, + { + "type_name": "Amount", + "name": "SendMax", + "nth_of_type": 9, + "type": 6, + "expected_hex": "69" + }, + { + "type_name": "Amount", + "name": "MinimumOffer", + "nth_of_type": 16, + "type": 6, + "expected_hex": "6010" + }, + { + "type_name": "Amount", + "name": "RippleEscrow", + "nth_of_type": 17, + "type": 6, + "expected_hex": "6011" + }, + { + "type_name": "Amount", + "name": "DeliveredAmount", + "nth_of_type": 18, + "type": 6, + "expected_hex": "6012" + }, + { + "type_name": "Blob", + "name": "PublicKey", + "nth_of_type": 1, + "type": 7, + "expected_hex": "71" + }, + { + "type_name": "Blob", + "name": "MessageKey", + "nth_of_type": 2, + "type": 7, + "expected_hex": "72" + }, + { + "type_name": "Blob", + "name": "SigningPubKey", + "nth_of_type": 3, + "type": 7, + "expected_hex": "73" + }, + { + "type_name": "Blob", + "name": "TxnSignature", + "nth_of_type": 4, + "type": 7, + "expected_hex": "74" + }, + { + "type_name": "Blob", + "name": "Generator", + "nth_of_type": 5, + "type": 7, + "expected_hex": "75" + }, + { + "type_name": "Blob", + "name": "Signature", + "nth_of_type": 6, + "type": 7, + "expected_hex": "76" + }, + { + "type_name": "Blob", + "name": "Domain", + "nth_of_type": 7, + "type": 7, + "expected_hex": "77" + }, + { + "type_name": "Blob", + "name": "FundCode", + "nth_of_type": 8, + "type": 7, + "expected_hex": "78" + }, + { + "type_name": "Blob", + "name": "RemoveCode", + "nth_of_type": 9, + "type": 7, + "expected_hex": "79" + }, + { + "type_name": "Blob", + "name": "ExpireCode", + "nth_of_type": 10, + "type": 7, + "expected_hex": "7A" + }, + { + "type_name": "Blob", + "name": "CreateCode", + "nth_of_type": 11, + "type": 7, + "expected_hex": "7B" + }, + { + "type_name": "Blob", + "name": "MemoType", + "nth_of_type": 12, + "type": 7, + "expected_hex": "7C" + }, + { + "type_name": "Blob", + "name": "MemoData", + "nth_of_type": 13, + "type": 7, + "expected_hex": "7D" + }, + { + "type_name": "Blob", + "name": "MemoFormat", + "nth_of_type": 14, + "type": 7, + "expected_hex": "7E" + }, + { + "type_name": "Blob", + "name": "Fulfillment", + "nth_of_type": 16, + "type": 7, + "expected_hex": "7010" + }, + { + "type_name": "Blob", + "name": "Condition", + "nth_of_type": 17, + "type": 7, + "expected_hex": "7011" + }, + { + "type_name": "AccountID", + "name": "Account", + "nth_of_type": 1, + "type": 8, + "expected_hex": "81" + }, + { + "type_name": "AccountID", + "name": "Owner", + "nth_of_type": 2, + "type": 8, + "expected_hex": "82" + }, + { + "type_name": "AccountID", + "name": "Destination", + "nth_of_type": 3, + "type": 8, + "expected_hex": "83" + }, + { + "type_name": "AccountID", + "name": "Issuer", + "nth_of_type": 4, + "type": 8, + "expected_hex": "84" + }, + { + "type_name": "AccountID", + "name": "Target", + "nth_of_type": 7, + "type": 8, + "expected_hex": "87" + }, + { + "type_name": "AccountID", + "name": "RegularKey", + "nth_of_type": 8, + "type": 8, + "expected_hex": "88" + }, + { + "type_name": "SerializedDict", + "name": "ObjectEndMarker", + "nth_of_type": 1, + "type": 14, + "expected_hex": "E1" + }, + { + "type_name": "SerializedDict", + "name": "TransactionMetaData", + "nth_of_type": 2, + "type": 14, + "expected_hex": "E2" + }, + { + "type_name": "SerializedDict", + "name": "CreatedNode", + "nth_of_type": 3, + "type": 14, + "expected_hex": "E3" + }, + { + "type_name": "SerializedDict", + "name": "DeletedNode", + "nth_of_type": 4, + "type": 14, + "expected_hex": "E4" + }, + { + "type_name": "SerializedDict", + "name": "ModifiedNode", + "nth_of_type": 5, + "type": 14, + "expected_hex": "E5" + }, + { + "type_name": "SerializedDict", + "name": "PreviousFields", + "nth_of_type": 6, + "type": 14, + "expected_hex": "E6" + }, + { + "type_name": "SerializedDict", + "name": "FinalFields", + "nth_of_type": 7, + "type": 14, + "expected_hex": "E7" + }, + { + "type_name": "SerializedDict", + "name": "NewFields", + "nth_of_type": 8, + "type": 14, + "expected_hex": "E8" + }, + { + "type_name": "SerializedDict", + "name": "TemplateEntry", + "nth_of_type": 9, + "type": 14, + "expected_hex": "E9" + }, + { + "type_name": "SerializedDict", + "name": "Memo", + "nth_of_type": 10, + "type": 14, + "expected_hex": "EA" + }, + { + "type_name": "SerializedList", + "name": "ArrayEndMarker", + "nth_of_type": 1, + "type": 15, + "expected_hex": "F1" + }, + { + "type_name": "SerializedList", + "name": "Signers", + "nth_of_type": 3, + "type": 15, + "expected_hex": "F3" + }, + { + "type_name": "SerializedList", + "name": "SignerEntries", + "nth_of_type": 4, + "type": 15, + "expected_hex": "F4" + }, + { + "type_name": "SerializedList", + "name": "Template", + "nth_of_type": 5, + "type": 15, + "expected_hex": "F5" + }, + { + "type_name": "SerializedList", + "name": "Necessary", + "nth_of_type": 6, + "type": 15, + "expected_hex": "F6" + }, + { + "type_name": "SerializedList", + "name": "Sufficient", + "nth_of_type": 7, + "type": 15, + "expected_hex": "F7" + }, + { + "type_name": "SerializedList", + "name": "AffectedNodes", + "nth_of_type": 8, + "type": 15, + "expected_hex": "F8" + }, + { + "type_name": "SerializedList", + "name": "Memos", + "nth_of_type": 9, + "type": 15, + "expected_hex": "F9" + }, + { + "type_name": "UInt8", + "name": "CloseResolution", + "nth_of_type": 1, + "type": 16, + "expected_hex": "0110" + }, + { + "type_name": "UInt8", + "name": "Method", + "nth_of_type": 2, + "type": 16, + "expected_hex": "0210" + }, + { + "type_name": "UInt8", + "name": "TransactionResult", + "nth_of_type": 3, + "type": 16, + "expected_hex": "0310" + }, + { + "type_name": "Hash160", + "name": "TakerPaysCurrency", + "nth_of_type": 1, + "type": 17, + "expected_hex": "0111" + }, + { + "type_name": "Hash160", + "name": "TakerPaysIssuer", + "nth_of_type": 2, + "type": 17, + "expected_hex": "0211" + }, + { + "type_name": "Hash160", + "name": "TakerGetsCurrency", + "nth_of_type": 3, + "type": 17, + "expected_hex": "0311" + }, + { + "type_name": "Hash160", + "name": "TakerGetsIssuer", + "nth_of_type": 4, + "type": 17, + "expected_hex": "0411" + }, + { + "type_name": "PathSet", + "name": "Paths", + "nth_of_type": 1, + "type": 18, + "expected_hex": "0112" + }, + { + "type_name": "Vector256", + "name": "Indexes", + "nth_of_type": 1, + "type": 19, + "expected_hex": "0113" + }, + { + "type_name": "Vector256", + "name": "Hashes", + "nth_of_type": 2, + "type": 19, + "expected_hex": "0213" + }, + { + "type_name": "Vector256", + "name": "Amendments", + "nth_of_type": 3, + "type": 19, + "expected_hex": "0313" + }, + { + "type_name": "UInt8", + "name": "TickSize", + "nth_of_type": 16, + "type": 16, + "expected_hex": "001010" + } + ], + "whole_objects": [ + { + "tx_json": { + "TakerPays": "101204800", + "Account": "rGFpans8aW7XZNEcNky6RHKyEdLvXPMnUn", + "TransactionType": "OfferCreate", + "Fee": "12", + "Expiration": 1398443249, + "TakerGets": { + "currency": "CNY", + "value": "4.2", + "issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y" + }, + "Flags": 0, + "Sequence": 6068 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0007" + ], + "json": "OfferCreate", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "000017B4" + ], + "json": 6068, + "field_header": "24" + } + ], + [ + "Expiration", + { + "binary": [ + "535A8CF1" + ], + "json": 1398443249, + "field_header": "2A" + } + ], + [ + "TakerPays", + { + "binary": [ + "4000000006084340" + ], + "json": "101204800", + "field_header": "64" + } + ], + [ + "TakerGets", + { + "binary": [ + "D48EEBE0B40E8000", + "000000000000000000000000434E590000000000", + "CED6E99370D5C00EF4EBF72567DA99F5661BFB3A" + ], + "json": { + "currency": "CNY", + "value": "4.2", + "issuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y" + }, + "field_header": "65" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "AD6E583D47F90F29FD8B23225E6F905602B0292E" + ], + "vl_length": "14", + "json": "rGFpans8aW7XZNEcNky6RHKyEdLvXPMnUn", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "120007220000000024000017B42A535A8CF164400000000608434065D48EEBE0B40E8000000000000000000000000000434E590000000000CED6E99370D5C00EF4EBF72567DA99F5661BFB3A68400000000000000C8114AD6E583D47F90F29FD8B23225E6F905602B0292E" + }, + { + "tx_json": { + "TakerPays": "1311313", + "Account": "rLpW9Reyn9YqZ8mxbq8nviXSp4TnHafVJQ", + "TransactionType": "OfferCreate", + "Fee": "12", + "TakerGets": { + "currency": "CNY", + "value": "0.05114362355976031", + "issuer": "rGYYWKxT1XgNipUJouCq4cKiyAdq8xBoE9" + }, + "Flags": 0, + "Sequence": 20772, + "LastLedgerSequence": 6220012 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0007" + ], + "json": "OfferCreate", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00005124" + ], + "json": 20772, + "field_header": "24" + } + ], + [ + "LastLedgerSequence", + { + "binary": [ + "005EE8EC" + ], + "json": 6220012, + "field_header": "201B" + } + ], + [ + "TakerPays", + { + "binary": [ + "4000000000140251" + ], + "json": "1311313", + "field_header": "64" + } + ], + [ + "TakerGets", + { + "binary": [ + "D4122B7C477B075F", + "000000000000000000000000434E590000000000", + "AA8114C65DA8EA1BE40849F974685289CC145CCF" + ], + "json": { + "currency": "CNY", + "value": "0.05114362355976031", + "issuer": "rGYYWKxT1XgNipUJouCq4cKiyAdq8xBoE9" + }, + "field_header": "65" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "D0B32295596E50017E246FE85FC5982A1BD89CE4" + ], + "vl_length": "14", + "json": "rLpW9Reyn9YqZ8mxbq8nviXSp4TnHafVJQ", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "12000722000000002400005124201B005EE8EC64400000000014025165D4122B7C477B075F000000000000000000000000434E590000000000AA8114C65DA8EA1BE40849F974685289CC145CCF68400000000000000C8114D0B32295596E50017E246FE85FC5982A1BD89CE4" + }, + { + "tx_json": { + "TakerPays": "223174650", + "Account": "rPk2dXr27rMw9G5Ej9ad2Tt7RJzGy8ycBp", + "TransactionType": "OfferCreate", + "Memos": [ + { + "Memo": { + "MemoType": "584D4D2076616C7565", + "MemoData": "322E3230393635" + } + } + ], + "Fee": "15", + "OfferSequence": 1002, + "TakerGets": { + "currency": "XMM", + "value": "100", + "issuer": "rExAPEZvbkZqYPuNcZ7XEBLENEshsWDQc8" + }, + "Flags": 524288, + "Sequence": 1003, + "LastLedgerSequence": 6220135 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0007" + ], + "json": "OfferCreate", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00080000" + ], + "json": 524288, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "000003EB" + ], + "json": 1003, + "field_header": "24" + } + ], + [ + "OfferSequence", + { + "binary": [ + "000003EA" + ], + "json": 1002, + "field_header": "2019" + } + ], + [ + "LastLedgerSequence", + { + "binary": [ + "005EE967" + ], + "json": 6220135, + "field_header": "201B" + } + ], + [ + "TakerPays", + { + "binary": [ + "400000000D4D5FFA" + ], + "json": "223174650", + "field_header": "64" + } + ], + [ + "TakerGets", + { + "binary": [ + "D5038D7EA4C68000", + "000000000000000000000000584D4D0000000000", + "A426093A78AA86EB2B878E5C2E33FEC224A01849" + ], + "json": { + "currency": "XMM", + "value": "100", + "issuer": "rExAPEZvbkZqYPuNcZ7XEBLENEshsWDQc8" + }, + "field_header": "65" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000F" + ], + "json": "15", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "F990B9E746546554A7B50A5E013BCB57095C6BB8" + ], + "vl_length": "14", + "json": "rPk2dXr27rMw9G5Ej9ad2Tt7RJzGy8ycBp", + "field_header": "81" + } + ], + [ + "Memos", + { + "binary": [ + "EA", + "7C", + "09", + "584D4D2076616C7565", + "7D", + "07", + "322E3230393635", + "E1" + ], + "json": [ + { + "Memo": { + "MemoType": "584D4D2076616C7565", + "MemoData": "322E3230393635" + } + } + ], + "field_header": "F9" + } + ] + ], + "blob_with_no_signing": "120007220008000024000003EB2019000003EA201B005EE96764400000000D4D5FFA65D5038D7EA4C68000000000000000000000000000584D4D0000000000A426093A78AA86EB2B878E5C2E33FEC224A0184968400000000000000F8114F990B9E746546554A7B50A5E013BCB57095C6BB8F9EA7C09584D4D2076616C75657D07322E3230393635E1F1" + }, + { + "tx_json": { + "TakerPays": { + "currency": "BTC", + "value": "0.01262042643559221", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + "Account": "rPEZyTnSyQyXBCwMVYyaafSVPL8oMtfG6a", + "TransactionType": "OfferCreate", + "Fee": "50", + "OfferSequence": 526554, + "TakerGets": "1010386370", + "Sequence": 526615 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0007" + ], + "json": "OfferCreate", + "field_header": "12" + } + ], + [ + "Sequence", + { + "binary": [ + "00080917" + ], + "json": 526615, + "field_header": "24" + } + ], + [ + "OfferSequence", + { + "binary": [ + "000808DA" + ], + "json": 526554, + "field_header": "2019" + } + ], + [ + "TakerPays", + { + "binary": [ + "D4047BD23375F335", + "0000000000000000000000004254430000000000", + "0A20B3C85F482532A9578DBB3950B85CA06594D1" + ], + "json": { + "currency": "BTC", + "value": "0.01262042643559221", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + "field_header": "64" + } + ], + [ + "TakerGets", + { + "binary": [ + "400000003C3945C2" + ], + "json": "1010386370", + "field_header": "65" + } + ], + [ + "Fee", + { + "binary": [ + "4000000000000032" + ], + "json": "50", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "F4141D8B4EF33BC3EE224088CA418DFCD2847193" + ], + "vl_length": "14", + "json": "rPEZyTnSyQyXBCwMVYyaafSVPL8oMtfG6a", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "12000724000809172019000808DA64D4047BD23375F33500000000000000000000000042544300000000000A20B3C85F482532A9578DBB3950B85CA06594D165400000003C3945C26840000000000000328114F4141D8B4EF33BC3EE224088CA418DFCD2847193" + }, + { + "tx_json": { + "Account": "rHXUjUtk5eiPFYpg27izxHeZ1t4x835Ecn", + "Destination": "r45dBj4S3VvMMYXxr9vHX4Z4Ma6ifPMCkK", + "TransactionType": "Payment", + "Amount": { + "currency": "CNY", + "value": "5000", + "issuer": "r45dBj4S3VvMMYXxr9vHX4Z4Ma6ifPMCkK" + }, + "Fee": "12", + "SendMax": { + "currency": "CNY", + "value": "5050", + "issuer": "rHXUjUtk5eiPFYpg27izxHeZ1t4x835Ecn" + }, + "Flags": 0, + "Sequence": 6, + "Paths": [ + [ + { + "account": "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA" + } + ] + ], + "DestinationTag": 736049272 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0000" + ], + "json": "Payment", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00000006" + ], + "json": 6, + "field_header": "24" + } + ], + [ + "DestinationTag", + { + "binary": [ + "2BDF3878" + ], + "json": 736049272, + "field_header": "2E" + } + ], + [ + "Amount", + { + "binary": [ + "D551C37937E08000", + "000000000000000000000000434E590000000000", + "EE39E6D05CFD6A90DAB700A1D70149ECEE29DFEC" + ], + "json": { + "currency": "CNY", + "value": "5000", + "issuer": "r45dBj4S3VvMMYXxr9vHX4Z4Ma6ifPMCkK" + }, + "field_header": "61" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "SendMax", + { + "binary": [ + "D551F0F2C01DA000", + "000000000000000000000000434E590000000000", + "B53847FA45E828BF9A52E38F7FB39E363493CE8B" + ], + "json": { + "currency": "CNY", + "value": "5050", + "issuer": "rHXUjUtk5eiPFYpg27izxHeZ1t4x835Ecn" + }, + "field_header": "69" + } + ], + [ + "Account", + { + "binary": [ + "B53847FA45E828BF9A52E38F7FB39E363493CE8B" + ], + "vl_length": "14", + "json": "rHXUjUtk5eiPFYpg27izxHeZ1t4x835Ecn", + "field_header": "81" + } + ], + [ + "Destination", + { + "binary": [ + "EE39E6D05CFD6A90DAB700A1D70149ECEE29DFEC" + ], + "vl_length": "14", + "json": "r45dBj4S3VvMMYXxr9vHX4Z4Ma6ifPMCkK", + "field_header": "83" + } + ], + [ + "Paths", + { + "binary": [ + "01", + "41C8BE2C0A6AA17471B9F6D0AF92AAB1C94D5A25", + "00" + ], + "json": [ + [ + { + "account": "razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA" + } + ] + ], + "field_header": "0112" + } + ] + ], + "blob_with_no_signing": "120000220000000024000000062E2BDF387861D551C37937E08000000000000000000000000000434E590000000000EE39E6D05CFD6A90DAB700A1D70149ECEE29DFEC68400000000000000C69D551F0F2C01DA000000000000000000000000000434E590000000000B53847FA45E828BF9A52E38F7FB39E363493CE8B8114B53847FA45E828BF9A52E38F7FB39E363493CE8B8314EE39E6D05CFD6A90DAB700A1D70149ECEE29DFEC01120141C8BE2C0A6AA17471B9F6D0AF92AAB1C94D5A2500" + }, + { + "tx_json": { + "Account": "rP2jdgJhtY1pwDJQEMLfCixesg4cw8HcrW", + "Destination": "rHoUTGMxWKbrTTF8tpAjysjpu8PWrbt1Wx", + "TransactionType": "Payment", + "Amount": { + "currency": "RDD", + "value": "1150.848", + "issuer": "ra9eZxMbJrUcgV8ui7aPc161FgrqWScQxV" + }, + "Fee": "10", + "SendMax": { + "currency": "RDD", + "value": "1152", + "issuer": "ra9eZxMbJrUcgV8ui7aPc161FgrqWScQxV" + }, + "Flags": 2147483648, + "Sequence": 21703 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0000" + ], + "json": "Payment", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "80000000" + ], + "json": 2147483648, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "000054C7" + ], + "json": 21703, + "field_header": "24" + } + ], + [ + "Amount", + { + "binary": [ + "D54416B0AE3B0000", + "0000000000000000000000005244440000000000", + "387B5123A1C93417271BA6DBBBD087E68E7445B2" + ], + "json": { + "currency": "RDD", + "value": "1150.848", + "issuer": "ra9eZxMbJrUcgV8ui7aPc161FgrqWScQxV" + }, + "field_header": "61" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000A" + ], + "json": "10", + "field_header": "68" + } + ], + [ + "SendMax", + { + "binary": [ + "D54417BCE6C80000", + "0000000000000000000000005244440000000000", + "387B5123A1C93417271BA6DBBBD087E68E7445B2" + ], + "json": { + "currency": "RDD", + "value": "1152", + "issuer": "ra9eZxMbJrUcgV8ui7aPc161FgrqWScQxV" + }, + "field_header": "69" + } + ], + [ + "Account", + { + "binary": [ + "F7B414E9D25EE050553D8A0BB27202F4249AD328" + ], + "vl_length": "14", + "json": "rP2jdgJhtY1pwDJQEMLfCixesg4cw8HcrW", + "field_header": "81" + } + ], + [ + "Destination", + { + "binary": [ + "B83EB506BBE5BCF3E89C638FDB185B1DEAC96584" + ], + "vl_length": "14", + "json": "rHoUTGMxWKbrTTF8tpAjysjpu8PWrbt1Wx", + "field_header": "83" + } + ] + ], + "blob_with_no_signing": "120000228000000024000054C761D54416B0AE3B00000000000000000000000000005244440000000000387B5123A1C93417271BA6DBBBD087E68E7445B268400000000000000A69D54417BCE6C800000000000000000000000000005244440000000000387B5123A1C93417271BA6DBBBD087E68E7445B28114F7B414E9D25EE050553D8A0BB27202F4249AD3288314B83EB506BBE5BCF3E89C638FDB185B1DEAC96584" + }, + { + "tx_json": { + "Account": "r9TeThyi5xiuUUrFjtPKZiHcDxs7K9H6Rb", + "Destination": "r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C", + "TransactionType": "Payment", + "Amount": "25000000", + "Fee": "10", + "Flags": 0, + "Sequence": 2 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0000" + ], + "json": "Payment", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00000002" + ], + "json": 2, + "field_header": "24" + } + ], + [ + "Amount", + { + "binary": [ + "40000000017D7840" + ], + "json": "25000000", + "field_header": "61" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000A" + ], + "json": "10", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "5CCB151F6E9D603F394AE778ACF10D3BECE874F6" + ], + "vl_length": "14", + "json": "r9TeThyi5xiuUUrFjtPKZiHcDxs7K9H6Rb", + "field_header": "81" + } + ], + [ + "Destination", + { + "binary": [ + "E851BBBE79E328E43D68F43445368133DF5FBA5A" + ], + "vl_length": "14", + "json": "r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C", + "field_header": "83" + } + ] + ], + "blob_with_no_signing": "120000220000000024000000026140000000017D784068400000000000000A81145CCB151F6E9D603F394AE778ACF10D3BECE874F68314E851BBBE79E328E43D68F43445368133DF5FBA5A" + }, + { + "tx_json": { + "Account": "rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e", + "Destination": "rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ", + "TransactionType": "Payment", + "Amount": "200000", + "Fee": "15", + "Flags": 0, + "Sequence": 144, + "LastLedgerSequence": 6220218 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0000" + ], + "json": "Payment", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00000090" + ], + "json": 144, + "field_header": "24" + } + ], + [ + "LastLedgerSequence", + { + "binary": [ + "005EE9BA" + ], + "json": 6220218, + "field_header": "201B" + } + ], + [ + "Amount", + { + "binary": [ + "4000000000030D40" + ], + "json": "200000", + "field_header": "61" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000F" + ], + "json": "15", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "AA1BD19D9E87BE8069FDBF6843653C43837C03C6" + ], + "vl_length": "14", + "json": "rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e", + "field_header": "81" + } + ], + [ + "Destination", + { + "binary": [ + "67FE6EC28E0464DD24FB2D62A492AAC697CFAD02" + ], + "vl_length": "14", + "json": "rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ", + "field_header": "83" + } + ] + ], + "blob_with_no_signing": "12000022000000002400000090201B005EE9BA614000000000030D4068400000000000000F8114AA1BD19D9E87BE8069FDBF6843653C43837C03C6831467FE6EC28E0464DD24FB2D62A492AAC697CFAD02" + }, + { + "tx_json": { + "Account": "r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C", + "Destination": "rBqSFEFg2B6GBMobtxnU1eLA1zbNC9NDGM", + "TransactionType": "Payment", + "Amount": "25000000", + "Fee": "12", + "Flags": 0, + "Sequence": 1, + "DestinationTag": 4146942154 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0000" + ], + "json": "Payment", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00000001" + ], + "json": 1, + "field_header": "24" + } + ], + [ + "DestinationTag", + { + "binary": [ + "F72D50CA" + ], + "json": 4146942154, + "field_header": "2E" + } + ], + [ + "Amount", + { + "binary": [ + "40000000017D7840" + ], + "json": "25000000", + "field_header": "61" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "E851BBBE79E328E43D68F43445368133DF5FBA5A" + ], + "vl_length": "14", + "json": "r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C", + "field_header": "81" + } + ], + [ + "Destination", + { + "binary": [ + "76DAC5E814CD4AA74142C3AB45E69A900E637AA2" + ], + "vl_length": "14", + "json": "rBqSFEFg2B6GBMobtxnU1eLA1zbNC9NDGM", + "field_header": "83" + } + ] + ], + "blob_with_no_signing": "120000220000000024000000012EF72D50CA6140000000017D784068400000000000000C8114E851BBBE79E328E43D68F43445368133DF5FBA5A831476DAC5E814CD4AA74142C3AB45E69A900E637AA2" + }, + { + "tx_json": { + "Account": "rFLiPGytDEwC5heoqFcFAZoqPPmKBzX1o", + "Destination": "rBsbetvMYuMkEeHZYizPMkpveCVH8EVQYd", + "TransactionType": "Payment", + "Amount": "500000", + "Fee": "20", + "SourceTag": 668920, + "Sequence": 34954 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0000" + ], + "json": "Payment", + "field_header": "12" + } + ], + [ + "SourceTag", + { + "binary": [ + "000A34F8" + ], + "json": 668920, + "field_header": "23" + } + ], + [ + "Sequence", + { + "binary": [ + "0000888A" + ], + "json": 34954, + "field_header": "24" + } + ], + [ + "Amount", + { + "binary": [ + "400000000007A120" + ], + "json": "500000", + "field_header": "61" + } + ], + [ + "Fee", + { + "binary": [ + "4000000000000014" + ], + "json": "20", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "08F41F116A1F60D60296B16907F0A041BF106197" + ], + "vl_length": "14", + "json": "rFLiPGytDEwC5heoqFcFAZoqPPmKBzX1o", + "field_header": "81" + } + ], + [ + "Destination", + { + "binary": [ + "6E2F0455C46CF5DF61A1E58419A89D45459045EA" + ], + "vl_length": "14", + "json": "rBsbetvMYuMkEeHZYizPMkpveCVH8EVQYd", + "field_header": "83" + } + ] + ], + "blob_with_no_signing": "12000023000A34F8240000888A61400000000007A120684000000000000014811408F41F116A1F60D60296B16907F0A041BF10619783146E2F0455C46CF5DF61A1E58419A89D45459045EA" + }, + { + "tx_json": { + "Account": "r3ZDv3hLmTKwkgAqcXtX2yaMfnhRD3Grjc", + "Destination": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "TransactionType": "Payment", + "Amount": { + "currency": "BTC", + "value": "0.04", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q" + }, + "Fee": "106", + "SendMax": "3267350000", + "Flags": 0, + "Sequence": 10, + "Paths": [ + [ + { + "currency": "BTC", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q" + } + ] + ], + "InvoiceID": "342B8D16BEE494D169034AFF0908FDE35874A38E548D4CEC8DFC5C49E9A33B76", + "DestinationTag": 1403334172 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0000" + ], + "json": "Payment", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "0000000A" + ], + "json": 10, + "field_header": "24" + } + ], + [ + "DestinationTag", + { + "binary": [ + "53A52E1C" + ], + "json": 1403334172, + "field_header": "2E" + } + ], + [ + "InvoiceID", + { + "binary": [ + "342B8D16BEE494D169034AFF0908FDE35874A38E548D4CEC8DFC5C49E9A33B76" + ], + "json": "342B8D16BEE494D169034AFF0908FDE35874A38E548D4CEC8DFC5C49E9A33B76", + "field_header": "5011" + } + ], + [ + "Amount", + { + "binary": [ + "D40E35FA931A0000", + "0000000000000000000000004254430000000000", + "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3" + ], + "json": { + "currency": "BTC", + "value": "0.04", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q" + }, + "field_header": "61" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000006A" + ], + "json": "106", + "field_header": "68" + } + ], + [ + "SendMax", + { + "binary": [ + "40000000C2BFCDF0" + ], + "json": "3267350000", + "field_header": "69" + } + ], + [ + "Account", + { + "binary": [ + "52E0F910686FB449A23BC78C3D4CE564C988C6C0" + ], + "vl_length": "14", + "json": "r3ZDv3hLmTKwkgAqcXtX2yaMfnhRD3Grjc", + "field_header": "81" + } + ], + [ + "Destination", + { + "binary": [ + "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3" + ], + "vl_length": "14", + "json": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q", + "field_header": "83" + } + ], + [ + "Paths", + { + "binary": [ + "30", + "0000000000000000000000004254430000000000", + "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3", + "00" + ], + "json": [ + [ + { + "currency": "BTC", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q" + } + ] + ], + "field_header": "0112" + } + ] + ], + "blob_with_no_signing": "1200002200000000240000000A2E53A52E1C5011342B8D16BEE494D169034AFF0908FDE35874A38E548D4CEC8DFC5C49E9A33B7661D40E35FA931A00000000000000000000000000004254430000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD368400000000000006A6940000000C2BFCDF0811452E0F910686FB449A23BC78C3D4CE564C988C6C08314DD39C650A96EDA48334E70CC4A85B8B2E8502CD30112300000000000000000000000004254430000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD300" + }, + { + "tx_json": { + "Account": "rLpW9Reyn9YqZ8mxbq8nviXSp4TnHafVJQ", + "TransactionType": "OfferCancel", + "Fee": "12", + "OfferSequence": 20763, + "Flags": 0, + "Sequence": 20769, + "LastLedgerSequence": 6220009 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0008" + ], + "json": "OfferCancel", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00005121" + ], + "json": 20769, + "field_header": "24" + } + ], + [ + "OfferSequence", + { + "binary": [ + "0000511B" + ], + "json": 20763, + "field_header": "2019" + } + ], + [ + "LastLedgerSequence", + { + "binary": [ + "005EE8E9" + ], + "json": 6220009, + "field_header": "201B" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "D0B32295596E50017E246FE85FC5982A1BD89CE4" + ], + "vl_length": "14", + "json": "rLpW9Reyn9YqZ8mxbq8nviXSp4TnHafVJQ", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "1200082200000000240000512120190000511B201B005EE8E968400000000000000C8114D0B32295596E50017E246FE85FC5982A1BD89CE4" + }, + { + "tx_json": { + "Account": "rfeMWWbSaGqc6Yth2dTetLBeKeUTTfE2pG", + "TransactionType": "SetRegularKey", + "Fee": "10", + "Flags": 2147483648, + "Sequence": 3, + "RegularKey": "rfeMWWbSaGqc6Yth2dTetLBeKeUTTfE2pG" + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0005" + ], + "json": "SetRegularKey", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "80000000" + ], + "json": 2147483648, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00000003" + ], + "json": 3, + "field_header": "24" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000A" + ], + "json": "10", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "48E143E2384A1B3C69A412789F2CA3FCE2F65F0B" + ], + "vl_length": "14", + "json": "rfeMWWbSaGqc6Yth2dTetLBeKeUTTfE2pG", + "field_header": "81" + } + ], + [ + "RegularKey", + { + "binary": [ + "48E143E2384A1B3C69A412789F2CA3FCE2F65F0B" + ], + "vl_length": "14", + "json": "rfeMWWbSaGqc6Yth2dTetLBeKeUTTfE2pG", + "field_header": "88" + } + ] + ], + "blob_with_no_signing": "1200052280000000240000000368400000000000000A811448E143E2384A1B3C69A412789F2CA3FCE2F65F0B881448E143E2384A1B3C69A412789F2CA3FCE2F65F0B" + }, + { + "tx_json": { + "Account": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz", + "TransactionType": "SetRegularKey", + "Fee": "12", + "Flags": 2147483648, + "Sequence": 238, + "RegularKey": "rP9jbfTepHAHWB4q9YjNkLyaZT15uvexiZ", + "LastLedgerSequence": 6224204 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0005" + ], + "json": "SetRegularKey", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "80000000" + ], + "json": 2147483648, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "000000EE" + ], + "json": 238, + "field_header": "24" + } + ], + [ + "LastLedgerSequence", + { + "binary": [ + "005EF94C" + ], + "json": 6224204, + "field_header": "201B" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "CB3F392892D0772FF5AD155D8D70404B1DB2ACFE" + ], + "vl_length": "14", + "json": "rKXCummUHnenhYudNb9UoJ4mGBR75vFcgz", + "field_header": "81" + } + ], + [ + "RegularKey", + { + "binary": [ + "F2F9A54D9CEBBE64342B52DE3450FFA0738C8D00" + ], + "vl_length": "14", + "json": "rP9jbfTepHAHWB4q9YjNkLyaZT15uvexiZ", + "field_header": "88" + } + ] + ], + "blob_with_no_signing": "120005228000000024000000EE201B005EF94C68400000000000000C8114CB3F392892D0772FF5AD155D8D70404B1DB2ACFE8814F2F9A54D9CEBBE64342B52DE3450FFA0738C8D00" + }, + { + "tx_json": { + "Account": "rJMiz2rCMjZzEMijXNH1exNBryTQEjFd9S", + "TransactionType": "TrustSet", + "LimitAmount": { + "currency": "WCG", + "value": "10000000", + "issuer": "rUx4xgE7bNWCCgGcXv1CCoQyTcCeZ275YG" + }, + "Fee": "12", + "Flags": 131072, + "Sequence": 44 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0014" + ], + "json": "TrustSet", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00020000" + ], + "json": 131072, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "0000002C" + ], + "json": 44, + "field_header": "24" + } + ], + [ + "LimitAmount", + { + "binary": [ + "D6438D7EA4C68000", + "0000000000000000000000005743470000000000", + "832297BEF589D59F9C03A84F920F8D9128CC1CE4" + ], + "json": { + "currency": "WCG", + "value": "10000000", + "issuer": "rUx4xgE7bNWCCgGcXv1CCoQyTcCeZ275YG" + }, + "field_header": "63" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "BE6C30732AE33CF2AF3344CE8172A6B9300183E3" + ], + "vl_length": "14", + "json": "rJMiz2rCMjZzEMijXNH1exNBryTQEjFd9S", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "1200142200020000240000002C63D6438D7EA4C680000000000000000000000000005743470000000000832297BEF589D59F9C03A84F920F8D9128CC1CE468400000000000000C8114BE6C30732AE33CF2AF3344CE8172A6B9300183E3" + }, + { + "tx_json": { + "Account": "rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN", + "TransactionType": "TrustSet", + "LimitAmount": { + "currency": "BTC", + "value": "1", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q" + }, + "Fee": "12", + "Flags": 2147614720, + "Sequence": 43, + "LastLedgerSequence": 6220463 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0014" + ], + "json": "TrustSet", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "80020000" + ], + "json": 2147614720, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "0000002B" + ], + "json": 43, + "field_header": "24" + } + ], + [ + "LastLedgerSequence", + { + "binary": [ + "005EEAAF" + ], + "json": 6220463, + "field_header": "201B" + } + ], + [ + "LimitAmount", + { + "binary": [ + "D4838D7EA4C68000", + "0000000000000000000000004254430000000000", + "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3" + ], + "json": { + "currency": "BTC", + "value": "1", + "issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q" + }, + "field_header": "63" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000C" + ], + "json": "12", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "8353C031DF5AA061A23535E6ABCEEEA23F152B1E" + ], + "vl_length": "14", + "json": "rUyPiNcSFFj6uMR2gEaD8jUerQ59G1qvwN", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "1200142280020000240000002B201B005EEAAF63D4838D7EA4C680000000000000000000000000004254430000000000DD39C650A96EDA48334E70CC4A85B8B2E8502CD368400000000000000C81148353C031DF5AA061A23535E6ABCEEEA23F152B1E" + }, + { + "tx_json": { + "Account": "rpP2GdsQwenNnFPefbXFgiTvEgJWQpq8Rw", + "TransactionType": "AccountSet", + "Fee": "10", + "Flags": 0, + "Sequence": 10598 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0003" + ], + "json": "AccountSet", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00002966" + ], + "json": 10598, + "field_header": "24" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000A" + ], + "json": "10", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "0F3D0C7D2CFAB2EC8295451F0B3CA038E8E9CDCD" + ], + "vl_length": "14", + "json": "rpP2GdsQwenNnFPefbXFgiTvEgJWQpq8Rw", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "1200032200000000240000296668400000000000000A81140F3D0C7D2CFAB2EC8295451F0B3CA038E8E9CDCD" + }, + { + "tx_json": { + "Account": "rGCnJuD31Kx4QGZJ2dX7xoje6T4Zr5s9EB", + "TransactionType": "AccountSet", + "Fee": "15", + "Flags": 0, + "Sequence": 290, + "LastLedgerSequence": 6221014 + }, + "fields": [ + [ + "TransactionType", + { + "binary": [ + "0003" + ], + "json": "AccountSet", + "field_header": "12" + } + ], + [ + "Flags", + { + "binary": [ + "00000000" + ], + "json": 0, + "field_header": "22" + } + ], + [ + "Sequence", + { + "binary": [ + "00000122" + ], + "json": 290, + "field_header": "24" + } + ], + [ + "LastLedgerSequence", + { + "binary": [ + "005EECD6" + ], + "json": 6221014, + "field_header": "201B" + } + ], + [ + "Fee", + { + "binary": [ + "400000000000000F" + ], + "json": "15", + "field_header": "68" + } + ], + [ + "Account", + { + "binary": [ + "ABBD4A3AF95FDFD6D072F11421D8F107CAEA1852" + ], + "vl_length": "14", + "json": "rGCnJuD31Kx4QGZJ2dX7xoje6T4Zr5s9EB", + "field_header": "81" + } + ] + ], + "blob_with_no_signing": "12000322000000002400000122201B005EECD668400000000000000F8114ABBD4A3AF95FDFD6D072F11421D8F107CAEA1852" + } + ], + "values_tests": [ + { + "test_json": "0", + "type_id": 6, + "is_native": true, + "type": "Amount", + "expected_hex": "4000000000000000", + "is_negative": false + }, + { + "test_json": "1", + "type_id": 6, + "is_native": true, + "type": "Amount", + "expected_hex": "4000000000000001", + "is_negative": false + }, + { + "test_json": { + "currency": "USD", + "value": "-1", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "00038D7EA4C68000", + "type": "Amount", + "expected_hex": "94838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": true, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "1", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "00038D7EA4C68000", + "type": "Amount", + "expected_hex": "D4838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "0000000000000000", + "type": "Amount", + "expected_hex": "800000000000000000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "-1.0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "00038D7EA4C68000", + "type": "Amount", + "expected_hex": "94838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": true, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "1.0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "00038D7EA4C68000", + "type": "Amount", + "expected_hex": "D4838D7EA4C6800000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "0.0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 1, + "type_id": 6, + "is_native": false, + "mantissa": "0000000000000000", + "type": "Amount", + "expected_hex": "800000000000000000000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "1.111111111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D483F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -15 + }, + { + "test_json": { + "currency": "USD", + "value": "11.11111111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D4C3F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -14 + }, + { + "test_json": { + "currency": "USD", + "value": "111.1111111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D503F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -13 + }, + { + "test_json": { + "currency": "USD", + "value": "1111.111111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D543F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -12 + }, + { + "test_json": { + "currency": "USD", + "value": "11111.11111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D583F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -11 + }, + { + "test_json": { + "currency": "USD", + "value": "111111.1111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D5C3F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -10 + }, + { + "test_json": { + "currency": "USD", + "value": "1111111.111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D603F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -9 + }, + { + "test_json": { + "currency": "USD", + "value": "11111111.11111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D643F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -8 + }, + { + "test_json": { + "currency": "USD", + "value": "111111111.1111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D683F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -7 + }, + { + "test_json": { + "currency": "USD", + "value": "1111111111.111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D6C3F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -6 + }, + { + "test_json": { + "currency": "USD", + "value": "11111111111.11111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D703F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -5 + }, + { + "test_json": { + "currency": "USD", + "value": "111111111111.1111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D743F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -4 + }, + { + "test_json": { + "currency": "USD", + "value": "1111111111111.111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D783F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -3 + }, + { + "test_json": { + "currency": "USD", + "value": "11111111111111.11", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D7C3F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -2 + }, + { + "test_json": { + "currency": "USD", + "value": "111111111111111.1", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D803F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -1 + }, + { + "test_json": { + "currency": "USD", + "value": "1111111111111111.0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28CB71571C7", + "type": "Amount", + "expected_hex": "D843F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": 0 + }, + { + "test_json": { + "currency": "USD", + "value": "1111111111111111.1", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 17, + "type_id": 6, + "is_native": false, + "type": "Amount", + "error": "value precision of 17 is greater than maximum iou precision of 16", + "is_negative": false + }, + { + "test_json": { + "currency": "USD", + "value": "9999999999999999000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "type": "Amount", + "error": "exponent is too large" + }, + { + "test_json": { + "currency": "USD", + "value": "11111000.00000001", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "significant_digits": 16, + "type_id": 6, + "is_native": false, + "mantissa": "0003F28A20CF5801", + "type": "Amount", + "expected_hex": "D643F28A20CF580100000000000000000000000055534400000000000000000000000000000000000000000000000001", + "is_negative": false, + "exponent": -8 + }, + { + "test_json": "1000000000000000000", + "type_id": 6, + "is_native": true, + "type": "Amount", + "error": "1000000000000 absolute XRP is bigger than max native value 100000000000.0", + "is_negative": false + }, + { + "test_json": "-10000000000000000000000000", + "type_id": 6, + "is_native": true, + "type": "Amount", + "error": "10000000000000000000 absolute XRP is bigger than max native value 100000000000.0", + "is_negative": true + }, + { + "test_json": 0, + "canonical_json": "Payment", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0000" + }, + { + "test_json": "EscrowCreate", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0001" + }, + { + "test_json": 1, + "canonical_json": "EscrowCreate", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0001" + }, + { + "test_json": "EscrowFinish", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0002" + }, + { + "test_json": 2, + "canonical_json": "EscrowFinish", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0002" + }, + { + "test_json": "AccountSet", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0003" + }, + { + "test_json": 3, + "canonical_json": "AccountSet", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0003" + }, + { + "test_json": "EscrowCancel", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0004" + }, + { + "test_json": 4, + "canonical_json": "EscrowCancel", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0004" + }, + { + "test_json": "SetRegularKey", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0005" + }, + { + "test_json": 5, + "canonical_json": "SetRegularKey", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0005" + }, + { + "test_json": "NickNameSet", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0006" + }, + { + "test_json": 6, + "canonical_json": "NickNameSet", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0006" + }, + { + "test_json": "OfferCreate", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0007" + }, + { + "test_json": 7, + "canonical_json": "OfferCreate", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0007" + }, + { + "test_json": "OfferCancel", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0008" + }, + { + "test_json": 8, + "canonical_json": "OfferCancel", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0008" + }, + { + "test_json": "Contract", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0009" + }, + { + "test_json": 9, + "canonical_json": "Contract", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0009" + }, + { + "test_json": "TicketCreate", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "000A" + }, + { + "test_json": 10, + "canonical_json": "TicketCreate", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "000A" + }, + { + "test_json": "TicketCancel", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "000B" + }, + { + "test_json": 11, + "canonical_json": "TicketCancel", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "000B" + }, + { + "test_json": "TrustSet", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0014" + }, + { + "test_json": 20, + "canonical_json": "TrustSet", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0014" + }, + { + "test_json": "EnableAmendment", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0064" + }, + { + "test_json": 100, + "canonical_json": "EnableAmendment", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0064" + }, + { + "test_json": "SetFee", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0065" + }, + { + "test_json": 101, + "canonical_json": "SetFee", + "type_id": 1, + "type_specialisation_field": "TransactionType", + "type": "UInt16", + "expected_hex": "0065" + }, + { + "test_json": "AccountRoot", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0061" + }, + { + "test_json": 97, + "canonical_json": "AccountRoot", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0061" + }, + { + "test_json": "DirectoryNode", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0064" + }, + { + "test_json": 100, + "canonical_json": "DirectoryNode", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0064" + }, + { + "test_json": "GeneratorMap", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0067" + }, + { + "test_json": 103, + "canonical_json": "GeneratorMap", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0067" + }, + { + "test_json": "RippleState", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0072" + }, + { + "test_json": 114, + "canonical_json": "RippleState", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0072" + }, + { + "test_json": "Offer", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "006F" + }, + { + "test_json": 111, + "canonical_json": "Offer", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "006F" + }, + { + "test_json": "Contract", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0063" + }, + { + "test_json": 99, + "canonical_json": "Contract", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0063" + }, + { + "test_json": "LedgerHashes", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0068" + }, + { + "test_json": 104, + "canonical_json": "LedgerHashes", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0068" + }, + { + "test_json": "Amendments", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0066" + }, + { + "test_json": 102, + "canonical_json": "Amendments", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0066" + }, + { + "test_json": "FeeSettings", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0073" + }, + { + "test_json": 115, + "canonical_json": "FeeSettings", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0073" + }, + { + "test_json": "Ticket", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0054" + }, + { + "test_json": 84, + "canonical_json": "Ticket", + "type_id": 1, + "type_specialisation_field": "LedgerEntryType", + "type": "UInt16", + "expected_hex": "0054" + }, + { + "test_json": "tesSUCCESS", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "00" + }, + { + "test_json": 0, + "canonical_json": "tesSUCCESS", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "00" + }, + { + "test_json": "tecCLAIM", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "64" + }, + { + "test_json": 100, + "canonical_json": "tecCLAIM", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "64" + }, + { + "test_json": "tecPATH_PARTIAL", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "65" + }, + { + "test_json": 101, + "canonical_json": "tecPATH_PARTIAL", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "65" + }, + { + "test_json": "tecUNFUNDED_ADD", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "66" + }, + { + "test_json": 102, + "canonical_json": "tecUNFUNDED_ADD", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "66" + }, + { + "test_json": "tecUNFUNDED_OFFER", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "67" + }, + { + "test_json": 103, + "canonical_json": "tecUNFUNDED_OFFER", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "67" + }, + { + "test_json": "tecUNFUNDED_PAYMENT", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "68" + }, + { + "test_json": 104, + "canonical_json": "tecUNFUNDED_PAYMENT", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "68" + }, + { + "test_json": "tecFAILED_PROCESSING", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "69" + }, + { + "test_json": 105, + "canonical_json": "tecFAILED_PROCESSING", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "69" + }, + { + "test_json": "tecDIR_FULL", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "79" + }, + { + "test_json": 121, + "canonical_json": "tecDIR_FULL", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "79" + }, + { + "test_json": "tecINSUF_RESERVE_LINE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7A" + }, + { + "test_json": 122, + "canonical_json": "tecINSUF_RESERVE_LINE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7A" + }, + { + "test_json": "tecINSUF_RESERVE_OFFER", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7B" + }, + { + "test_json": 123, + "canonical_json": "tecINSUF_RESERVE_OFFER", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7B" + }, + { + "test_json": "tecNO_DST", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7C" + }, + { + "test_json": 124, + "canonical_json": "tecNO_DST", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7C" + }, + { + "test_json": "tecNO_DST_INSUF_XRP", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7D" + }, + { + "test_json": 125, + "canonical_json": "tecNO_DST_INSUF_XRP", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7D" + }, + { + "test_json": "tecNO_LINE_INSUF_RESERVE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7E" + }, + { + "test_json": 126, + "canonical_json": "tecNO_LINE_INSUF_RESERVE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7E" + }, + { + "test_json": "tecNO_LINE_REDUNDANT", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7F" + }, + { + "test_json": 127, + "canonical_json": "tecNO_LINE_REDUNDANT", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "7F" + }, + { + "test_json": "tecPATH_DRY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "80" + }, + { + "test_json": 128, + "canonical_json": "tecPATH_DRY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "80" + }, + { + "test_json": "tecUNFUNDED", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "81" + }, + { + "test_json": 129, + "canonical_json": "tecUNFUNDED", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "81" + }, + { + "test_json": "tecNO_ALTERNATIVE_KEY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "82" + }, + { + "test_json": 130, + "canonical_json": "tecNO_ALTERNATIVE_KEY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "82" + }, + { + "test_json": "tecNO_REGULAR_KEY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "83" + }, + { + "test_json": 131, + "canonical_json": "tecNO_REGULAR_KEY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "83" + }, + { + "test_json": "tecOWNERS", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "84" + }, + { + "test_json": 132, + "canonical_json": "tecOWNERS", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "84" + }, + { + "test_json": "tecNO_ISSUER", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "85" + }, + { + "test_json": 133, + "canonical_json": "tecNO_ISSUER", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "85" + }, + { + "test_json": "tecNO_AUTH", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "86" + }, + { + "test_json": 134, + "canonical_json": "tecNO_AUTH", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "86" + }, + { + "test_json": "tecNO_LINE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "87" + }, + { + "test_json": 135, + "canonical_json": "tecNO_LINE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "87" + }, + { + "test_json": "tecINSUFF_FEE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "88" + }, + { + "test_json": 136, + "canonical_json": "tecINSUFF_FEE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "88" + }, + { + "test_json": "tecFROZEN", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "89" + }, + { + "test_json": 137, + "canonical_json": "tecFROZEN", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "89" + }, + { + "test_json": "tecNO_TARGET", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8A" + }, + { + "test_json": 138, + "canonical_json": "tecNO_TARGET", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8A" + }, + { + "test_json": "tecNO_PERMISSION", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8B" + }, + { + "test_json": 139, + "canonical_json": "tecNO_PERMISSION", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8B" + }, + { + "test_json": "tecNO_ENTRY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8C" + }, + { + "test_json": 140, + "canonical_json": "tecNO_ENTRY", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8C" + }, + { + "test_json": "tecINSUFFICIENT_RESERVE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8D" + }, + { + "test_json": 141, + "canonical_json": "tecINSUFFICIENT_RESERVE", + "type_id": 16, + "type_specialisation_field": "TransactionResult", + "type": "UInt8", + "expected_hex": "8D" + } + ] +} diff --git a/src/core/test_data/iou-tests.json b/src/core/test_data/iou-tests.json new file mode 100644 index 00000000..3116e763 --- /dev/null +++ b/src/core/test_data/iou-tests.json @@ -0,0 +1,58 @@ +[ + [ + { + "value": "0", + "currency": "USD", + "issuer": "rDgZZ3wyprx4ZqrGQUkquE9Fs2Xs8XBcdw" + }, + "800000000000000000000000000000000000000055534400000000008B1CE810C13D6F337DAC85863B3D70265A24DF44" + ], + [ + { + "value": "1", + "currency": "USD", + "issuer": "rDgZZ3wyprx4ZqrGQUkquE9Fs2Xs8XBcdw" + }, + "D4838D7EA4C6800000000000000000000000000055534400000000008B1CE810C13D6F337DAC85863B3D70265A24DF44" + ], + [ + { + "value": "2", + "currency": "USD", + "issuer": "rDgZZ3wyprx4ZqrGQUkquE9Fs2Xs8XBcdw" + }, + "D4871AFD498D000000000000000000000000000055534400000000008B1CE810C13D6F337DAC85863B3D70265A24DF44" + ], + [ + { + "value": "-2", + "currency": "USD", + "issuer": "rDgZZ3wyprx4ZqrGQUkquE9Fs2Xs8XBcdw" + }, + "94871AFD498D000000000000000000000000000055534400000000008B1CE810C13D6F337DAC85863B3D70265A24DF44" + ], + [ + { + "value": "2.1", + "currency": "USD", + "issuer": "rDgZZ3wyprx4ZqrGQUkquE9Fs2Xs8XBcdw" + }, + "D48775F05A07400000000000000000000000000055534400000000008B1CE810C13D6F337DAC85863B3D70265A24DF44" + ], + [ + { + "currency": "XRP", + "value": "2.1", + "issuer": "rrrrrrrrrrrrrrrrrrrrrhoLvTp" + }, + "D48775F05A07400000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + [ + { + "currency": "USD", + "value": "1111111111111111", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "D843F28CB71571C700000000000000000000000055534400000000000000000000000000000000000000000000000001" + ] +] diff --git a/src/core/test_data/path-set-test.json b/src/core/test_data/path-set-test.json new file mode 100644 index 00000000..22ca12f5 --- /dev/null +++ b/src/core/test_data/path-set-test.json @@ -0,0 +1,63 @@ +[ + [ + { + "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "currency": "BTC", + "issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K" + }, + { + "account": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo", + "currency": "BTC", + "issuer": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo" + }, + { + "account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "currency": "BTC", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + }, + { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + ], + [ + { + "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "currency": "BTC", + "issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K" + }, + { + "account": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo", + "currency": "BTC", + "issuer": "rM1oqKtfh1zgjdAgbFmaRm3btfGBX25xVo" + }, + { + "account": "rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi", + "currency": "BTC", + "issuer": "rpvfJ4mR6QQAeogpXEKnuyGBx8mYCSnYZi" + }, + { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + ], + [ + { + "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "currency": "BTC", + "issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K" + }, + { + "account": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn", + "currency": "BTC", + "issuer": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn" + }, + { + "currency": "XRP" + }, + { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } + ] +] diff --git a/src/core/test_data/path-test.json b/src/core/test_data/path-test.json new file mode 100644 index 00000000..07e9b192 --- /dev/null +++ b/src/core/test_data/path-test.json @@ -0,0 +1,19 @@ +[ + { + "account": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K", + "currency": "BTC", + "issuer": "r9hEDb4xBGRfBCcX3E4FirDWQBAYtpxC8K" + }, + { + "account": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn", + "currency": "BTC", + "issuer": "r3AWbdp2jQLXLywJypdoNwVSvr81xs3uhn" + }, + { + "currency": "XRP" + }, + { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B" + } +] diff --git a/src/core/test_data/x-codec-fixtures.json b/src/core/test_data/x-codec-fixtures.json new file mode 100644 index 00000000..5408b721 --- /dev/null +++ b/src/core/test_data/x-codec-fixtures.json @@ -0,0 +1,232 @@ +{ + "transactions": [{ + "rjson": { + "Account": "r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV", + "Destination": "rLQBHVhFnaC5gLEkgr6HgBJJ3bgeZHg9cj", + "TransactionType": "Payment", + "TxnSignature": "3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639", + "SigningPubKey": "034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E", + "Amount": "10000000000", + "DestinationTag": 1010, + "SourceTag": 84854, + "Fee": "10", + "Flags": 0, + "Sequence": 62 + }, + "xjson": { + "Account": "X7tFPvjMH7nDxP8nTGkeeggcUpCZj8UbyT2QoiRHGDfjqrB", + "Destination": "XVYmGpJqHS95ir411XvanwY1xt5Z2314WsamHPVgUNABUGV", + "TransactionType": "Payment", + "TxnSignature": "3045022022EB32AECEF7C644C891C19F87966DF9C62B1F34BABA6BE774325E4BB8E2DD62022100A51437898C28C2B297112DF8131F2BB39EA5FE613487DDD611525F1796264639", + "SigningPubKey": "034AADB09CFF4A4804073701EC53C3510CDC95917C2BB0150FB742D0C66E6CEE9E", + "Amount": "10000000000", + "Fee": "10", + "Flags": 0, + "Sequence": 62 + } + }, + { + "rjson": { + "Account": "r4DymtkgUAh2wqRxVfdd3Xtswzim6eC6c5", + "Amount": "199000000", + "Destination": "rsekGH9p9neiPxym2TMJhqaCzHFuokenTU", + "DestinationTag": 3663729509, + "Fee": "6335", + "Flags": 2147483648, + "LastLedgerSequence": 57313352, + "Sequence": 105791, + "SigningPubKey": "02053A627976CE1157461336AC65290EC1571CAAD1B327339980F7BF65EF776F83", + "TransactionType": "Payment", + "TxnSignature": "30440220086D3330CD6CE01D891A26BA0355D8D5A5D28A5C9A1D0C5E06E321C81A02318A0220027C3F6606E41FEA35103EDE5224CC489B6514ACFE27543185B0419DD02E301C" + }, + "xjson": { + "Account": "r4DymtkgUAh2wqRxVfdd3Xtswzim6eC6c5", + "Amount": "199000000", + "Destination": "X7cBoj6a5xSEfPCr6AStN9YPhbMAA2yaN2XYWwRJKAKb3y5", + "Fee": "6335", + "Flags": 2147483648, + "LastLedgerSequence": 57313352, + "Sequence": 105791, + "SigningPubKey": "02053A627976CE1157461336AC65290EC1571CAAD1B327339980F7BF65EF776F83", + "TransactionType": "Payment", + "TxnSignature": "30440220086D3330CD6CE01D891A26BA0355D8D5A5D28A5C9A1D0C5E06E321C81A02318A0220027C3F6606E41FEA35103EDE5224CC489B6514ACFE27543185B0419DD02E301C" + } + }, + { + "rjson": { + "Account": "rDsbeomae4FXwgQTJp9Rs64Qg9vDiTCdBv", + "Amount": "105302107", + "Destination": "r33hypJXDs47LVpmvta7hMW9pR8DYeBtkW", + "DestinationTag": 1658156118, + "Fee": "60000", + "Flags": 2147483648, + "LastLedgerSequence": 57313566, + "Sequence": 1113196, + "SigningPubKey": "03D847C2DBED3ABF0453F71DCD7641989136277218DF516AD49519C9693F32727E", + "TransactionType": "Payment", + "TxnSignature": "3045022100FCA10FBAC65EA60C115A970CD52E6A526B1F9DDB6C4F843DA3DE7A97DFF9492D022037824D0FC6F663FB08BE0F2812CBADE1F61836528D44945FC37F10CC03215111" + }, + "xjson": { + "Account": "rDsbeomae4FXwgQTJp9Rs64Qg9vDiTCdBv", + "Amount": "105302107", + "Destination": "X7ikFY5asEwp6ikt2AJdTfBLALEs5JN35kkeqKVeT1GdvY1", + "Fee": "60000", + "Flags": 2147483648, + "LastLedgerSequence": 57313566, + "Sequence": 1113196, + "SigningPubKey": "03D847C2DBED3ABF0453F71DCD7641989136277218DF516AD49519C9693F32727E", + "TransactionType": "Payment", + "TxnSignature": "3045022100FCA10FBAC65EA60C115A970CD52E6A526B1F9DDB6C4F843DA3DE7A97DFF9492D022037824D0FC6F663FB08BE0F2812CBADE1F61836528D44945FC37F10CC03215111" + } + }, + { + "rjson": { + "Account": "rDsbeomae4FXwgQTJp9Rs64Qg9vDiTCdBv", + "Amount": "3899911571", + "Destination": "rU2mEJSLqBRkYLVTv55rFTgQajkLTnT6mA", + "DestinationTag": 255406, + "Fee": "60000", + "Flags": 2147483648, + "LastLedgerSequence": 57313566, + "Sequence": 1113197, + "SigningPubKey": "03D847C2DBED3ABF0453F71DCD7641989136277218DF516AD49519C9693F32727E", + "TransactionType": "Payment", + "TxnSignature": "3044022077642D94BB3C49BF3CB4C804255EC830D2C6009EA4995E38A84602D579B8AAD702206FAD977C49980226E8B495BF03C8D9767380F1546BBF5A4FD47D604C0D2CCF9B" + }, + "xjson": { + "Account": "rDsbeomae4FXwgQTJp9Rs64Qg9vDiTCdBv", + "Amount": "3899911571", + "Destination": "XVfH8gwNWVbB5Kft16jmTNgGTqgw1dzA8ZTBkNjSLw6JdXS", + "Fee": "60000", + "Flags": 2147483648, + "LastLedgerSequence": 57313566, + "Sequence": 1113197, + "SigningPubKey": "03D847C2DBED3ABF0453F71DCD7641989136277218DF516AD49519C9693F32727E", + "TransactionType": "Payment", + "TxnSignature": "3044022077642D94BB3C49BF3CB4C804255EC830D2C6009EA4995E38A84602D579B8AAD702206FAD977C49980226E8B495BF03C8D9767380F1546BBF5A4FD47D604C0D2CCF9B" + } + }, + { + "rjson": { + "Account": "r4eEbLKZGbVSBHnSUBZW8i5XaMjGLdqT4a", + "Amount": "820370849", + "Destination": "rDhmyBh4JwDAtXyRZDarNgg52UcLLRoGje", + "DestinationTag": 2017780486, + "Fee": "6000", + "Flags": 2147483648, + "LastLedgerSequence": 57315579, + "Sequence": 234254, + "SigningPubKey": "038CF47114672A12B269AEE015BF7A8438609B994B0640E4B28B2F56E93D948B15", + "TransactionType": "Payment", + "TxnSignature": "3044022015004653B1CBDD5CCA1F7B38555F1B37FE3F811E9D5070281CCC6C8A93460D870220679E9899184901EA69750C8A9325768490B1B9C1A733842446727653FF3D1DC0" + }, + "xjson": { + "Account": "r4eEbLKZGbVSBHnSUBZW8i5XaMjGLdqT4a", + "Amount": "820370849", + "Destination": "XV31huWNJQXsAJFwgE6rnC8uf8jRx4H4waq4MyGUxz5CXzS", + "Fee": "6000", + "Flags": 2147483648, + "LastLedgerSequence": 57315579, + "Sequence": 234254, + "SigningPubKey": "038CF47114672A12B269AEE015BF7A8438609B994B0640E4B28B2F56E93D948B15", + "TransactionType": "Payment", + "TxnSignature": "3044022015004653B1CBDD5CCA1F7B38555F1B37FE3F811E9D5070281CCC6C8A93460D870220679E9899184901EA69750C8A9325768490B1B9C1A733842446727653FF3D1DC0" + } + }, + { + "rjson": { + "Account": "rsGeDwS4rpocUumu9smpXomzaaeG4Qyifz", + "Amount": "1500000000", + "Destination": "rDxfhNRgCDNDckm45zT5ayhKDC4Ljm7UoP", + "DestinationTag": 1000635172, + "Fee": "5000", + "Flags": 2147483648, + "Sequence": 55741075, + "SigningPubKey": "02ECB814477DF9D8351918878E235EE6AF147A2A5C20F1E71F291F0F3303357C36", + "SourceTag": 1000635172, + "TransactionType": "Payment", + "TxnSignature": "304402202A90972E21823214733082E1977F9EA2D6B5101902F108E7BDD7D128CEEA7AF3022008852C8DAD746A7F18E66A47414FABF551493674783E8EA7409C501D3F05F99A" + }, + "xjson": { + "Account": "rsGeDwS4rpocUumu9smpXomzaaeG4Qyifz", + "Amount": "1500000000", + "Destination": "XVBkK1yLutMqFGwTm6hykn7YXGDUrjsZSkpzMgRveZrMbHs", + "Fee": "5000", + "Flags": 2147483648, + "Sequence": 55741075, + "SigningPubKey": "02ECB814477DF9D8351918878E235EE6AF147A2A5C20F1E71F291F0F3303357C36", + "SourceTag": 1000635172, + "TransactionType": "Payment", + "TxnSignature": "304402202A90972E21823214733082E1977F9EA2D6B5101902F108E7BDD7D128CEEA7AF3022008852C8DAD746A7F18E66A47414FABF551493674783E8EA7409C501D3F05F99A" + } + }, + { + "rjson": { + "Account": "rHWcuuZoFvDS6gNbmHSdpb7u1hZzxvCoMt", + "Amount": "48918500000", + "Destination": "rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh", + "DestinationTag": 105959914, + "Fee": "10", + "Flags": 2147483648, + "Sequence": 32641, + "SigningPubKey": "02E98DA545CCCC5D14C82594EE9E6CCFCF5171108E2410B3E784183E1068D33429", + "TransactionType": "Payment", + "TxnSignature": "304502210091DCA7AF189CD9DC93BDE24DEAE87381FBF16789C43113EE312241D648982B2402201C6055FEFFF1F119640AAC0B32C4F37375B0A96033E0527A21C1366920D6A524" + }, + "xjson": { + "Account": "rHWcuuZoFvDS6gNbmHSdpb7u1hZzxvCoMt", + "Amount": "48918500000", + "Destination": "XVH3aqvbYGhRhrD1FYSzGooNuxdzbG3VR2fuM47oqbXxQr7", + "Fee": "10", + "Flags": 2147483648, + "Sequence": 32641, + "SigningPubKey": "02E98DA545CCCC5D14C82594EE9E6CCFCF5171108E2410B3E784183E1068D33429", + "TransactionType": "Payment", + "TxnSignature": "304502210091DCA7AF189CD9DC93BDE24DEAE87381FBF16789C43113EE312241D648982B2402201C6055FEFFF1F119640AAC0B32C4F37375B0A96033E0527A21C1366920D6A524" + } + }, + { + "rjson": { + "PreviousTxnLgrSeq": 239, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "rnziParaNb8nsU4aruQdwYE3j5jUcqjzFm" + }, + "PreviousTxnID": "C6A2521BBCCF13282C4FFEBC00D47BBA18C6CE5F5E4E0EFC3E3FCE364BAFC6B8", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "rMYBVwiY95QyUnCeuBQA1D47kXA9zuoBui" + } + }, + "xjson": { + "PreviousTxnLgrSeq": 239, + "LedgerEntryType": "RippleState", + "LowLimit": { + "currency": "USD", + "value": "0", + "issuer": "X7jqKn4UidCF7AQ7Eeyfn3vJubztfhF1LwTD9PV2QhPpnvx" + }, + "PreviousTxnID": "C6A2521BBCCF13282C4FFEBC00D47BBA18C6CE5F5E4E0EFC3E3FCE364BAFC6B8", + "Flags": 131072, + "Balance": { + "currency": "USD", + "value": "0", + "issuer": "X7TYFRtYHMcHtT2qNycMwgXzFbcRvdrhMLy5kJrYHpJDkc2" + }, + "HighLimit": { + "currency": "USD", + "value": "10", + "issuer": "XVc7LPeF5cfwQ1XRmz6Ursck9i8yWpb1yqApvW4ZGuDNZbD" + } + } + }] +} diff --git a/src/core/types/account_id.rs b/src/core/types/account_id.rs new file mode 100644 index 00000000..83d393a6 --- /dev/null +++ b/src/core/types/account_id.rs @@ -0,0 +1,133 @@ +//! Codec for currency property inside an XRPL +//! issued currency amount json. + +use crate::constants::ACCOUNT_ID_LENGTH; +use crate::core::addresscodec::exceptions::XRPLAddressCodecException; +use crate::core::addresscodec::*; +use crate::core::types::exceptions::XRPLHashException; +use crate::core::types::*; +use crate::core::BinaryParser; +use crate::utils::is_hex_address; +use alloc::string::String; +use alloc::string::ToString; +use core::convert::TryFrom; +use serde::ser::Error; +use serde::Serializer; +use serde::{Deserialize, Serialize}; + +/// Codec for serializing and deserializing AccountID fields. +/// +/// See AccountID Fields: +/// `` +/// +/// +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "&str")] +pub struct AccountId(Hash160); + +impl XRPLType for AccountId { + type Error = XRPLHashException; + + /// Construct an AccountID from given bytes. + /// If buffer is not provided, default to 20 zero bytes. + fn new(buffer: Option<&[u8]>) -> Result { + let hash160 = Hash160::new(buffer.or(Some(&[0; ACCOUNT_ID_LENGTH])))?; + Ok(AccountId(hash160)) + } +} + +impl TryFromParser for AccountId { + type Error = XRPLHashException; + + /// Build AccountId from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + length: Option, + ) -> Result { + Ok(AccountId(Hash160::from_parser(parser, length)?)) + } +} + +impl Serialize for AccountId { + /// Return the value of this AccountID encoded as a + /// base58 string. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let result = &encode_classic_address(self.as_ref()); + + if let Ok(data) = result { + serializer.serialize_str(data) + } else { + Err(S::Error::custom(result.as_ref().unwrap_err())) + } + } +} + +impl TryFrom<&str> for AccountId { + type Error = XRPLHashException; + + /// Construct an AccountId from a hex string or + /// a base58 r-Address. + fn try_from(value: &str) -> Result { + if is_hex_address(value) { + Self::new(Some(&hex::decode(value)?)) + } else if is_valid_classic_address(value) { + Self::new(Some(&decode_classic_address(value)?)) + } else if is_valid_xaddress(value) { + let (classic_address, _, _) = xaddress_to_classic_address(value)?; + Self::new(Some(&decode_classic_address(&classic_address)?)) + } else { + Err(XRPLHashException::XRPLAddressCodecError( + XRPLAddressCodecException::InvalidClassicAddressValue, + )) + } + } +} + +impl ToString for AccountId { + /// Get the classic address of the AccountId bytes. + fn to_string(&self) -> String { + encode_classic_address(self.as_ref()).expect("to_string") + } +} + +impl AsRef<[u8]> for AccountId { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::format; + + const HEX_ENCODING: &str = "5E7B112523F68D2F5E879DB4EAC51C6698A69304"; + const BASE58_ENCODING: &str = "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"; + + #[test] + fn test_accountid_new() { + let hex = hex::decode(HEX_ENCODING).expect(""); + let account = AccountId::new(Some(&hex)).unwrap(); + assert_eq!(HEX_ENCODING, hex::encode_upper(account)); + } + + #[test] + fn test_accountid_try_from() { + let account = AccountId::try_from(BASE58_ENCODING).unwrap(); + assert_eq!(HEX_ENCODING, hex::encode_upper(account)); + } + + #[test] + fn accept_accountid_serde_encode_decode() { + let account: AccountId = AccountId::try_from(BASE58_ENCODING).unwrap(); + let serialize = serde_json::to_string(&account).unwrap(); + let deserialize: AccountId = serde_json::from_str(&serialize).unwrap(); + + assert_eq!(format!("\"{}\"", BASE58_ENCODING), serialize); + assert_eq!(account.to_string(), deserialize.to_string()); + } +} diff --git a/src/core/types/amount.rs b/src/core/types/amount.rs new file mode 100644 index 00000000..ffcd3d01 --- /dev/null +++ b/src/core/types/amount.rs @@ -0,0 +1,446 @@ +//! Codec for serializing and deserializing Amount fields. +//! +//! See Amount Fields: +//! `` + +use crate::core::binarycodec::exceptions::XRPLBinaryCodecException; +use crate::core::types::exceptions::XRPLTypeException; +use crate::core::types::*; +use crate::core::BinaryParser; +use crate::core::Parser; +use crate::utils::exceptions::JSONParseException; +use crate::utils::exceptions::XRPRangeException; +use crate::utils::*; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::convert::TryInto; +use core::str::FromStr; +use rust_decimal::prelude::ToPrimitive; +use rust_decimal::Decimal; +use serde::ser::Error; +use serde::ser::SerializeMap; +use serde::Serializer; +use serde::{Deserialize, Serialize}; + +const _MIN_MANTISSA: u128 = u128::pow(10, 15); +const _MAX_MANTISSA: u128 = u128::pow(10, 16) - 1; + +const _NOT_XRP_BIT_MASK: u8 = 0x80; +const _POS_SIGN_BIT_MASK: i64 = 0x4000000000000000; +const _ZERO_CURRENCY_AMOUNT_HEX: u64 = 0x8000000000000000; +const _NATIVE_AMOUNT_BYTE_LENGTH: u8 = 8; +const _CURRENCY_AMOUNT_BYTE_LENGTH: u8 = 48; + +/// An Issued Currency object. +#[derive(Debug, Serialize, Deserialize, Clone)] +pub(crate) struct IssuedCurrency { + pub value: Decimal, + pub currency: Currency, + pub issuer: AccountId, +} + +/// Codec for serializing and deserializing Amount fields. +/// +/// See Amount Fields: +/// `` +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "&str")] +pub struct Amount(Vec); + +/// Returns True if the given string contains a +/// decimal point character. +fn _contains_decimal(string: &str) -> bool { + string.contains('.') +} + +/// Serializes the value field of an issued currency amount +/// to its bytes representation. +fn _serialize_issued_currency_value(decimal: Decimal) -> Result<[u8; 8], XRPRangeException> { + verify_valid_ic_value(&decimal.to_string())?; + + if decimal.is_zero() { + return Ok((_ZERO_CURRENCY_AMOUNT_HEX).to_be_bytes()); + }; + + let is_positive: bool = decimal.is_sign_positive(); + let mut exp: i32 = -(decimal.normalize().scale() as i32); + let mut mantissa: u128 = decimal.normalize().mantissa().abs() as u128; + + while mantissa < _MIN_MANTISSA && exp > MIN_IOU_EXPONENT { + mantissa *= 10; + exp -= 1; + } + + while mantissa > _MAX_MANTISSA { + if exp >= MAX_IOU_EXPONENT { + return Err(XRPRangeException::UnexpectedICAmountOverflow { + max: MAX_IOU_EXPONENT as usize, + found: exp as usize, + }); + } else { + mantissa /= 10; + exp += 1; + } + } + + if exp < MIN_IOU_EXPONENT || mantissa < _MIN_MANTISSA { + // Round to zero + Ok((_ZERO_CURRENCY_AMOUNT_HEX).to_be_bytes()) + } else if exp > MAX_IOU_EXPONENT || mantissa > _MAX_MANTISSA { + Err(XRPRangeException::UnexpectedICAmountOverflow { + max: MAX_IOU_EXPONENT as usize, + found: exp as usize, + }) + } else { + // "Not XRP" bit set + let mut serial: i128 = _ZERO_CURRENCY_AMOUNT_HEX as i128; + + // "Is positive" bit set + if is_positive { + serial |= _POS_SIGN_BIT_MASK as i128; + }; + + // next 8 bits are exponents + serial |= ((exp as i64 + 97) << 54) as i128; + // last 54 bits are mantissa + serial |= mantissa as i128; + + Ok((serial as u64).to_be_bytes()) + } +} + +/// Serializes an XRP amount. +fn _serialize_xrp_amount(value: &str) -> Result<[u8; 8], XRPRangeException> { + verify_valid_xrp_value(value)?; + + let decimal = Decimal::from_str(value)?.normalize(); + + if let Some(result) = decimal.to_i64() { + let value_with_pos_bit = result | _POS_SIGN_BIT_MASK; + Ok(value_with_pos_bit.to_be_bytes()) + } else { + // Safety, should never occur + Err(XRPRangeException::InvalidXRPAmount) + } +} + +/// Serializes an issued currency amount. +fn _serialize_issued_currency_amount( + issused_currency: IssuedCurrency, +) -> Result<[u8; 48], XRPRangeException> { + let mut bytes = vec![]; + let amount_bytes = _serialize_issued_currency_value(issused_currency.value)?; + let currency_bytes: &[u8] = issused_currency.currency.as_ref(); + let issuer_bytes: &[u8] = issused_currency.issuer.as_ref(); + + bytes.extend_from_slice(&amount_bytes); + bytes.extend_from_slice(currency_bytes); + bytes.extend_from_slice(issuer_bytes); + + if bytes.len() != 48 { + Err(XRPRangeException::InvalidICSerializationLength { + expected: 48, + found: bytes.len(), + }) + } else { + Ok(bytes.try_into().expect("_serialize_issued_currency_amount")) + } +} + +impl Amount { + /// Deserialize native asset amount. + fn _deserialize_native_amount(&self) -> String { + let mut sized: [u8; 8] = Default::default(); + + sized.copy_from_slice(&self.as_ref()[..8]); + (u64::from_be_bytes(sized) & 0x3FFFFFFFFFFFFFFF).to_string() + } + + /// Returns True if this amount is a native XRP amount. + pub fn is_native(&self) -> bool { + self.0[0] & 0x80 == 0 + } + + /// Returns true if 2nd bit in 1st byte is set to 1 + /// (positive amount). + pub fn is_positive(&self) -> bool { + self.0[1] & 0x40 > 0 + } +} + +impl IssuedCurrency { + /// Deserialize the issued currency amount. + fn _deserialize_issued_currency_amount( + parser: &mut BinaryParser, + ) -> Result { + let mut value: Decimal; + let bytes = parser.read(8)?; + // Some wizardry by Amie Corso + let exp = ((bytes[0] as i32 & 0x3F) << 2) + ((bytes[1] as i32 & 0xFF) >> 6) - 97; + + if exp < MIN_IOU_EXPONENT { + value = Decimal::ZERO; + } else { + let hex_mantissa = hex::encode([&[bytes[1] & 0x3F], &bytes[2..]].concat()); + let int_mantissa = i128::from_str_radix(&hex_mantissa, 16)?; + value = Decimal::from_i128_with_scale(int_mantissa, exp.abs() as u32); + + if bytes[0] & 0x40 > 0 { + value.set_sign_positive(true); + } else { + value.set_sign_negative(true); + } + } + + verify_valid_ic_value(&value.to_string())?; + Ok(value.normalize()) + } +} + +impl XRPLType for Amount { + type Error = hex::FromHexError; + + /// Construct an Amount from given bytes. + fn new(buffer: Option<&[u8]>) -> Result { + if let Some(data) = buffer { + Ok(Amount(data.to_vec())) + } else { + Ok(Amount(vec![])) + } + } +} + +impl TryFromParser for Amount { + type Error = XRPLBinaryCodecException; + + /// Build Amount from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + _length: Option, + ) -> Result { + let parser_first_byte = parser.peek(); + let num_bytes = match parser_first_byte { + None => _CURRENCY_AMOUNT_BYTE_LENGTH, + Some(_) => _NATIVE_AMOUNT_BYTE_LENGTH, + }; + + Ok(Amount(parser.read(num_bytes as usize)?)) + } +} + +impl TryFromParser for IssuedCurrency { + type Error = XRPLTypeException; + + /// Build IssuedCurrency from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + _length: Option, + ) -> Result { + Ok(IssuedCurrency { + value: IssuedCurrency::_deserialize_issued_currency_amount(parser)?, + currency: Currency::from_parser(parser, None)?, + issuer: AccountId::from_parser(parser, None)?, + }) + } +} + +impl Serialize for Amount { + /// Construct a JSON object representing this Amount. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if self.is_native() { + serializer.serialize_str(&self._deserialize_native_amount()) + } else { + let mut parser = BinaryParser::from(self.as_ref()); + + if let Ok(ic) = IssuedCurrency::from_parser(&mut parser, None) { + let mut builder = serializer.serialize_map(Some(3))?; + + builder.serialize_entry("value", &ic.value)?; + builder.serialize_entry("currency", &ic.currency)?; + builder.serialize_entry("issuer", &ic.issuer)?; + builder.end() + } else { + Err(S::Error::custom( + XRPLBinaryCodecException::InvalidReadFromBytesValue, + )) + } + } + } +} + +impl TryFrom<&str> for Amount { + type Error = XRPLTypeException; + + /// Construct an Amount object from a hex string. + fn try_from(value: &str) -> Result { + let serialized = _serialize_xrp_amount(value)?; + Ok(Amount::new(Some(&serialized))?) + } +} + +impl TryFrom for Amount { + type Error = XRPLTypeException; + + /// Construct an Amount object from an IssuedCurrency. + fn try_from(value: IssuedCurrency) -> Result { + let serialized = _serialize_issued_currency_amount(value)?; + Ok(Amount::new(Some(&serialized))?) + } +} + +impl TryFrom for Amount { + type Error = XRPLTypeException; + + /// Construct an Amount object from a Serde JSON Value. + fn try_from(value: serde_json::Value) -> Result { + if value.is_string() { + Self::try_from(value.as_str().ok_or(XRPLTypeException::InvalidNoneValue)?) + } else if value.is_object() { + Ok(Self::try_from(IssuedCurrency::try_from(value)?)?) + } else { + Err(XRPLTypeException::JSONParseError( + JSONParseException::InvalidSerdeValue { + expected: "String/Object".into(), + found: value, + }, + )) + } + } +} + +impl TryFrom for IssuedCurrency { + type Error = XRPLTypeException; + + /// Construct an IssuedCurrency object from a Serde JSON Value. + fn try_from(json: serde_json::Value) -> Result { + let value = Decimal::from_str( + json["value"] + .as_str() + .ok_or(XRPLTypeException::InvalidNoneValue)?, + )?; + let currency = Currency::try_from( + json["currency"] + .as_str() + .ok_or(XRPLTypeException::InvalidNoneValue)?, + )?; + let issuer = AccountId::try_from( + json["issuer"] + .as_str() + .ok_or(XRPLTypeException::InvalidNoneValue)?, + )?; + + Ok(IssuedCurrency { + value, + currency, + issuer, + }) + } +} + +impl ToString for Amount { + /// Get the hex representation of the Amount bytes. + fn to_string(&self) -> String { + hex::encode_upper(self.as_ref()) + } +} + +impl AsRef<[u8]> for Amount { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::core::binarycodec::test_cases::load_data_tests; + use crate::core::types::test_cases::IOUCase; + use crate::core::types::test_cases::TEST_XRP_CASES; + use alloc::format; + + const IOU_TEST: &str = include_str!("../test_data/iou-tests.json"); + + #[test] + fn test_contains_decimal() { + assert!(_contains_decimal("1.00")); + assert!(!_contains_decimal("100")); + } + + #[test] + fn test_amount_new() { + let json: Vec = serde_json::from_str(IOU_TEST).expect(""); + + for case in json { + let bytes = hex::decode(case.1).expect(""); + let amount: Amount = Amount::new(Some(&bytes)).unwrap(); + + assert_eq!(hex::encode_upper(bytes), amount.to_string()) + } + } + + #[test] + fn test_amount_try_from_issuedcurrency() { + let json: Vec = serde_json::from_str(IOU_TEST).expect(""); + + for case in json { + let amount = Amount::try_from(case.0).unwrap(); + assert_eq!(case.1, amount.to_string()) + } + } + + #[test] + fn test_amount_try_from_string() { + for (xrp, result) in TEST_XRP_CASES { + let amount = Amount::try_from(xrp).unwrap(); + assert_eq!(result, amount.to_string()) + } + } + + #[test] + fn accept_amount_serde_encode_decode() { + let json: Vec = serde_json::from_str(IOU_TEST).expect(""); + + for case in json { + let expect = serde_json::to_string(&case.0).expect(""); + let bytes = hex::decode(case.1).expect(""); + let amount: Amount = Amount::new(Some(&bytes)).unwrap(); + let serialize = serde_json::to_string(&amount).unwrap(); + + assert_eq!(serialize, expect); + } + + for (xrp, result) in TEST_XRP_CASES { + let bytes = hex::decode(result).expect(""); + let amount: Amount = Amount::new(Some(&bytes)).unwrap(); + let serialize = serde_json::to_string(&amount).unwrap(); + + assert_eq!(serialize, format!("\"{}\"", xrp)); + } + } + + #[test] + fn accept_amount_value_tests() { + let tests = load_data_tests(Some("Amount")); + + for test in tests { + extern crate std; + std::println!("{:?}", test.test_json); + //let data = test.test_json.clone(); + let amount = Amount::try_from(test.test_json); + + if test.error.is_none() { + assert_eq!(test.expected_hex, Some(amount.unwrap().to_string())); + //assert_eq!(data, serde_json::to_string(&amount).unwrap()); + } else { + assert!(amount.is_err()); + } + } + } +} diff --git a/src/core/types/blob.rs b/src/core/types/blob.rs new file mode 100644 index 00000000..5515bddd --- /dev/null +++ b/src/core/types/blob.rs @@ -0,0 +1,91 @@ +//! Codec for serializing and deserializing blob fields. +//! +//! See Blob Fields: +//! `` + +use crate::core::binarycodec::exceptions::XRPLBinaryCodecException; +use crate::core::types::*; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; +use serde::Serializer; +use serde::{Deserialize, Serialize}; + +/// Codec for serializing and deserializing blob fields. +/// +/// See Blob Fields: +/// `` +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "&str")] +pub struct Blob(Vec); + +impl XRPLType for Blob { + type Error = XRPLBinaryCodecException; + + fn new(buffer: Option<&[u8]>) -> Result { + if let Some(data) = buffer { + Ok(Blob(data.to_vec())) + } else { + Ok(Blob(vec![])) + } + } +} + +impl Serialize for Blob { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex::encode_upper(self.as_ref())) + } +} + +impl TryFrom<&str> for Blob { + type Error = XRPLBinaryCodecException; + + /// Construct a Blob from a hex string. + fn try_from(value: &str) -> Result { + Self::new(Some(&hex::decode(value)?)) + } +} + +impl ToString for Blob { + /// Get the hex representation of the Blob bytes. + fn to_string(&self) -> String { + hex::encode_upper(self.as_ref()) + } +} + +impl AsRef<[u8]> for Blob { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + + const TEST_HEX: &str = "00AA"; + + #[test] + fn test_blob_new() { + let bytes = hex::decode(TEST_HEX).unwrap(); + let blob = Blob::new(Some(&bytes)); + + assert!(blob.is_ok()); + assert_eq!(bytes, blob.unwrap().as_ref()); + } + + #[test] + fn test_blob_try_from() { + let bytes = hex::decode(TEST_HEX).unwrap(); + let blob = Blob::try_from(TEST_HEX); + + assert!(blob.is_ok()); + assert_eq!(bytes, blob.unwrap().as_ref()); + } +} diff --git a/src/core/types/currency.rs b/src/core/types/currency.rs new file mode 100644 index 00000000..735adf5a --- /dev/null +++ b/src/core/types/currency.rs @@ -0,0 +1,240 @@ +//! Codec for currency property inside an XRPL +//! issued currency amount json. + +use crate::constants::HEX_CURRENCY_REGEX; +use crate::constants::ISO_CURRENCY_REGEX; +use crate::core::types::exceptions::XRPLHashException; +use crate::core::types::utils::CURRENCY_CODE_LENGTH; +use crate::core::types::*; +use crate::core::BinaryParser; +use crate::utils::exceptions::ISOCodeException; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; +use core::convert::TryInto; +use regex::Regex; +use serde::Serializer; +use serde::{Deserialize, Serialize}; + +pub const NATIVE_HEX_CODE: &str = "0000000000000000000000000000000000000000"; +pub const NATIVE_CODE: &str = "XRP"; + +/// Codec for serializing and deserializing +/// vectors of Hash256. +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "&str")] +pub struct Currency(Hash160); + +/// Tests if value is a valid 3-char iso code. +fn _is_iso_code(value: &str) -> bool { + let regex = Regex::new(ISO_CURRENCY_REGEX).expect("_is_iso_code"); + regex.is_match(value) +} + +/// Tests if value is a valid 40-char hex string. +fn _is_hex(value: &str) -> bool { + let regex = Regex::new(HEX_CURRENCY_REGEX).expect("_is_hex"); + regex.is_match(value) +} + +fn _iso_code_from_hex(value: &[u8]) -> Result, ISOCodeException> { + let candidate_iso = alloc::str::from_utf8(&value[12..15])?; + + if candidate_iso == NATIVE_CODE { + Err(ISOCodeException::InvalidXRPBytes) + } else if _is_iso_code(candidate_iso) { + Ok(Some(candidate_iso.to_string())) + } else { + Ok(None) + } +} + +/// Convert an ISO code to a 160-bit (20 byte) encoded +/// representation. +/// +/// See "Currency codes" subheading in Amount Fields: +/// `` +fn _iso_to_bytes(value: &str) -> Result<[u8; CURRENCY_CODE_LENGTH], ISOCodeException> { + if !_is_iso_code(value) { + Err(ISOCodeException::InvalidISOCode) + } else if value == NATIVE_CODE { + Ok(Default::default()) + } else { + let value = value.to_uppercase(); + let iso_bytes = value.as_bytes(); + let pad_left: [u8; 12] = Default::default(); + let pad_right: [u8; 5] = Default::default(); + let mut result: Vec = vec![]; + + result.extend_from_slice(&pad_left); + result.extend_from_slice(iso_bytes); + result.extend_from_slice(&pad_right); + + Ok(result + .try_into() + .or(Err(ISOCodeException::InvalidISOLength))?) + } +} + +impl XRPLType for Currency { + type Error = XRPLHashException; + + fn new(buffer: Option<&[u8]>) -> Result { + let hash160 = Hash160::new(buffer.or(Some(&[0; CURRENCY_CODE_LENGTH])))?; + Ok(Currency(hash160)) + } +} + +impl TryFromParser for Currency { + type Error = XRPLHashException; + + /// Build Currency from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + length: Option, + ) -> Result { + Ok(Currency(Hash160::from_parser(parser, length)?)) + } +} + +impl Serialize for Currency { + /// Returns the JSON representation of a currency. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl TryFrom<&str> for Currency { + type Error = XRPLHashException; + + /// Construct a Currency object from a string + /// representation of a currency. + fn try_from(value: &str) -> Result { + if _is_iso_code(value) { + let iso_bytes = _iso_to_bytes(value)?; + let hash160 = Hash160::new(Some(&iso_bytes))?; + Ok(Currency(hash160)) + } else if _is_hex(value) { + Ok(Currency(Hash160::new(Some(&hex::decode(value)?))?)) + } else { + Err(XRPLHashException::ISOCodeError( + ISOCodeException::UnsupportedCurrencyRepresentation, + )) + } + } +} + +impl ToString for Currency { + /// Get the ISO or hex representation of the Currency bytes. + fn to_string(&self) -> String { + let buffer = self.0.as_ref(); + + if hex::encode_upper(buffer) == NATIVE_HEX_CODE { + NATIVE_CODE.to_string() + } else { + let iso = _iso_code_from_hex(buffer); + + if let Ok(code) = iso { + code.or_else(|| Some(hex::encode_upper(buffer))).unwrap() + } else { + hex::encode_upper(buffer) + } + } + } +} + +impl AsRef<[u8]> for Currency { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::format; + + const ILLEGAL_NATIVE_HEX_CODE: &str = "0000000000000000000000005852500000000000"; + const USD_HEX_CODE: &str = "0000000000000000000000005553440000000000"; + const NONSTANDARD_HEX_CODE: &str = "015841551A748AD2C1F76FF6ECB0CCCD00000000"; + const USD_ISO: &str = "USD"; + + #[test] + fn test_is_iso_code() { + let valid_code = "ABC"; + let valid_code_numeric = "123"; + let invalid_code_long = "LONG"; + let invalid_code_short = "NO"; + + assert!(_is_iso_code(valid_code)); + assert!(_is_iso_code(valid_code_numeric)); + assert!(!_is_iso_code(invalid_code_long)); + assert!(!_is_iso_code(invalid_code_short)); + } + + #[test] + fn test_is_hex() { + // Valid = 40 char length and only valid hex chars + let valid_hex: &str = "0000000000000000000000005553440000000000"; + let invalid_hex_chars: &str = "USD0000000000000000000005553440000000000"; + let invalid_hex_long: &str = "0000000000000000000000005553440000000000123455"; + let invalid_hex_short: &str = "1234"; + + assert!(_is_hex(valid_hex)); + assert!(!_is_hex(invalid_hex_long)); + assert!(!_is_hex(invalid_hex_short)); + assert!(!_is_hex(invalid_hex_chars)); + } + + #[test] + fn test_iso_to_bytes() { + // Valid non-XRP + let usd_iso_bytes = _iso_to_bytes(USD_ISO).unwrap(); + // Valid XRP + let xrp_iso_bytes = _iso_to_bytes(NATIVE_CODE).unwrap(); + // Error case + let invalid_iso = "INVALID"; + + assert_eq!(USD_HEX_CODE, hex::encode_upper(usd_iso_bytes)); + assert_eq!(NATIVE_HEX_CODE, hex::encode_upper(xrp_iso_bytes)); + assert!(_iso_to_bytes(invalid_iso).is_err()); + } + + #[test] + fn test_currency_new() { + let hex = hex::decode(USD_HEX_CODE).expect(""); + let currency = Currency::new(Some(&hex)); + assert_eq!(USD_HEX_CODE, hex::encode_upper(currency.unwrap())) + } + + #[test] + fn test_currency_try_from() { + let from_hex_xrp = Currency::try_from(NATIVE_HEX_CODE).unwrap(); + let from_hex_ic = Currency::try_from(USD_HEX_CODE).unwrap(); + let from_iso_xrp = Currency::try_from(NATIVE_CODE).unwrap(); + let from_iso_ic = Currency::try_from(USD_ISO).unwrap(); + let from_ns = Currency::try_from(NONSTANDARD_HEX_CODE).unwrap(); + + assert_eq!(NATIVE_CODE, from_hex_xrp.to_string()); + assert_eq!(USD_ISO, from_hex_ic.to_string()); + assert_eq!(NATIVE_HEX_CODE, hex::encode_upper(from_iso_xrp)); + assert_eq!(USD_HEX_CODE, hex::encode_upper(from_iso_ic)); + assert_eq!(NONSTANDARD_HEX_CODE, hex::encode_upper(from_ns)); + } + + #[test] + fn accept_currency_serde_encode_decode() { + let currency = Currency::try_from(USD_HEX_CODE).unwrap(); + let serialize = serde_json::to_string(¤cy).unwrap(); + let deserialize: Currency = serde_json::from_str(&serialize).unwrap(); + + assert_eq!(format!("\"{}\"", USD_ISO), serialize); + assert_eq!(currency.to_string(), deserialize.to_string()); + } +} diff --git a/src/core/types/exceptions.rs b/src/core/types/exceptions.rs new file mode 100644 index 00000000..e1cc3702 --- /dev/null +++ b/src/core/types/exceptions.rs @@ -0,0 +1,129 @@ +//! Exception for invalid XRP Ledger type data. + +use crate::core::addresscodec::exceptions::XRPLAddressCodecException; +use crate::core::binarycodec::exceptions::XRPLBinaryCodecException; +use crate::utils::exceptions::ISOCodeException; +use crate::utils::exceptions::JSONParseException; +use crate::utils::exceptions::XRPRangeException; + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum XRPLTypeException { + InvalidNoneValue, + XRPLBinaryCodecError(XRPLBinaryCodecException), + XRPLHashError(XRPLHashException), + XRPLRangeError(XRPRangeException), + DecimalError(rust_decimal::Error), + JSONParseError(JSONParseException), + HexError(hex::FromHexError), +} + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum XRPLHashException { + InvalidHashLength { expected: usize, found: usize }, + ISOCodeError(ISOCodeException), + XRPLBinaryCodecError(XRPLBinaryCodecException), + XRPLAddressCodecError(XRPLAddressCodecException), + SerdeJsonError(serde_json::error::Category), + HexError(hex::FromHexError), +} + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum XRPLVectorException { + InvalidVector256Bytes, + XRPLBinaryCodecError(XRPLBinaryCodecException), + XRPLHashError(XRPLHashException), + HexError(hex::FromHexError), +} + +impl From for XRPLTypeException { + fn from(err: XRPLHashException) -> Self { + XRPLTypeException::XRPLHashError(err) + } +} + +impl From for XRPLTypeException { + fn from(err: XRPRangeException) -> Self { + XRPLTypeException::XRPLRangeError(err) + } +} + +impl From for XRPLTypeException { + fn from(err: XRPLBinaryCodecException) -> Self { + XRPLTypeException::XRPLBinaryCodecError(err) + } +} + +impl From for XRPLTypeException { + fn from(err: JSONParseException) -> Self { + XRPLTypeException::JSONParseError(err) + } +} + +impl From for XRPLTypeException { + fn from(err: rust_decimal::Error) -> Self { + XRPLTypeException::DecimalError(err) + } +} + +impl From for XRPLTypeException { + fn from(err: hex::FromHexError) -> Self { + XRPLTypeException::HexError(err) + } +} + +impl From for XRPLHashException { + fn from(err: ISOCodeException) -> Self { + XRPLHashException::ISOCodeError(err) + } +} + +impl From for XRPLHashException { + fn from(err: XRPLBinaryCodecException) -> Self { + XRPLHashException::XRPLBinaryCodecError(err) + } +} + +impl From for XRPLHashException { + fn from(err: XRPLAddressCodecException) -> Self { + XRPLHashException::XRPLAddressCodecError(err) + } +} + +impl From for XRPLHashException { + fn from(err: serde_json::Error) -> Self { + XRPLHashException::SerdeJsonError(err.classify()) + } +} + +impl From for XRPLHashException { + fn from(err: hex::FromHexError) -> Self { + XRPLHashException::HexError(err) + } +} + +impl From for XRPLVectorException { + fn from(err: XRPLHashException) -> Self { + XRPLVectorException::XRPLHashError(err) + } +} + +impl core::fmt::Display for XRPLTypeException { + fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + write!(f, "XRPLTypeException: {:?}", self) + } +} + +impl core::fmt::Display for XRPLHashException { + fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + write!(f, "XRPLHashException: {:?}", self) + } +} + +impl core::fmt::Display for XRPLVectorException { + fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + write!(f, "XRPLVectorException: {:?}", self) + } +} diff --git a/src/core/types/hash.rs b/src/core/types/hash.rs new file mode 100644 index 00000000..be62655c --- /dev/null +++ b/src/core/types/hash.rs @@ -0,0 +1,398 @@ +//! Base class for XRPL Hash types. +//! +//! See Hash Fields: +//! `` + +use crate::core::types::exceptions::XRPLHashException; +use crate::core::types::utils::*; +use crate::core::types::*; +use crate::core::BinaryParser; +use crate::core::Parser; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; +use core::convert::TryFrom; +use serde::Deserialize; + +/// Codec for serializing and deserializing a hash field +/// with a width of 128 bits (16 bytes). +/// +/// See Hash Fields: +/// `` +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "&str")] +pub struct Hash128(Vec); + +/// Codec for serializing and deserializing a hash field +/// with a width of 160 bits (20 bytes). +/// +/// See Hash Fields: +/// `` +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "&str")] +pub struct Hash160(Vec); + +/// Codec for serializing and deserializing a hash field +/// with a width of 256 bits (32 bytes). +/// +/// See Hash Fields: +/// `` +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "&str")] +pub struct Hash256(Vec); + +/// XRPL Hash type. +/// +/// See Hash Fields: +/// `` +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::types::hash::Hash; +/// +/// #[derive(Debug)] +/// pub struct Hash256(Vec); +/// +/// const _HASH256_LENGTH: usize = 16; +/// +/// impl Hash for Hash256 { +/// fn get_length() -> usize { +/// _HASH256_LENGTH +/// } +/// } +/// ``` +pub trait Hash { + /// Get the length of the hash. + fn get_length() -> usize + where + Self: Sized; +} + +impl dyn Hash { + /// Make a new hash of type T. Useful for extending + /// new Hash lengths like Hash160. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::types::exceptions::XRPLHashException; + /// use xrpl::core::types::hash::Hash; + /// use xrpl::core::types::hash::Hash160; + /// + /// fn handle_success_case(hash: Vec) { + /// assert!(true) + /// } + /// + /// fn handle_hash_error(error: XRPLHashException) { + /// // Error Conditions + /// match error { + /// XRPLHashException::InvalidHashLength { + /// expected, + /// found, + /// } => assert!(true), + /// _ => assert!(true), + /// } + /// } + /// + /// let buffer: &str = "1000000000200000000030000000004000000000"; + /// let data: Vec = hex::decode(buffer).expect(""); + /// let result: Result, XRPLHashException> = + /// ::make::(Some(&data)); + /// + /// match result { + /// Ok(hash) => handle_success_case(hash), + /// Err(e) => handle_hash_error(e), + /// }; + /// ``` + pub fn make(bytes: Option<&[u8]>) -> Result, XRPLHashException> { + let byte_value: &[u8] = bytes.or(Some(&[])).unwrap(); + let hash_length: usize = T::get_length(); + + if byte_value.len() != hash_length { + Err(XRPLHashException::InvalidHashLength { + expected: hash_length, + found: byte_value.len(), + }) + } else { + Ok(byte_value.to_vec()) + } + } + + /// Parse a hash type from a binary parser. + /// + /// # Examples + /// + /// ## Basic usage + /// + /// ``` + /// use xrpl::core::types::exceptions::XRPLHashException; + /// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; + /// use xrpl::core::binarycodec::BinaryParser; + /// use xrpl::core::types::TryFromParser; + /// use xrpl::core::types::hash::Hash; + /// use xrpl::core::types::hash::Hash128; + /// + /// fn handle_success_case(hash: Hash128) { + /// // Success Conditions + /// assert!(true); + /// } + /// + /// fn handle_parser_error(error: XRPLHashException) { + /// // Error Conditions + /// match error { + /// XRPLHashException::XRPLBinaryCodecError(_e) => assert!(false), + /// _ => assert!(false), + /// } + /// } + /// + /// fn handle_hash128_from_parser(data: &[u8]) { + /// let mut parser: BinaryParser = BinaryParser::from(data); + /// let result: Result = + /// Hash128::from_parser(&mut parser, None); + /// + /// match result { + /// Ok(hash) => handle_success_case(hash), + /// Err(e) => handle_parser_error(e) + /// } + /// } + /// + /// let buffer: &str = "10000000002000000000300000000012"; + /// let data: Vec = hex::decode(buffer).expect(""); + /// + /// handle_hash128_from_parser(&data); + /// ``` + pub fn parse( + parser: &mut BinaryParser, + length: Option, + ) -> Result, XRPLHashException> { + let read_length = length.or_else(|| Some(T::get_length())).unwrap(); + Ok(parser.read(read_length)?) + } +} + +impl Hash for Hash128 { + fn get_length() -> usize { + HASH128_LENGTH + } +} + +impl Hash for Hash160 { + fn get_length() -> usize { + HASH160_LENGTH + } +} + +impl Hash for Hash256 { + fn get_length() -> usize { + HASH256_LENGTH + } +} + +impl XRPLType for Hash128 { + type Error = XRPLHashException; + + fn new(buffer: Option<&[u8]>) -> Result { + Ok(Hash128(::make::(buffer)?)) + } +} + +impl XRPLType for Hash160 { + type Error = XRPLHashException; + + fn new(buffer: Option<&[u8]>) -> Result { + Ok(Hash160(::make::(buffer)?)) + } +} + +impl XRPLType for Hash256 { + type Error = XRPLHashException; + + fn new(buffer: Option<&[u8]>) -> Result { + Ok(Hash256(::make::(buffer)?)) + } +} + +impl TryFromParser for Hash128 { + type Error = XRPLHashException; + + /// Build Hash128 from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + length: Option, + ) -> Result { + Ok(Hash128(::parse::(parser, length)?)) + } +} + +impl TryFromParser for Hash160 { + type Error = XRPLHashException; + + /// Build Hash160 from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + length: Option, + ) -> Result { + Ok(Hash160(::parse::(parser, length)?)) + } +} + +impl TryFromParser for Hash256 { + type Error = XRPLHashException; + + /// Build Hash256 from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + length: Option, + ) -> Result { + Ok(Hash256(::parse::(parser, length)?)) + } +} + +impl TryFrom<&str> for Hash128 { + type Error = XRPLHashException; + + /// Construct a Hash object from a hex string. + fn try_from(value: &str) -> Result { + Hash128::new(Some(&hex::decode(value)?)) + } +} + +impl TryFrom<&str> for Hash160 { + type Error = XRPLHashException; + + /// Construct a Hash object from a hex string. + fn try_from(value: &str) -> Result { + Hash160::new(Some(&hex::decode(value)?)) + } +} + +impl TryFrom<&str> for Hash256 { + type Error = XRPLHashException; + + /// Construct a Hash object from a hex string. + fn try_from(value: &str) -> Result { + Hash256::new(Some(&hex::decode(value)?)) + } +} + +impl ToString for Hash128 { + /// Get the hex representation of the Hash128 bytes. + fn to_string(&self) -> String { + hex::encode_upper(self.as_ref()) + } +} + +impl ToString for Hash160 { + /// Get the hex representation of the Hash160 bytes. + fn to_string(&self) -> String { + hex::encode_upper(self.as_ref()) + } +} + +impl ToString for Hash256 { + /// Get the hex representation of the Hash256 bytes. + fn to_string(&self) -> String { + hex::encode_upper(self.as_ref()) + } +} + +impl AsRef<[u8]> for Hash160 { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for Hash128 { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for Hash256 { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + + const HASH128_HEX_TEST: &str = "10000000002000000000300000000012"; + const HASH160_HEX_TEST: &str = "1000000000200000000030000000004000000000"; + const HASH256_HEX_TEST: &str = + "1000000000200000000030000000004000000000500000000060000000001234"; + + #[test] + fn test_hash_new() { + let hex128 = hex::decode(HASH128_HEX_TEST).unwrap(); + let hex160 = hex::decode(HASH160_HEX_TEST).unwrap(); + let hex256 = hex::decode(HASH256_HEX_TEST).unwrap(); + + assert_eq!(HASH128_HEX_TEST, Hash128(hex128).to_string()); + assert_eq!(HASH160_HEX_TEST, Hash160(hex160).to_string()); + assert_eq!(HASH256_HEX_TEST, Hash256(hex256).to_string()); + } + + #[test] + fn test_hash_try_from_parser() { + let hex = hex::decode(HASH128_HEX_TEST).expect(""); + let mut parser = BinaryParser::from(hex); + let result = Hash128::from_parser(&mut parser, None); + + assert!(result.is_ok()); + assert_eq!(HASH128_HEX_TEST, result.unwrap().to_string()); + + let hex = hex::decode(HASH160_HEX_TEST).expect(""); + let mut parser = BinaryParser::from(hex); + let result = Hash160::from_parser(&mut parser, None); + + assert!(result.is_ok()); + assert_eq!(HASH160_HEX_TEST, result.unwrap().to_string()); + + let hex = hex::decode(HASH256_HEX_TEST).expect(""); + let mut parser = BinaryParser::from(hex); + let result = Hash256::from_parser(&mut parser, None); + + assert!(result.is_ok()); + assert_eq!(HASH256_HEX_TEST, result.unwrap().to_string()); + } + + #[test] + fn test_hash_try_from() { + let result = Hash128::try_from(HASH128_HEX_TEST); + + assert!(result.is_ok()); + assert_eq!(HASH128_HEX_TEST, result.unwrap().to_string()); + + let result = Hash160::try_from(HASH160_HEX_TEST); + + assert!(result.is_ok()); + assert_eq!(HASH160_HEX_TEST, result.unwrap().to_string()); + + let result = Hash256::try_from(HASH256_HEX_TEST); + + assert!(result.is_ok()); + assert_eq!(HASH256_HEX_TEST, result.unwrap().to_string()); + } + + #[test] + fn accept_hash_invalid_length_errors() { + let hash128 = Hash128::try_from("1000000000200000000030000000001234"); + let hash160 = Hash160::try_from("100000000020000000003000000000400000000012"); + let hash256 = + Hash256::try_from("100000000020000000003000000000400000000050000000006000000000123456"); + + assert!(hash128.is_err()); + assert!(hash160.is_err()); + assert!(hash256.is_err()); + } +} diff --git a/src/core/types/mod.rs b/src/core/types/mod.rs new file mode 100644 index 00000000..47176c6a --- /dev/null +++ b/src/core/types/mod.rs @@ -0,0 +1,125 @@ +//! Top-level exports for types used in binary_codec. + +pub mod account_id; +pub mod amount; +pub mod blob; +pub mod currency; +pub mod exceptions; +pub mod hash; +pub mod paths; +pub(crate) mod test_cases; +pub mod utils; +pub mod vector256; + +pub use self::account_id::AccountId; +pub use self::amount::Amount; +pub use self::blob::Blob; +pub use self::currency::Currency; +pub use self::hash::Hash; +pub use self::hash::Hash128; +pub use self::hash::Hash160; +pub use self::hash::Hash256; +pub use self::paths::Path; +pub use self::paths::PathSet; +pub use self::paths::PathStep; +pub use self::vector256::Vector256; + +use crate::core::BinaryParser; +use alloc::vec::Vec; + +/// Contains a serialized buffer of a Serializer type. +#[derive(Debug)] +pub struct SerializedType(Vec); + +/// Class for serializing and deserializing Lists of objects. +/// +/// See Array Fields: +/// `` +#[derive(Debug)] +pub struct SerializedList(SerializedType); + +/// Class for serializing/deserializing Indexmaps of objects. +/// +/// See Object Fields: +/// `` +#[derive(Debug)] +pub struct SerializedMap(SerializedType); + +/// An XRPL Type will implement this trait. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::types::XRPLType; +/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; +/// +/// pub struct Example(Vec); +/// +/// impl XRPLType for Example { +/// type Error = XRPLBinaryCodecException; +/// +/// fn new(buffer: Option<&[u8]>) -> Result { +/// if let Some(data) = buffer { +/// Ok(Example(data.to_vec())) +/// } else { +/// Ok(Example(vec![])) +/// } +/// } +/// } +/// ``` +pub trait XRPLType { + /// Error type for implementing type. + type Error; + + /// Create a new instance of a type. + fn new(buffer: Option<&[u8]>) -> Result + where + Self: Sized; +} + +/// Converter for transforming a BinaryParser into a type. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::core::types::TryFromParser; +/// use xrpl::core::binarycodec::BinaryParser; +/// use xrpl::core::Parser; +/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException; +/// +/// pub struct Example(Vec); +/// +/// impl TryFromParser for Example { +/// type Error = XRPLBinaryCodecException; +/// +/// fn from_parser( +/// parser: &mut BinaryParser, +/// _length: Option, +/// ) -> Result { +/// Ok(Example(parser.read(42)?)) +/// } +/// } +/// ``` +pub trait TryFromParser { + /// Error type for implementing type. + type Error; + + /// Construct a type from a BinaryParser. + fn from_parser(parser: &mut BinaryParser, length: Option) -> Result + where + Self: Sized; +} + +impl<'value, T> From for SerializedType +where + T: XRPLType + AsRef<[u8]>, +{ + /// Create a serialized type from an XRPLType. + fn from(instance: T) -> Self { + SerializedType(instance.as_ref().to_vec()) + } +} diff --git a/src/core/types/paths.rs b/src/core/types/paths.rs new file mode 100644 index 00000000..2151343c --- /dev/null +++ b/src/core/types/paths.rs @@ -0,0 +1,614 @@ +//! Codec for serializing and deserializing PathSet fields. +//! +//! See PathSet Fields: +//! `` + +use crate::constants::ACCOUNT_ID_LENGTH; +use crate::core::binarycodec::exceptions::XRPLBinaryCodecException; +use crate::core::types::exceptions::XRPLHashException; +use crate::core::types::utils::CURRENCY_CODE_LENGTH; +use crate::core::types::AccountId; +use crate::core::types::Currency; +use crate::core::types::*; +use crate::core::BinaryParser; +use crate::core::Parser; +use alloc::borrow::ToOwned; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; +use indexmap::IndexMap; +use serde::ser::SerializeMap; +use serde::ser::SerializeSeq; +use serde::Serializer; +use serde::{Deserialize, Serialize}; + +// Constant Keys +const _ACC_KEY: &str = "account"; +const _CUR_KEY: &str = "currency"; +const _ISS_KEY: &str = "issuer"; + +// Constant for masking types of a PathStep +const _TYPE_ACCOUNT: u8 = 0x01; +const _TYPE_CURRENCY: u8 = 0x10; +const _TYPE_ISSUER: u8 = 0x20; + +// Constants for separating Paths in a PathSet +const _PATHSET_END_BYTE: u8 = 0x00; +const _PATH_SEPARATOR_BYTE: u8 = 0xFF; + +#[derive(Debug, Serialize, Deserialize, Clone)] +struct PathStepData { + #[serde(skip_serializing)] + index: u8, + #[serde(skip_serializing_if = "Option::is_none")] + account: Option, + #[serde(skip_serializing_if = "Option::is_none")] + currency: Option, + #[serde(skip_serializing_if = "Option::is_none")] + issuer: Option, +} + +/// Serialize and deserialize a single step in a Path. +#[derive(Debug, Clone)] +pub struct PathStep(Vec); + +/// Class for serializing/deserializing Paths. +#[derive(Debug, Clone)] +pub struct Path(Vec); + +/// Class for serializing/deserializing Paths. +#[derive(Debug, Clone)] +pub struct PathSet(Vec); + +/// Helper function to determine if a dictionary represents +/// a valid path step. +fn _is_path_step(value: &IndexMap) -> bool { + value.contains_key(_ISS_KEY) || value.contains_key(_ACC_KEY) || value.contains_key(_CUR_KEY) +} + +/// Helper function to determine if a list represents a +/// valid path set. +fn _is_path_set(value: &[Vec>]) -> bool { + value.is_empty() || value[0].is_empty() || _is_path_step(&value[0][0]) +} + +impl XRPLType for PathStep { + type Error = XRPLBinaryCodecException; + + fn new(buffer: Option<&[u8]>) -> Result { + if let Some(data) = buffer { + Ok(PathStep(data.to_vec())) + } else { + Ok(PathStep(vec![])) + } + } +} + +impl XRPLType for Path { + type Error = XRPLBinaryCodecException; + + fn new(buffer: Option<&[u8]>) -> Result { + if let Some(data) = buffer { + Ok(Path(data.to_vec())) + } else { + Ok(Path(vec![])) + } + } +} + +impl XRPLType for PathSet { + type Error = XRPLBinaryCodecException; + + fn new(buffer: Option<&[u8]>) -> Result { + if let Some(data) = buffer { + Ok(PathSet(data.to_vec())) + } else { + Ok(PathSet(vec![])) + } + } +} + +impl PathStepData { + /// Constract a new instance of PathStepData. + pub fn new(account: Option, currency: Option, issuer: Option) -> Self { + Self { + index: _PATHSET_END_BYTE | _TYPE_ACCOUNT, + account, + currency, + issuer, + } + } +} + +impl TryFromParser for PathStep { + type Error = XRPLBinaryCodecException; + + /// Build PathStep from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + _length: Option, + ) -> Result { + let mut value_bytes: Vec = vec![]; + let mut buffer: Vec = vec![]; + let data_type = parser.read_uint8()?; + + if data_type & _TYPE_ACCOUNT != 0 { + value_bytes.extend_from_slice(&parser.read(ACCOUNT_ID_LENGTH)?); + }; + + if data_type & _TYPE_CURRENCY != 0 { + value_bytes.extend_from_slice(&parser.read(CURRENCY_CODE_LENGTH)?); + }; + + if data_type & _TYPE_ISSUER != 0 { + value_bytes.extend_from_slice(&parser.read(ACCOUNT_ID_LENGTH)?); + }; + + buffer.extend_from_slice(&[data_type]); + buffer.extend_from_slice(&value_bytes); + + PathStep::new(Some(&buffer)) + } +} + +impl TryFromParser for PathStepData { + type Error = XRPLHashException; + + /// Build PathStepData from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + _length: Option, + ) -> Result { + let account: Option; + let currency: Option; + let issuer: Option; + let data_type = parser.read_uint8()?; + + if data_type & _TYPE_ACCOUNT != 0 { + let data = AccountId::from_parser(parser, None)?; + account = Some(data.to_string()); + } else { + account = None; + } + + if data_type & _TYPE_CURRENCY != 0 { + let data = Currency::from_parser(parser, None)?; + currency = Some(data.to_string()); + } else { + currency = None; + } + + if data_type & _TYPE_ISSUER != 0 { + let data = AccountId::from_parser(parser, None)?; + issuer = Some(data.to_string()); + } else { + issuer = None; + } + + Ok(PathStepData::new(account, currency, issuer)) + } +} + +impl TryFromParser for Path { + type Error = XRPLBinaryCodecException; + + /// Build Path from a BinaryParser. + fn from_parser(parser: &mut BinaryParser, _length: Option) -> Result { + let mut buffer: Vec = vec![]; + + while !parser.is_end(None) { + let pathstep = PathStep::from_parser(parser, None)?; + buffer.extend_from_slice(pathstep.as_ref()); + + if parser.peek() == Some([_PATHSET_END_BYTE; 1]) + || parser.peek() == Some([_PATH_SEPARATOR_BYTE; 1]) + { + break; + } + } + + Path::new(Some(&buffer)) + } +} + +impl TryFromParser for PathSet { + type Error = XRPLBinaryCodecException; + + /// Build PathSet from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + _length: Option, + ) -> Result { + let mut buffer: Vec = vec![]; + + while !parser.is_end(None) { + let path = Path::from_parser(parser, None)?; + + buffer.extend_from_slice(path.as_ref()); + buffer.extend_from_slice(&parser.read(1)?); + + let len = buffer.len(); + + if buffer[len - 1] == _PATHSET_END_BYTE { + break; + } + } + + PathSet::new(Some(&buffer)) + } +} + +impl Serialize for PathStep { + /// Returns the JSON representation of a PathStep. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut parser = BinaryParser::from(self.as_ref()); + let result = PathStepData::from_parser(&mut parser, None); + + if let Ok(pathdata) = result { + let mut builder = serializer.serialize_map(None)?; + + for path in pathdata { + let (key, value) = path; + if let Some(data) = value { + builder.serialize_entry(&key, &data)?; + } else { + continue; + } + } + + builder.end() + } else { + Err(serde::ser::Error::custom(result.unwrap_err())) + } + } +} + +impl Serialize for Path { + /// Returns the JSON representation of a Path. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut sequence = serializer.serialize_seq(None)?; + let mut parser = BinaryParser::from(self.as_ref()); + + while !parser.is_end(None) { + let pathstep = PathStep::from_parser(&mut parser, None); + + if let Ok(step) = pathstep { + sequence.serialize_element(&step)?; + } else { + return Err(serde::ser::Error::custom(pathstep.unwrap_err())); + } + } + + sequence.end() + } +} + +impl Serialize for PathSet { + /// Returns the JSON representation of a Path. + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut sequence = serializer.serialize_seq(None)?; + let mut parser = BinaryParser::from(self.as_ref()); + + while !parser.is_end(None) { + let path = Path::from_parser(&mut parser, None); + + if let Ok(step) = path { + sequence.serialize_element(&step)?; + + if let Err(err) = parser.skip_bytes(1) { + return Err(serde::ser::Error::custom(err)); + }; + } else { + return Err(serde::ser::Error::custom(path.unwrap_err())); + } + } + + sequence.end() + } +} + +impl TryFrom> for PathStep { + type Error = XRPLHashException; + + /// Construct a PathStep object from a dictionary. + fn try_from(value: IndexMap) -> Result { + let mut value_bytes: Vec = vec![]; + let mut data_type = 0x00; + let mut buffer = vec![]; + + if value.contains_key(_ACC_KEY) { + let data = AccountId::try_from(value[_ACC_KEY].as_ref())?; + data_type |= _TYPE_ACCOUNT; + + value_bytes.extend_from_slice(data.as_ref()); + }; + + if value.contains_key(_CUR_KEY) { + let data = Currency::try_from(value[_CUR_KEY].as_ref())?; + data_type |= _TYPE_CURRENCY; + + value_bytes.extend_from_slice(data.as_ref()); + }; + + if value.contains_key(_ISS_KEY) { + let data = AccountId::try_from(value[_ISS_KEY].as_ref())?; + data_type |= _TYPE_ISSUER; + + value_bytes.extend_from_slice(data.as_ref()); + }; + + buffer.extend_from_slice(&[data_type]); + buffer.extend_from_slice(&value_bytes); + + Ok(Self::new(Some(&buffer))?) + } +} + +impl TryFrom>> for Path { + type Error = XRPLHashException; + + /// Construct a Path object from a list. + fn try_from(value: Vec>) -> Result { + let mut buffer: Vec = vec![]; + + for step in value { + let pathstep = PathStep::try_from(step)?; + buffer.extend_from_slice(pathstep.as_ref()); + } + + Ok(Path::new(Some(&buffer))?) + } +} + +impl TryFrom>>> for PathSet { + type Error = XRPLBinaryCodecException; + + /// Construct a PathSet object from a list. + fn try_from(value: Vec>>) -> Result { + if _is_path_set(&value) { + let mut buffer: Vec = vec![]; + + for path_val in value { + let result = Path::try_from(path_val); + + if let Ok(path) = result { + buffer.extend_from_slice(path.as_ref()); + buffer.extend_from_slice(&[_PATH_SEPARATOR_BYTE; 1]); + } else { + return Err(XRPLBinaryCodecException::InvalidPathSetFromValue); + } + } + + let len = buffer.len(); + buffer[len - 1] = _PATHSET_END_BYTE; + + PathSet::new(Some(&buffer)) + } else { + Err(XRPLBinaryCodecException::InvalidPathSetFromValue) + } + } +} + +impl TryFrom<&str> for Path { + type Error = XRPLHashException; + + /// Construct a Path object from a string. + fn try_from(value: &str) -> Result { + let json: Vec> = serde_json::from_str(value)?; + Self::try_from(json) + } +} + +impl TryFrom<&str> for PathSet { + type Error = XRPLBinaryCodecException; + + /// Construct a PathSet object from a string. + fn try_from(value: &str) -> Result { + let json: Vec>> = serde_json::from_str(value)?; + Self::try_from(json) + } +} + +impl AsRef<[u8]> for PathStep { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for Path { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl AsRef<[u8]> for PathSet { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Iterator for PathStepData { + type Item = (String, Option); + + fn next(&mut self) -> Option + where + Self: Sized, + { + if self.index & _TYPE_ACCOUNT != 0 { + self.index = _TYPE_CURRENCY; + Some((_ACC_KEY.to_string(), self.account.to_owned())) + } else if self.index & _TYPE_CURRENCY != 0 { + self.index = _TYPE_ISSUER; + Some((_CUR_KEY.to_string(), self.currency.to_owned())) + } else if self.index & _TYPE_ISSUER != 0 { + self.index = _PATHSET_END_BYTE; + Some((_ISS_KEY.to_string(), self.issuer.to_owned())) + } else { + None + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::core::types::test_cases::TEST_PATH_BUFFER; + use crate::core::types::test_cases::TEST_PATH_SET_BUFFER; + use crate::core::types::test_cases::TEST_PATH_STEP_BUFFER; + + pub const PATH_SET_TEST: &str = include_str!("../test_data/path-set-test.json"); + pub const PATH_TEST: &str = include_str!("../test_data/path-test.json"); + + #[test] + fn test_is_path_step() { + let json: Vec>> = + serde_json::from_str(PATH_SET_TEST).expect(""); + assert!(_is_path_set(&json)); + } + + #[test] + fn test_is_path_set() { + let json: Vec>> = + serde_json::from_str(PATH_SET_TEST).expect(""); + + for path in json { + for step in path { + assert!(_is_path_step(&step)); + } + } + } + + #[test] + fn test_pathstep_new() { + for data in TEST_PATH_STEP_BUFFER { + let hex: Vec = hex::decode(data).expect(""); + let pathstep = PathStep::new(Some(&hex)).unwrap(); + + assert_eq!(pathstep.as_ref(), hex); + } + } + + #[test] + fn test_path_new() { + let hex: Vec = hex::decode(TEST_PATH_BUFFER).expect(""); + let path = Path::new(Some(&hex)).unwrap(); + + assert_eq!(hex::encode_upper(path.as_ref()), TEST_PATH_BUFFER); + } + + #[test] + fn test_pathset_new() { + let hex: Vec = hex::decode(TEST_PATH_SET_BUFFER).expect(""); + let pathset = PathSet::new(Some(&hex)).unwrap(); + + assert_eq!(hex::encode_upper(pathset.as_ref()), TEST_PATH_SET_BUFFER); + } + + #[test] + fn test_pathstep_from_parser() { + for data in TEST_PATH_STEP_BUFFER { + let hex = hex::decode(data).expect(""); + let mut parser = BinaryParser::from(hex.clone()); + let pathset = PathStep::from_parser(&mut parser, None).unwrap(); + + assert_eq!(pathset.as_ref(), hex); + } + } + + #[test] + fn test_path_from_parser() { + let hex = hex::decode(TEST_PATH_BUFFER).expect(""); + let mut parser = BinaryParser::from(hex.clone()); + let pathset = Path::from_parser(&mut parser, None).unwrap(); + + assert_eq!(pathset.as_ref(), hex); + } + + #[test] + fn test_pathset_from_parser() { + let hex = hex::decode(TEST_PATH_SET_BUFFER).expect(""); + let mut parser = BinaryParser::from(hex.clone()); + let pathset = PathSet::from_parser(&mut parser, None).unwrap(); + + assert_eq!(pathset.as_ref(), hex); + } + + #[test] + fn test_pathstep_try_from() { + let json: Vec> = serde_json::from_str(PATH_TEST).expect(""); + let mut pathsteps: Vec = vec![]; + + for map in json { + pathsteps.extend_from_slice(PathStep::try_from(map.clone()).unwrap().as_ref()); + } + + assert_eq!(hex::encode_upper(pathsteps), TEST_PATH_BUFFER); + } + + #[test] + fn test_path_try_from() { + let hex = hex::decode(TEST_PATH_BUFFER).expect(""); + let path = Path::try_from(PATH_TEST).unwrap(); + + assert_eq!(path.as_ref(), hex) + } + + #[test] + fn test_pathset_try_from() { + let hex = hex::decode(TEST_PATH_SET_BUFFER).expect(""); + let pathset = PathSet::try_from(PATH_SET_TEST).unwrap(); + + assert_eq!(pathset.as_ref(), hex) + } + + #[test] + fn test_pathstep_to_json() { + let json: Vec> = serde_json::from_str(PATH_TEST).expect(""); + + for map in json { + let pathstep = PathStep::try_from(map.clone()).unwrap(); + assert_eq!( + serde_json::to_string(&pathstep).unwrap(), + serde_json::to_string(&map).expect(""), + ); + } + } + + #[test] + fn test_path_to_json() { + let hex: Vec = hex::decode(TEST_PATH_BUFFER).unwrap(); + let path = Path::new(Some(&hex)).unwrap(); + let compact: serde_json::Value = serde_json::from_str(PATH_TEST).unwrap(); + + assert_eq!( + serde_json::to_string(&path).unwrap(), + serde_json::to_string(&compact).expect(""), + ); + } + + #[test] + fn test_pathset_to_json() { + let hex: Vec = hex::decode(TEST_PATH_SET_BUFFER).expect(""); + let compact: serde_json::Value = serde_json::from_str(PATH_SET_TEST).expect(""); + let pathset = PathSet::new(Some(&hex)).unwrap(); + + assert_eq!( + serde_json::to_string(&pathset).unwrap(), + serde_json::to_string(&compact).expect(""), + ); + } +} diff --git a/src/core/types/test_cases.rs b/src/core/types/test_cases.rs new file mode 100644 index 00000000..c8dda4b5 --- /dev/null +++ b/src/core/types/test_cases.rs @@ -0,0 +1,51 @@ +use crate::core::types::amount::IssuedCurrency; +use alloc::string::String; +use serde::Deserialize; + +#[derive(Debug, Deserialize, Clone)] +pub struct IOUCase(pub(crate) IssuedCurrency, pub(crate) String); + +pub const TEST_XRP_CASES: [(&str, &str); 2] = [ + ("100", "4000000000000064"), + ("100000000000000000", "416345785D8A0000"), +]; + +pub const TEST_PATH_SET_BUFFER: &str = "31585E1F3BD02A15D6\ +185F8BB9B57CC60DEDDB37C10000000000000000000000004254430000000000\ +585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D694C\ +8531CDEECBE84F33670000000000000000000000004254430000000000E4FE68\ +7C90257D3D2D694C8531CDEECBE84F3367310A20B3C85F482532A9578DBB3950\ +B85CA06594D100000000000000000000000042544300000000000A20B3C85F48\ +2532A9578DBB3950B85CA06594D1300000000000000000000000005553440000\ +0000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A15\ +D6185F8BB9B57CC60DEDDB37C100000000000000000000000042544300000000\ +00585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C131E4FE687C90257D3D2D69\ +4C8531CDEECBE84F33670000000000000000000000004254430000000000E4FE\ +687C90257D3D2D694C8531CDEECBE84F33673115036E2D3F5437A83E5AC3CAEE\ +34FF2C21DEB618000000000000000000000000425443000000000015036E2D3F\ +5437A83E5AC3CAEE34FF2C21DEB6183000000000000000000000000055534400\ +000000000A20B3C85F482532A9578DBB3950B85CA06594D1FF31585E1F3BD02A\ +15D6185F8BB9B57CC60DEDDB37C1000000000000000000000000425443000000\ +0000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C13157180C769B66D942EE\ +69E6DCC940CA48D82337AD000000000000000000000000425443000000000057\ +180C769B66D942EE69E6DCC940CA48D82337AD10000000000000000000000000\ +00000000000000003000000000000000000000000055534400000000000A20B3\ +C85F482532A9578DBB3950B85CA06594D100"; + +pub const TEST_PATH_BUFFER: &str = "31585E1F3BD02A15D6185F8BB9B5\ +7CC60DEDDB37C10000000000000000000000004254430000000000585E1F3BD0\ +2A15D6185F8BB9B57CC60DEDDB37C13157180C769B66D942EE69E6DCC940CA48\ +D82337AD000000000000000000000000425443000000000057180C769B66D942\ +EE69E6DCC940CA48D82337AD1000000000000000000000000000000000000000\ +003000000000000000000000000055534400000000000A20B3C85F482532A957\ +8DBB3950B85CA06594D1"; + +pub const TEST_PATH_STEP_BUFFER: [&str; 4] = [ + "31585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C100000000000000000\ + 00000004254430000000000585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C1", + "3157180C769B66D942EE69E6DCC940CA48D82337AD00000000000000000\ + 0000000425443000000000057180C769B66D942EE69E6DCC940CA48D82337AD", + "100000000000000000000000000000000000000000", + "3000000000000000000000000055534400000000000A20B3C85F482532A\ + 9578DBB3950B85CA06594D1", +]; diff --git a/src/core/types/utils.rs b/src/core/types/utils.rs new file mode 100644 index 00000000..9830f8a4 --- /dev/null +++ b/src/core/types/utils.rs @@ -0,0 +1,9 @@ +/// Length of a Hash128 +pub(crate) const HASH128_LENGTH: usize = 16; +/// Length of a Hash160 +pub(crate) const HASH160_LENGTH: usize = 20; +/// Length of a Hash256 +pub(crate) const HASH256_LENGTH: usize = 32; + +/// Length of a currency code. +pub const CURRENCY_CODE_LENGTH: usize = 20; diff --git a/src/core/types/vector256.rs b/src/core/types/vector256.rs new file mode 100644 index 00000000..5f759d43 --- /dev/null +++ b/src/core/types/vector256.rs @@ -0,0 +1,157 @@ +//! Codec for serializing and deserializing +//! vectors of Hash256. + +use crate::core::types::exceptions::XRPLVectorException; +use crate::core::types::hash::Hash256; +use crate::core::types::*; +use crate::core::BinaryParser; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use core::convert::TryFrom; +use serde::ser::Error; +use serde::ser::SerializeSeq; +use serde::Serializer; +use serde::{Deserialize, Serialize}; + +const _HASH_LENGTH_BYTES: usize = 32; + +/// Codec for serializing and deserializing +/// vectors of Hash256. +#[derive(Debug, Deserialize, Clone)] +#[serde(try_from = "Vec<&str>")] +pub struct Vector256(Vec); + +impl XRPLType for Vector256 { + type Error = XRPLVectorException; + + fn new(buffer: Option<&[u8]>) -> Result { + if let Some(data) = buffer { + Ok(Vector256(data.to_vec())) + } else { + Ok(Vector256(vec![])) + } + } +} + +impl TryFromParser for Vector256 { + type Error = XRPLVectorException; + + /// Build Vector256 from a BinaryParser. + fn from_parser( + parser: &mut BinaryParser, + length: Option, + ) -> Result { + let mut bytes = vec![]; + let num_bytes: usize; + let num_hashes: usize; + + if let Some(value) = length { + num_bytes = value; + } else { + num_bytes = parser.len(); + } + + num_hashes = num_bytes / _HASH_LENGTH_BYTES; + + for _ in 0..num_hashes { + bytes.extend_from_slice(Hash256::from_parser(parser, None)?.as_ref()); + } + + Ok(Vector256(bytes)) + } +} + +impl Serialize for Vector256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if self.0.len() % _HASH_LENGTH_BYTES != 0 { + Err(S::Error::custom(XRPLVectorException::InvalidVector256Bytes)) + } else { + let mut sequence = serializer.serialize_seq(None)?; + + for i in (0..self.0.len()).step_by(_HASH_LENGTH_BYTES) { + let encoded = hex::encode_upper(&self.0[i..i + _HASH_LENGTH_BYTES]); + sequence.serialize_element(&encoded)?; + } + + sequence.end() + } + } +} + +impl TryFrom> for Vector256 { + type Error = XRPLVectorException; + + /// Construct a Vector256 from a list of strings. + fn try_from(value: Vec<&str>) -> Result { + let mut bytes = vec![]; + + for string in value { + bytes.extend_from_slice(Hash256::try_from(string)?.as_ref()) + } + + Ok(Vector256(bytes)) + } +} + +impl ToString for Vector256 { + /// Get the hex representation of the Vector256 bytes. + fn to_string(&self) -> String { + hex::encode_upper(self.as_ref()) + } +} + +impl AsRef<[u8]> for Vector256 { + /// Get a reference of the byte representation. + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + use alloc::format; + + const SERIALIZED: &str = "42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373"; + const HASH1: &str = "42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE"; + const HASH2: &str = "4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373"; + + #[test] + fn test_vector256_new() { + let bytes = hex::decode(HASH1).unwrap(); + assert_eq!(HASH1, Vector256(bytes).to_string()); + } + + #[test] + fn test_vector256_try_from_parser() { + let hex = hex::decode(SERIALIZED).expect(""); + let mut parser = BinaryParser::from(hex); + let result = Vector256::from_parser(&mut parser, None); + + assert!(result.is_ok()); + assert_eq!(SERIALIZED, result.unwrap().to_string()); + } + + #[test] + fn test_vector256_try_from() { + let result = Vector256::try_from(vec![HASH1, HASH2]); + + assert!(result.is_ok()); + assert_eq!(SERIALIZED, result.unwrap().to_string()); + } + + #[test] + fn accept_vector256_serde_encode_decode() { + let vector = Vector256::try_from(vec![HASH1, HASH2]).unwrap(); + let serialize = serde_json::to_string(&vector).unwrap(); + let deserialize: Vector256 = serde_json::from_str(&serialize).unwrap(); + + assert_eq!(format!("[\"{}\",\"{}\"]", HASH1, HASH2), serialize); + assert_eq!(SERIALIZED, deserialize.to_string()); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..a3f32abf --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright 2021 589Labs Developers. +// Licensed under the ISC License + +//! Utilities for interacting with the XRP Ledger. +//! +//! A pure Rust implementation for interacting with the XRP Ledger. The +//! xrpl-rust crate simplifies the hardest parts of XRP Ledger interaction +//! including serialization and transaction signing while providing idiomatic +//! Rust functionality for XRP Ledger transactions and core server API +//! (rippled) objects. +//! +//! # Quick Start +//! +//! TODO +//! +//! # The XRP Ledger +//! +//! For the user guide and further documentation, please read +//! [XRP Ledger](https://xrpl.org/docs.html). +#![no_std] +#![allow(dead_code)] // Remove eventually + +#[cfg(not(feature = "std"))] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std as alloc; + +pub mod constants; +#[cfg(feature = "core")] +pub mod core; +pub mod macros; +#[cfg(feature = "utils")] +pub mod utils; +pub mod wallet; + +pub extern crate indexmap; +pub extern crate serde_json; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 00000000..07fcdba5 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,9 @@ +#[macro_export] +macro_rules! skip_err { + ($result:expr) => { + match $result { + Ok(value) => Ok(value), + Err(_) => continue, + } + }; +} diff --git a/src/models/exceptions.rs b/src/models/exceptions.rs new file mode 100644 index 00000000..3c512056 --- /dev/null +++ b/src/models/exceptions.rs @@ -0,0 +1,7 @@ +/// General XRPL Model Exception. + +#[derive(Debug, PartialEq)] +pub enum XRPLModelException {} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPLModelException {} diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 00000000..3ffeb1a0 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,8 @@ +//! Top-level modules for the models package. +pub mod exceptions; +pub mod utils; + +/// TODO +pub trait FromXrpl { + pub fn from_xrpl() +} diff --git a/src/models/utils.rs b/src/models/utils.rs new file mode 100644 index 00000000..10dda9bb --- /dev/null +++ b/src/models/utils.rs @@ -0,0 +1 @@ +//! Helper util functions for the models module. diff --git a/src/utils/exceptions.rs b/src/utils/exceptions.rs new file mode 100644 index 00000000..914c9456 --- /dev/null +++ b/src/utils/exceptions.rs @@ -0,0 +1,126 @@ +//! Exception for invalid XRP Ledger amount data. + +use alloc::string::String; + +#[derive(Debug, Clone, PartialEq)] +pub enum XRPLTimeRangeException { + InvalidTimeBeforeEpoch { min: i64, found: i64 }, + UnexpectedTimeOverflow { max: i64, found: i64 }, +} + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum XRPRangeException { + InvalidXRPAmount, + InvalidICAmount, + InvalidValueContainsDecimal, + InvalidXRPAmountTooSmall { min: String, found: String }, + InvalidXRPAmountTooLarge { max: u64, found: String }, + InvalidICPrecisionTooSmall { min: i32, found: i32 }, + InvalidICPrecisionTooLarge { max: i32, found: i32 }, + InvalidDropsAmountTooLarge { max: String, found: String }, + InvalidICSerializationLength { expected: usize, found: usize }, + UnexpectedICAmountOverflow { max: usize, found: usize }, + FromHexError, + DecimalError(rust_decimal::Error), +} + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum ISOCodeException { + InvalidISOCode, + InvalidISOLength, + InvalidXRPBytes, + InvalidSerdeValue { + expected: String, + found: serde_json::Value, + }, + UnsupportedCurrencyRepresentation, + FromHexError, + Utf8Error, + DecimalError(rust_decimal::Error), +} + +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub enum JSONParseException { + ISOCodeError(ISOCodeException), + DecimalError(rust_decimal::Error), + XRPRangeError(XRPRangeException), + InvalidSerdeValue { + expected: String, + found: serde_json::Value, + }, +} + +impl From for XRPRangeException { + fn from(err: rust_decimal::Error) -> Self { + XRPRangeException::DecimalError(err) + } +} + +impl From for XRPRangeException { + fn from(_: hex::FromHexError) -> Self { + XRPRangeException::FromHexError + } +} + +impl From for ISOCodeException { + fn from(err: rust_decimal::Error) -> Self { + ISOCodeException::DecimalError(err) + } +} + +impl From for ISOCodeException { + fn from(_: core::str::Utf8Error) -> Self { + ISOCodeException::Utf8Error + } +} + +impl From for ISOCodeException { + fn from(_: hex::FromHexError) -> Self { + ISOCodeException::FromHexError + } +} + +impl From for JSONParseException { + fn from(err: XRPRangeException) -> Self { + JSONParseException::XRPRangeError(err) + } +} + +impl From for JSONParseException { + fn from(err: ISOCodeException) -> Self { + JSONParseException::ISOCodeError(err) + } +} + +impl From for JSONParseException { + fn from(err: rust_decimal::Error) -> Self { + JSONParseException::DecimalError(err) + } +} + +impl core::fmt::Display for XRPRangeException { + fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + write!(f, "XRPRangeException: {:?}", self) + } +} + +impl core::fmt::Display for ISOCodeException { + fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + write!(f, "ISOCodeException: {:?}", self) + } +} + +impl core::fmt::Display for XRPLTimeRangeException { + fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + write!(f, "XRPLTimeRangeException: {:?}", self) + } +} + +#[cfg(feature = "std")] +impl alloc::error::Error for XRPRangeException {} + +#[cfg(feature = "std")] +impl alloc::error::Error for ISOCodeException {} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 00000000..c18a644c --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,48 @@ +//! Convenience utilities for the XRP Ledger + +pub mod exceptions; +pub mod time_conversion; +pub mod xrpl_conversion; + +pub use self::time_conversion::*; +pub use self::xrpl_conversion::*; + +use crate::constants::HEX_CURRENCY_REGEX; +use alloc::vec::Vec; +use regex::Regex; + +/// Determine if the address string is a hex address. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::utils::is_hex_address; +/// +/// let value: &str = "5E7B112523F68D2F5E879DB4EAC51C6698A69304"; +/// +/// assert!(is_hex_address(value)); +/// ``` +pub fn is_hex_address(value: &str) -> bool { + let regex = Regex::new(HEX_CURRENCY_REGEX).expect("is_hex_address"); + regex.is_match(value) +} + +/// Converter to byte array with endianness. +pub trait ToBytes { + /// Return the byte array of self. + fn to_bytes(&self) -> Vec; +} + +#[cfg(test)] +mod test { + use super::*; + + const HEX_ENCODING: &str = "5E7B112523F68D2F5E879DB4EAC51C6698A69304"; + + #[test] + fn test_is_hex_address() { + assert!(is_hex_address(HEX_ENCODING)); + } +} diff --git a/src/utils/time_conversion.rs b/src/utils/time_conversion.rs new file mode 100644 index 00000000..e15e5da0 --- /dev/null +++ b/src/utils/time_conversion.rs @@ -0,0 +1,189 @@ +//! Conversions between the XRP Ledger's 'Ripple Epoch' time and native time +//! data types. + +use crate::utils::exceptions::XRPLTimeRangeException; +use chrono::DateTime; +use chrono::TimeZone; +use chrono::Utc; + +/// The "Ripple Epoch" of 2000-01-01T00:00:00 UTC +pub const RIPPLE_EPOCH: i64 = 946684800; +/// The maximum time that can be expressed on the XRPL +pub const MAX_XRPL_TIME: i64 = i64::pow(2, 32); + +/// Ensures time does not exceed max representable on XRPL. +fn _ripple_check_max(time: i64, ok: T) -> Result { + if !(0..=MAX_XRPL_TIME).contains(&time) { + Err(XRPLTimeRangeException::UnexpectedTimeOverflow { + max: MAX_XRPL_TIME, + found: time, + }) + } else { + Ok(ok) + } +} + +/// Convert from XRP Ledger 'Ripple Epoch' time to a UTC datetime. +/// Used internally. +/// See [`chrono::DateTime`] +/// +/// [`chrono::DateTime`]: mod@chrono::DateTime +/// ``` +pub(crate) fn ripple_time_to_datetime( + ripple_time: i64, +) -> Result, XRPLTimeRangeException> { + _ripple_check_max(ripple_time, Utc.timestamp(ripple_time + RIPPLE_EPOCH, 0)) +} + +/// Convert from a [`chrono::DateTime`] object to an XRP Ledger +/// 'Ripple Epoch' time. +/// Used internally. +/// +/// [`chrono::DateTime`]: mod@chrono::DateTime +/// ``` +pub(crate) fn datetime_to_ripple_time(dt: DateTime) -> Result { + let ripple_time = dt.timestamp() - RIPPLE_EPOCH; + _ripple_check_max(ripple_time, ripple_time) +} + +/// Convert from XRP Ledger 'Ripple Epoch' time to a POSIX-like +/// integer timestamp. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::utils::ripple_time_to_posix; +/// use xrpl::utils::exceptions::XRPLTimeRangeException; +/// +/// let posix: Option = match ripple_time_to_posix(946684801) { +/// Ok(time) => Some(time), +/// Err(e) => match e { +/// XRPLTimeRangeException::InvalidTimeBeforeEpoch { min: _, found: _} => None, +/// XRPLTimeRangeException::UnexpectedTimeOverflow { max: _, found: _ } => None, +/// _ => None, +/// }, +/// }; +/// +/// assert_eq!(Some(1893369601), posix); +/// ``` +pub fn ripple_time_to_posix(ripple_time: i64) -> Result { + _ripple_check_max(ripple_time, ripple_time + RIPPLE_EPOCH) +} + +/// Convert from a POSIX-like timestamp to an XRP Ledger +/// 'Ripple Epoch' time. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::utils::posix_to_ripple_time; +/// use xrpl::utils::exceptions::XRPLTimeRangeException; +/// +/// let timestamp: Option = match posix_to_ripple_time(946684801) { +/// Ok(time) => Some(time), +/// Err(e) => match e { +/// XRPLTimeRangeException::InvalidTimeBeforeEpoch { min: _, found: _} => None, +/// XRPLTimeRangeException::UnexpectedTimeOverflow { max: _, found: _ } => None, +/// _ => None, +/// }, +/// }; +/// +/// assert_eq!(Some(1), timestamp); +/// ``` +pub fn posix_to_ripple_time(timestamp: i64) -> Result { + let ripple_time = timestamp - RIPPLE_EPOCH; + _ripple_check_max(ripple_time, ripple_time) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_ripple_time_to_datetime() { + let success: DateTime = ripple_time_to_datetime(RIPPLE_EPOCH).unwrap(); + assert_eq!(success.timestamp(), RIPPLE_EPOCH + RIPPLE_EPOCH); + } + + #[test] + fn test_datetime_to_ripple_time() { + assert_eq!( + Ok(0_i64), + datetime_to_ripple_time(Utc.timestamp(RIPPLE_EPOCH, 0)) + ); + } + + #[test] + fn test_ripple_time_to_posix() { + assert_eq!( + ripple_time_to_posix(RIPPLE_EPOCH), + Ok(RIPPLE_EPOCH + RIPPLE_EPOCH) + ); + } + + #[test] + fn test_posix_to_ripple_time() { + assert_eq!(posix_to_ripple_time(RIPPLE_EPOCH), Ok(0_i64)); + } + + #[test] + fn accept_posix_round_trip() { + let current_time: i64 = Utc::now().timestamp(); + let ripple_time: i64 = posix_to_ripple_time(current_time).unwrap(); + let round_trip_time = ripple_time_to_posix(ripple_time); + + assert_eq!(Ok(current_time), round_trip_time); + } + + #[test] + fn accept_datetime_round_trip() { + let current_time: DateTime = Utc.timestamp(Utc::now().timestamp(), 0); + let ripple_time: i64 = datetime_to_ripple_time(current_time).unwrap(); + let round_trip_time = ripple_time_to_datetime(ripple_time); + + assert_eq!(Ok(current_time), round_trip_time); + } + + #[test] + fn accept_ripple_epoch() { + assert_eq!( + Ok(Utc.ymd(2000, 1, 1).and_hms(0, 0, 0)), + ripple_time_to_datetime(0) + ); + } + + /// "Ripple Epoch" time starts in the year 2000 + #[test] + fn accept_datetime_underflow() { + let datetime: DateTime = Utc.ymd(1999, 1, 1).and_hms(0, 0, 0); + assert!(datetime_to_ripple_time(datetime).is_err()) + } + + /// "Ripple Epoch" time starts in the year 2000 + #[test] + fn accept_posix_underflow() { + let datetime: DateTime = Utc.ymd(1999, 1, 1).and_hms(0, 0, 0); + assert!(posix_to_ripple_time(datetime.timestamp()).is_err()) + } + + /// "Ripple Epoch" time's equivalent to the + /// "Year 2038 problem" is not until 2136 + /// because it uses an *unsigned* 32-bit int + /// starting 30 years after UNIX time's signed + /// 32-bit int. + #[test] + fn accept_datetime_overflow() { + let datetime: DateTime = Utc.ymd(2137, 1, 1).and_hms(0, 0, 0); + assert!(datetime_to_ripple_time(datetime).is_err()) + } + + #[test] + fn accept_posix_overflow() { + let datetime: DateTime = Utc.ymd(2137, 1, 1).and_hms(0, 0, 0); + assert!(posix_to_ripple_time(datetime.timestamp()).is_err()) + } +} diff --git a/src/utils/xrpl_conversion.rs b/src/utils/xrpl_conversion.rs new file mode 100644 index 00000000..afa87eef --- /dev/null +++ b/src/utils/xrpl_conversion.rs @@ -0,0 +1,366 @@ +//! Conversions between XRP drops and native number types. + +use crate::utils::exceptions::XRPRangeException; +use alloc::format; +use alloc::string::String; +use alloc::string::ToString; +use regex::Regex; +use rust_decimal::prelude::*; +use rust_decimal::Decimal; + +/// Indivisible unit of XRP +pub(crate) const _ONE_DROP: Decimal = Decimal::from_parts(1, 0, 0, false, 6); + +/// One drop in decimal form. +pub const ONE_DROP: &str = "0.000001"; +/// 100 billion decimal XRP +pub const MAX_XRP: u64 = u64::pow(10, 11); +/// Maximum possible drops of XRP +pub const MAX_DROPS: u64 = u64::pow(10, 17); +/// Drops in one XRP +pub const XRP_DROPS: u64 = 1000000; +/// Minimum IC exponent +pub const MIN_IOU_EXPONENT: i32 = -96; +/// Maximum IC exponent +pub const MAX_IOU_EXPONENT: i32 = 80; +/// Maximum IC precision +pub const MAX_IOU_PRECISION: u8 = 16; + +/// TODO Make less bootleg +/// Get the precision of a number. +fn _calculate_precision(value: &str) -> Result { + let decimal = Decimal::from_str(value)?.normalize(); + let regex = Regex::new("[^1-9]").expect("_calculate_precision"); + + if decimal.checked_rem(Decimal::ONE).is_some() { + let stripped = regex + .replace(&decimal.to_string(), "") + .replace('.', "") + .replace('0', ""); + Ok(stripped.len()) + } else { + let quantized = decimal.round_dp_with_strategy(2, RoundingStrategy::MidpointAwayFromZero); + let stripped = regex + .replace(&quantized.to_string(), "") + .replace('.', "") + .replace('0', ""); + Ok(stripped.len()) + } +} + +/// Ensure that the value after being multiplied by the +/// exponent does not contain a decimal. +fn _verify_no_decimal(decimal: Decimal) -> Result<(), XRPRangeException> { + let value: String; + let decimal = Decimal::from_u32(decimal.scale()).expect("_verify_no_decimal"); + + if decimal == Decimal::ZERO { + value = decimal.mantissa().to_string(); + } else { + value = decimal + .checked_mul(decimal) + .or(Some(Decimal::ZERO)) + .unwrap() + .to_string(); + } + + if value.contains('.') { + Err(XRPRangeException::InvalidValueContainsDecimal) + } else { + Ok(()) + } +} + +/// Convert a numeric XRP amount to drops of XRP. +/// Return an equivalent amount in drops of XRP. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::utils::xrp_to_drops; +/// use xrpl::utils::exceptions::XRPRangeException; +/// +/// let xrp: &str = "100.000001"; +/// let drops: String = "100000001".to_string(); +/// +/// let conversion: Option = match xrp_to_drops(xrp) { +/// Ok(xrp) => Some(xrp), +/// Err(e) => match e { +/// XRPRangeException::InvalidXRPAmountTooLarge { max: _, found: _ } => None, +/// XRPRangeException::InvalidXRPAmountTooSmall { min: _, found: _ } => None, +/// _ => None, +/// }, +/// }; +/// +/// assert_eq!(Some(drops), conversion); +/// ``` +pub fn xrp_to_drops(xrp: &str) -> Result { + let xrp_d = Decimal::from_str(xrp)?; + + if xrp_d < _ONE_DROP && xrp_d != Decimal::ZERO { + Err(XRPRangeException::InvalidXRPAmountTooSmall { + min: ONE_DROP.to_string(), + found: xrp.to_string(), + }) + } else if xrp_d.gt(&Decimal::new(MAX_XRP as i64, 0)) { + Err(XRPRangeException::InvalidXRPAmountTooLarge { + max: MAX_XRP, + found: xrp.into(), + }) + } else { + Ok(format!("{}", (xrp_d / _ONE_DROP).trunc())) + } +} + +/// Convert from drops to decimal XRP. +/// Return an equivalent amount of XRP from drops. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::utils::drops_to_xrp; +/// use xrpl::utils::exceptions::XRPRangeException; +/// +/// let drops: &str = "100000000"; +/// let xrp: String = "100".to_string(); +/// +/// let conversion: Option = match drops_to_xrp(drops) { +/// Ok(xrp) => Some(xrp), +/// Err(e) => match e { +/// XRPRangeException::InvalidDropsAmountTooLarge { max: _, found: _ } => None, +/// _ => None, +/// }, +/// }; +/// +/// assert_eq!(Some(xrp), conversion); +/// ``` +pub fn drops_to_xrp(drops: &str) -> Result { + let drops_d = Decimal::from_str(drops)?; + let xrp = drops_d * _ONE_DROP; + + if xrp.gt(&Decimal::new(MAX_XRP as i64, 0)) { + Err(XRPRangeException::InvalidDropsAmountTooLarge { + max: MAX_XRP.to_string(), + found: drops.to_string(), + }) + } else { + Ok(xrp.normalize().to_string()) + } +} + +/// Validate if a provided XRP amount is valid. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::utils::verify_valid_xrp_value; +/// use xrpl::utils::exceptions::XRPRangeException; +/// +/// let valid: bool = match verify_valid_xrp_value("0.000001") { +/// Ok(()) => true, +/// Err(e) => match e { +/// XRPRangeException::InvalidXRPAmountTooSmall { min: _, found: _ } => false, +/// XRPRangeException::InvalidXRPAmountTooLarge { max: _, found: _ } => false, +/// _ => false, +/// }, +/// }; +/// +/// assert!(valid); +/// ``` +pub fn verify_valid_xrp_value(xrp_value: &str) -> Result<(), XRPRangeException> { + let decimal = Decimal::from_str(xrp_value)?; + let max = Decimal::new(MAX_DROPS as i64, 0); + + match decimal { + xrp if xrp.is_zero() => Ok(()), + xrp if xrp.ge(&_ONE_DROP) && xrp.le(&max) => Ok(()), + xrp if xrp.lt(&_ONE_DROP) => Err(XRPRangeException::InvalidXRPAmountTooSmall { + min: ONE_DROP.to_string(), + found: xrp.to_string(), + }), + xrp if xrp.gt(&max) => Err(XRPRangeException::InvalidDropsAmountTooLarge { + max: MAX_XRP.to_string(), + found: xrp.to_string(), + }), + // Should never occur + _ => Err(XRPRangeException::InvalidXRPAmount), + } +} + +/// Validates the format of an issued currency amount value. +/// +/// # Examples +/// +/// ## Basic usage +/// +/// ``` +/// use xrpl::utils::verify_valid_ic_value; +/// use xrpl::utils::exceptions::XRPRangeException; +/// +/// let valid: bool = match verify_valid_ic_value("1111111111111111.0") { +/// Ok(()) => true, +/// Err(e) => match e { +/// XRPRangeException::InvalidICPrecisionTooSmall { min: _, found: _ } => false, +/// XRPRangeException::InvalidICPrecisionTooLarge { max: _, found: _ } => false, +/// _ => false, +/// }, +/// }; +/// +/// assert!(valid); +/// ``` +pub fn verify_valid_ic_value(ic_value: &str) -> Result<(), XRPRangeException> { + let decimal = Decimal::from_str(ic_value)?.normalize(); + let scale = -(decimal.scale() as i32); + let prec = _calculate_precision(ic_value)?; + + match decimal { + ic if ic.is_zero() => Ok(()), + _ if prec > MAX_IOU_PRECISION as usize || scale > MAX_IOU_EXPONENT as i32 => { + Err(XRPRangeException::InvalidICPrecisionTooLarge { + max: MAX_IOU_EXPONENT as i32, + found: scale, + }) + } + _ if prec > MAX_IOU_PRECISION as usize || scale < MIN_IOU_EXPONENT as i32 => { + Err(XRPRangeException::InvalidICPrecisionTooSmall { + min: MIN_IOU_EXPONENT as i32, + found: scale as i32, + }) + } + _ => _verify_no_decimal(decimal), + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::alloc::string::ToString; + extern crate std; + + #[test] + fn test_one_drop_decimal() { + assert_eq!(Ok(_ONE_DROP), Decimal::from_str("0.000001")); + } + + #[test] + fn test_xrp_to_drops() { + assert_eq!( + Ok((100 * XRP_DROPS + 1).to_string()), + xrp_to_drops("100.000001") + ); + } + + #[test] + fn test_drops_to_xrp() { + assert_eq!( + drops_to_xrp("100000001"), + Ok(Decimal::new(100000001, 6).to_string()) + ); + } + + #[test] + fn test_verify_valid_xrp_value() { + assert!(verify_valid_xrp_value(ONE_DROP).is_ok()); + assert!(verify_valid_xrp_value(&MAX_DROPS.to_string()).is_ok()); + assert!(verify_valid_xrp_value("0.0000001").is_err()); + assert!(verify_valid_xrp_value("100000000000000001").is_err()); + } + + #[test] + fn test_verify_valid_ic_value() { + // { zero, pos, negative } * fractional, large, small + let valid = [ + "0", + "0.0", + "1", + "1.1111", + "-1", + "-1.1", + "1111111111111111.0", + "-1111111111111111.0", + "0.00000000001", + "0.00000000001", + "-0.00000000001", + "0.001111111111111111", + "-0.001111111111111111", + ]; + + let invalid = ["-0.0011111111111111111"]; + + for case in valid { + assert!(verify_valid_ic_value(case).is_ok()); + } + + for case in invalid { + assert!(verify_valid_ic_value(case).is_err()); + } + } + + #[test] + fn accept_one_xrp() { + assert_eq!(xrp_to_drops("1"), Ok("1000000".to_string())); + } + + #[test] + fn accept_zero_xrp() { + assert_eq!(xrp_to_drops("0"), Ok("0".to_string())); + } + + #[test] + fn accept_min_xrp() { + assert_eq!(xrp_to_drops("0.000001"), Ok("1".to_string())); + } + + #[test] + fn accept_max_xrp() { + assert_eq!( + xrp_to_drops(&MAX_XRP.to_string()), + Ok("100000000000000000".to_string()) + ); + } + + #[test] + fn accept_too_small_xrp() { + assert!(xrp_to_drops("0.0000001").is_err()); + } + + #[test] + fn accept_too_big_xrp() { + let xrp = (MAX_XRP + 1).to_string(); + assert!(xrp_to_drops(&xrp).is_err()); + } + + #[test] + fn accept_one_drop() { + assert_eq!(drops_to_xrp("1"), Ok(ONE_DROP.to_string())); + } + + #[test] + fn accept_zero_drops() { + assert_eq!(drops_to_xrp("0"), Ok("0".to_string())); + } + + #[test] + fn accept_1mil_drops() { + assert_eq!(drops_to_xrp("1000000"), Ok(Decimal::new(1, 0).to_string())); + } + + #[test] + fn accept_max_drops() { + assert_eq!( + drops_to_xrp(&MAX_DROPS.to_string()), + Ok(Decimal::new(MAX_XRP as i64, 0).to_string()) + ); + } + + #[test] + fn accept_too_big_drops() { + assert!(xrp_to_drops(&(MAX_XRP + 1).to_string()).is_err()); + } +} diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs new file mode 100644 index 00000000..5864d07a --- /dev/null +++ b/src/wallet/mod.rs @@ -0,0 +1,87 @@ +//! Methods for working with XRPL wallets. + +use crate::constants::CryptoAlgorithm; +use crate::core::addresscodec::classic_address_to_xaddress; +use crate::core::addresscodec::exceptions::XRPLAddressCodecException; +use crate::core::keypairs::derive_classic_address; +use crate::core::keypairs::derive_keypair; +use crate::core::keypairs::exceptions::XRPLKeypairsException; +use crate::core::keypairs::generate_seed; +use alloc::borrow::Cow; +use alloc::format; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec; + +/// The cryptographic keys needed to control an +/// XRP Ledger account. +/// +/// See Cryptographic Keys: +/// `` +struct Wallet { + /// The seed from which the public and private keys + /// are derived. + seed: String, + /// The public key that is used to identify this wallet's + /// signatures, as a hexadecimal string. + public_key: Cow<'static, str>, + /// The private key that is used to create signatures, as + /// a hexadecimal string. MUST be kept secret! + /// + /// TODO Use seckey + private_key: Cow<'static, str>, + /// The address that publicly identifies this wallet, as + /// a base58 string. + classic_address: Cow<'static, str>, + /// The next available sequence number to use for + /// transactions from this wallet. Must be updated by the + /// user. Increments on the ledger with every successful + /// transaction submission, and stays the same with every + /// failed transaction submission. + sequence: u64, +} + +impl Wallet { + /// Generate a new Wallet. + pub fn new(seed: &str, sequence: u64) -> Result { + let (public_key, private_key) = derive_keypair(seed, false)?; + let classic_address = derive_classic_address(&public_key)?; + + Ok(Wallet { + seed: seed.into(), + public_key: public_key.into(), + private_key: private_key.into(), + classic_address: classic_address.into(), + sequence, + }) + } + + /// Generates a new seed and Wallet. + pub fn create( + crypto_algorithm: Option, + ) -> Result { + Self::new(&generate_seed(None, crypto_algorithm)?, 0) + } + + /// Returns the X-Address of the Wallet's account. + pub fn get_xaddress( + &self, + tag: Option, + is_test_network: bool, + ) -> Result { + classic_address_to_xaddress(&self.classic_address, tag, is_test_network) + } +} + +impl ToString for Wallet { + /// Returns a string representation of a Wallet. + fn to_string(&self) -> String { + let string_list = vec![ + format!("public_key: {}", self.public_key), + format!("private_key: {}", "-HIDDEN-"), + format!("classic_address: {}", self.classic_address), + ]; + + string_list.join("-") + } +} diff --git a/src/wallet/wallet_generation.rs b/src/wallet/wallet_generation.rs new file mode 100644 index 00000000..e69de29b