Giter Site home page Giter Site logo

xrpl4j's Introduction

xrpl4j: A 100% Java SDK for the XRP Ledger

codecov issues javadoc

This project is a pure Java implementation of an SDK that works with the XRP Ledger. This library supports XRPL key and address generation, transaction serialization and signing, provides useful Java bindings for XRP Ledger objects and rippled request/response objects, and also provides a JSON-RPC client for interacting with XRPL nodes.

Documentation

  • Get Started Using Java: a tutorial for building a very simple XRP Ledger-connected app.
  • Example usage can be found in the xrpl4j-integration-tests module here.

Usage

Requirements

  • JDK 1.8 or higher
  • A Java project manager such as Maven or Gradle

Maven Installation

You can use one or more xrpl4j modules in your Maven project by using the current BOM like this:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.xrpl</groupId>
            <artifactId>xrpl4j-bom</artifactId>
            <version>3.3.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Then you can add one or both of the xrpl4j-core and xrpl4j-client modules found in the BOM to your pom.xml. For example:

<dependencies>  
  ...
  <dependency>
    <groupId>org.xrpl</groupId>
    <artifactId>xrpl4j-core</artifactId>
  </dependency>
  <dependency>
    <groupId>org.xrpl</groupId>
    <artifactId>xrpl4j-client</artifactId>
  </dependency>
  ...
</dependencies>

Core Objects

This library provides Java objects modeling XRP Ledger Objects, Transactions, and request parameters/response results for the rippled API.

The objects in this module are annotated with @Value.Immutable from the immutables library, which generates immutable implementations with builders, copy constructors, and other useful boilerplate code.

For example, the following code constructs an EscrowCreate object, which represents an EscrowCreate Transaction:

EscrowCreate escrowCreate = EscrowCreate.builder()
  .account(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"))
  .fee(XrpCurrencyAmount.ofDrops(12))
  .sequence(UnsignedInteger.ONE)
  .amount(XrpCurrencyAmount.ofDrops(10000))
  .destination(Address.of("rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW"))
  .destinationTag(UnsignedInteger.valueOf(23480))
  .cancelAfter(UnsignedLong.valueOf(533257958))
  .finishAfter(UnsignedLong.valueOf(533171558))
  .condition(CryptoConditionReader.readCondition(
  BaseEncoding.base16()
    .decode("A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100"))
  )
  .sourceTag(UnsignedInteger.valueOf(11747))
  .build();

These objects can be serialized to, and deserialized from, rippled JSON representations using the provided Jackson ObjectMapper, which can be instantiated using ObjectMapperFactory.

Using the EscrowCreate object created above, it is then possible to use the supplied ObjectMapper to serialize to JSON like this:

  ObjectMapper objectMapper = ObjectMapperFactory.create();
  String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(escrowCreate);
  System.out.println(json);

Which produces the following output:

{
  "Account" : "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
  "Fee" : "12",
  "Sequence" : 1,
  "SourceTag" : 11747,
  "Flags" : 2147483648,
  "Amount" : "10000",
  "Destination" : "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
  "DestinationTag" : 23480,
  "CancelAfter" : 533257958,
  "FinishAfter" : 533171558,
  "Condition" : "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
  "TransactionType" : "EscrowCreate"
}

Public/Private Key Material

Most operations using this library require some sort of private key material. Broadly speaking, the library supports two mechanisms: (1) in-memory private keys, and (2) in-memory references to private keys where the actual private key material lives in an external system (e.g., keys in a Hardware Security Module, or HSM). In Java, this is modeled using the PrivateKeyable interface, which has two subclasses: PrivateKey and PrivateKeyReference.

In-Memory Private Keys (PrivateKey)

PrivateKey represents a private key held in memory, existing in the same JVM that is executing xrpl4j code. This key variant can be useful in the context of an android or native application, but is likely not suitable for server-side application because private key material is held in-memory (for these scenarios, consider using a remote service like an HSM).

For use-cases that require private keys to exist inside the running JVM, the following examples shows how to generate a keypair, and also how to derive an XRPL address from there:

import org.xrpl.xrpl4j.crypto.keys.KeyPair;
import org.xrpl.xrpl4j.crypto.keys.PrivateKey;
import org.xrpl.xrpl4j.crypto.keys.PublicKey;
import org.xrpl.xrpl4j.crypto.keys.Seed;
import org.xrpl.xrpl4j.model.transactions.Address;

Seed seed = Seed.ed25519Seed(); // <-- Generates a random seed.
KeyPair keyPair = seed.deriveKeyPair(); // <-- Derive a KeyPair from the seed.
PrivateKey privateKey = keyPair.privateKey(); // <-- Derive a privateKey from the KeyPair.
PublicKey publicKey = keyPair.publicKey(); // <-- Derive a publicKey from the KeyPair.
Address address = publicKey.deriveAddress(); // <-- Derive an address from the publicKey

Private Key References (PrivateKeyReference)

For applications with higher-security requirements, private-key material can be stored outside the JVM using an external system that can simultaneously manage the key material and also perform critical signing operations without exposing key material to the outside world (e.g., an HSM or cloud service provider). For these scenarios, PrivateKeyReference can be used.

This library does not provide an implementation that interacts with any particular external signing service or HSM. However, developers wishing to support such interactions should extend PrivateKeyReference for the particular external service, and implement SignatureService for their PrivateKeyReference type. interface. In addition, FauxGcpKmsSignatureServiceTest and FauxAwsKmsSignatureServiceTest illustrate faux variants of a simulated external key provider that can also be used for further guidance.

Signing and Verifying Transactions

The main interface used to sign and verify transactions is SignatureService, which has two concrete implementations: BcSignatureService and BcDerivedKeySignatureService. The first uses in-memory private key material to perform signing and validation operations, while the latter can be used to derive multiple private keys using a single entropy source combined with differing unique key identifiers (e.g., User Ids).

Construct and Sign an XRP Payment:

The following example illustrates how to construct a payment transaction, sign it using an in-memory private key, and then submit that transaction to the XRP Ledger for processing and validation:

import org.xrpl.xrpl4j.client.XrplClient;
import org.xrpl.xrpl4j.crypto.keys.PrivateKey;
import org.xrpl.xrpl4j.crypto.keys.Seed;
import org.xrpl.xrpl4j.crypto.signing.SignatureService;
import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction;
import org.xrpl.xrpl4j.model.client.transactions.SubmitResult;
import org.xrpl.xrpl4j.model.transactions.Address;

import org.xrpl.xrpl4j.crypto.signing.bc.BcSignatureService;
import org.xrpl.xrpl4j.model.transactions.Payment;

// Construct a SignatureService that uses in-memory Keys (see SignatureService.java for alternatives).
SignatureService signatureService = new BcSignatureService();

// Sender (using ed25519 key)
Seed seed = Seed.ed25519Seed(); // <-- Generates a random seed.
PrivateKey senderPrivateKey = seed.deriveKeyPair().privateKey();
  
// Receiver (using secp256k1 key)
Address receiverAddress = Address.of("r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59");

// Construct a Payment
Payment payment = ...; // See V3 ITs for examples.

SingleSignedTransaction<Payment> signedTransaction = signatureService.sign(sourcePrivateKey,payment);
SubmitResult<Payment> result = xrplClient.submit(signedTransaction);
assert result.engineResult().equals("tesSUCCESS");

Codecs

This library relies upon two important sub-modules called Codecs (One for the XRPL binary encoding, and one for XRPL canonical JSON encoding). Read more about each here:

Development

Project Structure

Xrpl4j is structured as a Maven multi-module project, with the following modules:

  • xrpl4j-core: javadoc
    • Provides core primitives like seeds, public/private keys definitions (supports secp256k1 and ed25519 key types and signing algorithms), signature interfaces, address and binary codecs etc. Also provides Java objects which model XRP Ledger objects, as well as request parameters and response results for the rippled websocket and JSON RPC APIs.
    • Provides a Jackson ObjectMapper with JSON bindings that serialize and deserialize to and from the JSON representation of XRPL Transactions; this is used to move to and from the canonical binary format of the XRP Ledger.
  • xrpl4j-client: javadoc
    • Provides an example rippled JSON RPC client which can be used to communicate with a rippled node
  • xrpl4j-integration-tests:
    • Contains the project's integration tests, which also serve as valuable xrpl4j usage examples for common XRPL flows.

You can build and test the entire project locally using maven from the command line:

mvn clean install

To build the project while skipping Integration tests, use the following command:

mvn clean install -DskipITs

To build the project while skipping Unit and Integration tests, use the following command:

mvn clean install -DskipITs -DskipTests

xrpl4j's People

Contributors

alloynetworks avatar dependabot[bot] avatar intelliot avatar kanumalivad avatar ledhed2222 avatar mukulljangid avatar nhartner avatar nkramer44 avatar omahs avatar rajatarora08 avatar sappenin avatar tekbar11 avatar theotherian avatar tommq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xrpl4j's Issues

Remove tfFullyCanonicalSig flag from all Flags Builders

The tfFullyCanonicalSig transaction flag was recently deprecated, and no new transactions submitted without this flag will be accepted. Currently, we do allow users to set tfFullyCanonicalSig to false for transactions which have custom flags, so we should always set tfFullyCanonicalSig to true. This can be done by removing the tfFullyCanonicalSig(boolean) method from every TransactionFlags Builder and always setting it to true.

XRP Wallet only works with 16 byte seed

I'm using xrpl4j to generate xrp wallet as part of a HD wallet. I am able to generate the wallet fine when the seed size is 16 bytes (128 bits / 12 word mnemonic) but when the seed size is 32 (256 bits / 24 word mnemonic) it fails. The current workaround I have is to strip the seed of the bytes in excess of 16.

See HD wallet generation using bitcoin4j code here.

new DeterministicSeed(
        new SecureRandom(),
        256,
        Optional.ofNullable(passphrase).orElse("")
    );

See stripping of bytes in excess of 16 workaround here.

ArrayUtils.subarray(privKey.toByteArray(), 0, 16);

While this is functional, it strips the entropy and is restrictively less secure.

Requirement:

  • XRP Wallet should allow seed length greater than and equal to current length of 16 bytes for more secure wallets

Make it easier to get the amount of an XRP payment as BigDecimal

The way amounts are mapped could be more convenient to use. In most cases, people just want to get the amount in XRP as a decimal, not drops. But on things like payments, that requires doing an instanceof then a cast to XrpCurrencyAmount then no obvious way to get the decimal value (instead of drops) from there

Add Constants for TransactionResult Codes

See Transaction result codes here. It would be useful to have these defined in constants so projects using this library don't accidentally introduce typos.

E.g.:

String TES = "tes";
String TES_SUCCESS = TES + "SUCCESS";

Create a type for XRPL Timestamps

Instead of using UnsignedLongs to denote XRPL timestamps, we should just have a type that wraps UnsignedLong that also has utility constructors for Instants and other Java time API primitives

Rewrite XrplClient to mirror the rippled API completely

While most of the methods in XrplClient follow the rippled API, meaning they take a set of XrplRequestParams and return an XrplResult, some methods (such as submit) are more abstract. We should consider reducing the complexity of the client by simply mirroring the rippled API. Also, we should go through and implement the rest of the rippled public API methods that are available.

Remove `ledgerIndex()` method from Transaction.java

The method ledgerIndex() in Transaction.java is only ever present in response to a account_tx call, and even then only sometimes.

So, we can create a new wrapper class called AccountTransactionsTransaction and inside of AccountTransactionsTransactionResult we can update the transaction() method to be as follows:

  /**
   * The {@link Transaction}.
   *
   * @return A {@link T} with the transaction fields.
   */
  @JsonProperty("tx")
  AccountTransactionsTransaction<T> transaction();

If AccountTransactionsTransaction has ledgerIndex() then everything should just work during deserialization, but from a developer perspective, the ledgerIndex() will be present in the correct circumstances.

wrong type for load_factor in ServerInfo

I started getting Exceptions from serverInfo() method when connected to s2.ripple.com. I realized the library expects load_factor to be UnsignedInteger (and it usually is "1"), but rippled sometimes returns it as double.

It can be shown here: https://xrpl.org/websocket-api-tool.html?server=wss%3A%2F%2Fs1.ripple.com%2F&req=%7B%22id%22%3A1%2C%22command%22%3A%22server_info%22%7D

clicking send several times eventually get you response with some higher load like:

{
  "id": 1,
  "result": {
    "info": {
      "build_version": "1.7.0",
      "complete_ledgers": "61881385-62562429",
      "hostid": "LARD",
      "io_latency_ms": 2,
      "jq_trans_overflow": "0",
      "last_close": {
        "converge_time_s": 3.002,
        "proposers": 38
      },
      "load_factor": 511.83203125,
      "load_factor_server": 1,
      "peer_disconnects": "224008",
      "peer_disconnects_resources": "747",
      "peers": 261,
      "pubkey_node": "n9MozjnGB3tpULewtTsVtuudg5JqYFyV3QFdAtVLzJaxHcBaxuXD",
      "server_state": "full",
      "server_state_duration_us": "2274468435925",
      "state_accounting": {
        "connected": {
          "duration_us": "231197490",
          "transitions": 2
        },
        "disconnected": {
          "duration_us": "1156933",
          "transitions": 2
        },
        "full": {
          "duration_us": "2274468435925",
          "transitions": 1
        },
        "syncing": {
          "duration_us": "4065001",
          "transitions": 1
        },
        "tracking": {
          "duration_us": "63",
          "transitions": 1
        }
      },
      "time": "2021-Mar-30 15:37:51.486384 UTC",
      "uptime": 2274704,
      "validated_ledger": {
        "age": 4,
        "base_fee_xrp": 0.00001,
        "hash": "E5A958048D98D4EFEEDD2BC3F36D23893BBC1D9354CB3E739068D2DFDE3D1AA3",
        "reserve_base_xrp": 20,
        "reserve_inc_xrp": 5,
        "seq": 62562429
      },
      "validation_quorum": 31
    }
  },
  "status": "success",
  "type": "response"
}

The exception I am getting is clearly saying that jackson can't construct UnsignedInteger from 512.90234375

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.google.common.primitives.UnsignedInteger` (although at least one Creator exists): no double/Double-argument constructor/factory method to deserialize from Number value (512.90234375)
 at [Source: (String)"{"info":{"build_version":"1.7.0","complete_ledgers":"32570-62562188","hostid":"CARR","io_latency_ms":1,"jq_trans_overflow":"2942","last_close":{"converge_time_s":3.005,"proposers":38},"load_factor":512.90234375,"load_factor_server":1,"peer_disconnects":"171040","peer_disconnects_resources":"4176","peers":214,"pubkey_node":"n9Kr6UDJAd2yUn9J6bXWpRvQcKRPAEVpeV33NJVBcaDSDVt2eho2","server_state":"full","server_state_duration_us":"172918538055","state_accounting":{"connected":{"duration_us":"471959630"[truncated 524 chars]; line: 1, column: 199] (through reference chain: org.xrpl.xrpl4j.model.client.server.ImmutableServerInfoResult$Json["info"]->org.xrpl.xrpl4j.model.client.server.ImmutableServerInfo$Json["load_factor"])

I suppose there fields like load_factor_local, load_factor_net and others may be the same case. I will try to investigate this to find the proper type later.

Add HD Wallet Support

When we initially wrote xrpl4j-keypairs, we decided to skip over implementing HD Wallet support. For the sake of having a full feature set, we should add support for HD wallet generation

Fix issue with LedgerHeader transaction deserialization

XrplClient.ledger(params) method throws an exception if transactions are returned because the LedgerHeader.transactions() declaration tries to deserialize them as String instead of JSON.

The transactions that are returned can either be full JSON transaction objects, or the identifying hashes of the transactions. We could add a custom deserializer, but this kind of polymorphism isn't easy to do in Java, so I think we should just be opinionated and force the request to set expand=true when asking for transactions

Resource definitions.json not found

Hi, I run into an issue when running a project with embedded tomcat in jar with Java 11.

It causes the following Exception:

Caused by: java.lang.IllegalArgumentException: resource definitions.json not found. at com.google.common.base.Preconditions.checkArgument(Preconditions.java:217) at com.google.common.io.Resources.getResource(Resources.java:195) at org.xrpl.xrpl4j.codec.binary.definitions.DefaultDefinitionsProvider.lambda$new$0(DefaultDefinitionsProvider.java:26)

And is coming from DefaultDefinitionProvider.java:26. The definitions.json file is in the jar, just the classLoader won't resolve it.

Add validation to LedgerIndex

LedgerIndex is just a wrapper around String. There are three defined character Strings that are recognized by rippled as shortcuts: "validated", "current", and "closed". All other ledger indexes must be a numerical String like "12345".

Currently, we don't do any client side validation that the String wrapped by LedgerIndex is either one of the shortcut Strings or a number, so a user could technically construct a LedgerIndex wrapping "foo". While this will ultimately be caught by rippled, we should add validation in the LedgerIndex class to short circuit any errors.

LedgerIndex uses UnsignedLong but Transaction uses UnsignedInteger

The LedgerIndex class emits an UnsignedLong via unsignedLongValue(), but the Transaction class uses an UnsignedInteger to represent lastLedgerSequence.

We should decide which Unsigned number type is best for representing numeric XRP Ledger index values, and be consistent across the library.

LedgerResult and LedgerHeader objects break when asking for current ledger

The ledgerHash and ledgerIndex fields are not Optional in LedgerResult, nor are closeTimeHuman and parentCloseTime in LeaderHeader. These fields are always present in responses to requests for validated ledgers, but are absent when asking for the current ledger.

We should probably go ahead and make the breaking change of making these Optional

Fix Javadoc Warnings

There are a lot of missing javadocs and missing elements of certain javadocs. We should go through and improve these javadocs and make sure the maven-javadoc-plugin does not spit out any warnings

Always trigger GH Actions for forked PRs

Currently, if someone opens a PR from a forked version of xrpl4j, we have to assign the PR to someone in order for GH Actions to get triggered. This is fine for now, but if new commits get added to the PR, we have to re-assign the PR to someone else, which is a pain.

Create type for XRPL Timestamps

Instead of using UnsignedLongs to denote XRPL timestamps, we should just have a type that wraps UnsignedLong that also has utility constructors for Instants and other Java time API primitives

Re-type marker fields in request params and result objects

A lot of the request/response objects have a field called marker, which is a server defined field that can either be a String or a JSON object. Currently, our objects are typed as Optional<String>, which will break Jackson when the server sends back a JSON object in that field.

We should create a Marker type with a custom serializer/deserializer to handle this.

Reduce public API footprint

Currently, almost everything in the library is public, when only a few classes are really needed in the public API that we expose. While it's nice to give users flexibility to use bits and pieces of the library, a large API increases the amount of code we need to make sure we don't break.

We should go through the different modules and figure out which classes we want to keep public and which we can make private

Improve Request/Response Java API for improved developer experience

Anywhere there's an Optional result, we should consider a sibling method that helps a developer not have to throw an exception as an alternative case.

Example, in AccountInfoResult.java, we could add:

@JsonIgnore
default LedgerIndex ledgerIndexSafe() {
  return ledgerIndex().orElseThrow(IllegalArgumentException::new);
}

If we embrace this pattern, we should add this to every client model object.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.