GithubHelp home page GithubHelp logo

algorand / java-algorand-sdk Goto Github PK

View Code? Open in Web Editor NEW
68.0 18.0 69.0 35.52 MB

Algorand SDK for Java7+ to interact with the Algorand network

Home Page: https://algorand.github.io/java-algorand-sdk/

License: MIT License

Java 99.48% Dockerfile 0.02% Shell 0.37% Makefile 0.06% Python 0.08%

java-algorand-sdk's Introduction

java-algorand-sdk

Build Status Maven Central

AlgoSDK is a Java library for communicating and interacting with the Algorand network. It contains a REST client for accessing algod instances over the web, and also exposes functionality for generating keypairs, mnemonics, creating transactions, signing transactions, and serializing data across the network.

Prerequisites

Java 7+ and Android minSdkVersion 16+

Installation

Maven:

<dependency>
    <groupId>com.algorand</groupId>
    <artifactId>algosdk</artifactId>
    <version>2.5.0</version>
</dependency>

Quickstart

This program connects to a running sandbox private network, creates a payment transaction between two of the accounts, signs it with kmd, and reads result from Indexer.

import com.algorand.algosdk.account.Account;
import com.algorand.algosdk.crypto.Address;
import com.algorand.algosdk.kmd.client.ApiException;
import com.algorand.algosdk.kmd.client.KmdClient;
import com.algorand.algosdk.kmd.client.api.KmdApi;
import com.algorand.algosdk.kmd.client.model.*;
import com.algorand.algosdk.transaction.SignedTransaction;
import com.algorand.algosdk.transaction.Transaction;
import com.algorand.algosdk.util.Encoder;
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.IndexerClient;
import com.algorand.algosdk.v2.client.common.Response;
import com.algorand.algosdk.v2.client.model.PendingTransactionResponse;
import com.algorand.algosdk.v2.client.model.PostTransactionsResponse;
import com.algorand.algosdk.v2.client.model.TransactionsResponse;

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {
    private static String token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    private static KmdApi kmd = null;

    public static void main(String[] args) throws Exception {
        // Initialize algod/indexer v2 clients.
        AlgodClient algod = new AlgodClient("http://localhost", 4001, token);
        IndexerClient indexer = new IndexerClient("http://localhost", 8980);

        // Initialize KMD v1 client
        KmdClient kmdClient = new KmdClient();
        kmdClient.setBasePath("http://localhost:4002");
        kmdClient.setApiKey(token);
        kmd = new KmdApi(kmdClient);

        // Get accounts from sandbox.
        String walletHandle = getDefaultWalletHandle();
        List<Address> accounts  = getWalletAccounts(walletHandle);

        // Create a payment transaction
        Transaction tx1 = Transaction.PaymentTransactionBuilder()
                .lookupParams(algod) // lookup fee, firstValid, lastValid
                .sender(accounts.get(0))
                .receiver(accounts.get(1))
                .amount(1000000)
                .noteUTF8("test transaction!")
                .build();

        // Sign with KMD
        SignedTransaction stx1a = signTransactionWithKMD(tx1, walletHandle);
        byte[] stx1aBytes = Encoder.encodeToMsgPack(stx1a);

        // Sign with private key
        byte[] privateKey = lookupPrivateKey(accounts.get(0), walletHandle);
        Account account = new Account(privateKey);
        SignedTransaction stx1b = account.signTransaction(tx1);
        byte[] stx1bBytes = Encoder.encodeToMsgPack(stx1b);

        // KMD and signing directly should both be the same.
        if (!Arrays.equals(stx1aBytes, stx1bBytes)) {
            throw new RuntimeException("KMD disagrees with the manual signature!");
        }

        // Send transaction
        Response<PostTransactionsResponse> post = algod.RawTransaction().rawtxn(stx1aBytes).execute();
        if (!post.isSuccessful()) {
            throw new RuntimeException("Failed to post transaction");
        }

        // Wait for confirmation
        boolean done = false;
        while (!done) {
            Response<PendingTransactionResponse> txInfo = algod.PendingTransactionInformation(post.body().txId).execute();
            if (!txInfo.isSuccessful()) {
                throw new RuntimeException("Failed to check on tx progress");
            }
            if (txInfo.body().confirmedRound != null) {
                done = true;
            }
        }

        // Wait for indexer to index the round.
        Thread.sleep(5000);

        // Query indexer for the transaction
        Response<TransactionsResponse> transactions = indexer.searchForTransactions()
                .txid(post.body().txId)
                .execute();

        if (!transactions.isSuccessful()) {
            throw new RuntimeException("Failed to lookup transaction");
        }

        System.out.println("Transaction received! \n" + transactions.toString());
    }

    public static SignedTransaction signTransactionWithKMD(Transaction tx, String walletHandle) throws IOException, ApiException {
        SignTransactionRequest req = new SignTransactionRequest();
        req.transaction(Encoder.encodeToMsgPack(tx));
        req.setWalletHandleToken(walletHandle);
        req.setWalletPassword("");
        byte[] stxBytes = kmd.signTransaction(req).getSignedTransaction();
        return Encoder.decodeFromMsgPack(stxBytes, SignedTransaction.class);
    }

    public static byte[] lookupPrivateKey(Address addr, String walletHandle) throws ApiException {
        ExportKeyRequest req = new ExportKeyRequest();
        req.setAddress(addr.toString());
        req.setWalletHandleToken(walletHandle);
        req.setWalletPassword("");
        return kmd.exportKey(req).getPrivateKey();
    }

    public static String getDefaultWalletHandle() throws ApiException {
        for (APIV1Wallet w : kmd.listWallets().getWallets()) {
            if (w.getName().equals("unencrypted-default-wallet")) {
                InitWalletHandleTokenRequest tokenreq = new InitWalletHandleTokenRequest();
                tokenreq.setWalletId(w.getId());
                tokenreq.setWalletPassword("");
                return kmd.initWalletHandleToken(tokenreq).getWalletHandleToken();
            }
        }
        throw new RuntimeException("Default wallet not found.");
    }

    public static List<Address> getWalletAccounts(String walletHandle) throws ApiException, NoSuchAlgorithmException {
        List<Address> accounts = new ArrayList<>();

        ListKeysRequest keysRequest = new ListKeysRequest();
        keysRequest.setWalletHandleToken(walletHandle);
        for (String addr : kmd.listKeysInWallet(keysRequest).getAddresses()) {
            accounts.add(new Address(addr));
        }

        return accounts;
    }
}

Documentation

Javadoc can be found at https://algorand.github.io/java-algorand-sdk.
Additional resources and code samples are located at https://developer.algorand.org.

Cryptography

AlgoSDK depends on org.bouncycastle:bcprov-jdk15on:1.61 for Ed25519 signatures, sha512/256 digests, and deserializing X.509-encoded Ed25519 private keys. The latter is the only explicit dependency on an external crypto library - all other references are abstracted through the JCA.

Java 9+

When using cryptographic functionality, and Java9+, you may run into the following warning:

WARNING: Illegal reflective access by org.bouncycastle.jcajce.provider.drbg.DRBG

This is known behavior, caused by more restrictive language features in Java 9+, that Bouncy Castle has yet to support. This warning can be suppressed safely. We will monitor cryptographic packages for updates or alternative implementations.

Contributing to this Project

build

This project uses Maven.

To build

~$ mvn package

To test

We are using separate version targets for production and testing to allow using JUnit5 for tests. Some IDEs, like IDEA do not support this very well. To workaround the issue a special ide profile should be enabled if your IDE does not support mixed target and testTarget versions. Regardless of IDE support, the tests can be run from the command line. In this case clean is used in case an incremental build was made by the IDE with Java8.

~$ mvn clean test

There is also a special integration test environment, and shared tests. To run these use the Makefile:

~$ make docker-test

To stand up the test harness, without running the entire test suite use the Makefile:

~$ make harness

You can then run specific cucumber-based unit and integration tests directly.

deploying artifacts

The generated pom file provides maven compatibility and deploy capabilities.

mvn clean install
mvn clean deploy -P github,default
mvn clean site -P github,default  # for javadoc
mvn clean deploy -P release,default

Testing

Many cross-SDK tests are defined in algorand-sdk-testing. Some are integration tests with additional dependencies. These dependencies are containerized in a docker file, which can be executed with make docker-test.

It is occasionally useful to run locally, or against alternate integration branches. To do this:

  1. Install feature files for your test branch "./run_integration_tests.sh -feature-only -test-branch "
  2. Run locally with make integration and make unit, or from the IDE by running "RunCucumberUnitTest.java"

Android Support

Significant work has been taken to ensure Android compatibility (in particular for minSdkVersion 16). Note that the default crypto provider on Android does not provide ed25519 signatures, so you will need to provide your own (e.g. BouncyCastle).

Algod V2 and Indexer Code Generation

The classes com.algorand.algosdk.v2.client.algod.\*, com.algorand.algosdk.v2.client.indexer.\*, com.algorand.algosdk.v2.client.common.AlgodClient, and com.algorand.algosdk.v2.client.common.IndexerClient are generated from OpenAPI specifications in: algod.oas2.json and indexer.oas2.json.

The specification files can be obtained from:

A testing framework can also be generated with: com.algorand.sdkutils.RunQueryMapperGenerator and the tests run from com.algorand.sdkutils.RunAlgodV2Tests and com.algorand.sdkutils.RunIndexerTests

Regenerate the Client Code

The actual generation is done using the generate_java.sh script in the generator repo.

Updating the kmd REST client

The kmd REST client has not been upgraded to use the new code generation, it is still largely autogenerated by swagger-codegen. [https://github.com/swagger-api/swagger-codegen]

To regenerate the clients, first, check out the latest swagger-codegen from the github repo. (In particular, the Homebrew version is out of date and fails to handle raw byte arrays properly). Note OpenAPI 2.0 doesn't support unsigned types. Luckily we don't have any uint32 types in algod, so we can do a lossless type-mapping fromt uint64->int64 (Long) -> BigInteger:

curl http://localhost:8080/swagger.json | sed -e 's/uint32/int64/g' > temp.json
swagger-codegen generate -i temp.json -l java -c config.json

config.json looks like:

{
  "library": "okhttp-gson",
  "java8": false,
  "hideGenerationTimestamp": true,
  "serializableModel": false,
  "supportJava6": true,
  "invokerPackage": "com.algorand.algosdk.{kmd or algod}.client",
  "apiPackage": "com.algorand.algosdk.{kmd or algod}.client.api",
  "modelPackage": "com.algorand.algosdk.{kmd or algod}.client.model"
}

Make sure you convert all uint32 types to Long types.

The generated code (as of April 2019) has one circular dependency involving client.Pair. The client package depends on client.auth, but client.auth uses client.Pair which is in the client package. One more problem is that uint64 is not a valid format in OpenAPI 2.0; however, we need to send large integers to the algod API (kmd is fine). To resolve this, we do the following manual pass on generated code:

  • Move Pair.java into the client.lib package
  • Find-and-replace Integer with BigInteger (for uint64), Long (for uint32), etc. in com.algorand.algosdk.algod and subpackages (unnecessary for algod)
  • Run an Optimize Imports operation on generated code, to minimize dependencies.

Note that msgpack-java is good at using the minimal representation.

java-algorand-sdk's People

Contributors

ahangsu avatar algobarb avatar algojack avatar algolucky avatar algonautshant avatar algorandskiy avatar algorotem avatar ayaggarwal avatar barnjamin avatar brianolson avatar bricerisingalgorand avatar egieseke avatar eric-warehime avatar evanjrichard avatar excalq avatar fabrice102 avatar github-actions[bot] avatar gmalouf avatar jasonpaulos avatar jesulonimi21 avatar michaeldiamant avatar michaeltchuang avatar mjiang102628 avatar onetechnical avatar reguzzoni avatar rotemh avatar shiqizng avatar tzaffi avatar vervious avatar winder 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

Watchers

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

java-algorand-sdk's Issues

[macos] bazel requires AdoptJDK 8, I'm running JDK 12. How do you run the example without bazel?

$ brew install bazel
bazel: Java 1.8 is required to install this formula.
Install AdoptOpenJDK 8 with Homebrew Cask:
  brew cask install homebrew/cask-versions/adoptopenjdk8
Error: An unsatisfied requirement failed this build.

I then tried just using building it with maven, seems like it worked

$ mvn clean install
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.570 s
[INFO] Finished at: 2019-09-04T17:02:25-06:00
[INFO] ------------------------------------------------------------------------

Tests also seem to pass:

$ mvn test
...
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.algorand.algosdk.transaction.TestTransaction
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.bouncycastle.jcajce.provider.drbg.DRBG (file:/Users/gubatron/.m2/repository/org/bouncycastle/bcprov-jdk15on/1.61/bcprov-jdk15on-1.61.jar) to constructor sun.security.provider.Sun()
WARNING: Please consider reporting this to the maintainers of org.bouncycastle.jcajce.provider.drbg.DRBG
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.484 sec
Running com.algorand.algosdk.crypto.TestMultisigAddress
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 sec
Running com.algorand.algosdk.crypto.TestAddress
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.023 sec
Running com.algorand.algosdk.mnemonic.TestMnemonic
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.229 sec
Running com.algorand.algosdk.account.TestAccount
{"amt":1234,"fee":1,"lv":107575,"rcv":"fT+Z5T00rknrL0WHYc9ThAj/2u41xw2CNBZt56vj5Rc=","snd":"G9Y9xnKwuynUL8r6NCKk04XAyBabsBWVur+IVc9ZaXk=","type":"pay"}
{"amt":1234,"fee":1,"fv":106575,"lv":107575,"rcv":"fT+Z5T00rknrL0WHYc9ThAj/2u41xw2CNBZt56vj5Rc=","snd":"G9Y9xnKwuynUL8r6NCKk04XAyBabsBWVur+IVc9ZaXk=","type":"pay"}
{"sig":"L30CgmvHfc0qbk0Jjdy2GcRnDB3ZjrqalvjZpW5P6P+YaM7gjvHq6CK8qemTUyREAnF61YUP2BNuBlL3KVvRDQ==","txn":{"amt":1234,"fee":1,"fv":106575,"lv":107575,"rcv":"fT+Z5T00rknrL0WHYc9ThAj/2u41xw2CNBZt56vj5Rc=","snd":"G9Y9xnKwuynUL8r6NCKk04XAyBabsBWVur+IVc9ZaXk=","type":"pay"}}
Tests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.194 sec

Results :

Tests run: 22, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.915 s
[INFO] Finished at: 2019-09-04T17:05:03-06:00
[INFO] ------------------------------------------------------------------------

I'll be sending a small patch for you guys, unrelated to this, but it'd be nice to know how to run the example without complicating things with yet another tool (bazel)

"Paging" is not working in REST API

The API endpoint to get all the transactions is ignoring the firstRound "filter" option.

eg.
GET /v1/account/{address}/transactions?firstRound=5630625

Returns transactions even before the firstRound.

allow KMD to accept account addresses

for example, ImportMultisigRequest takes in a public key which forces you to decode an address

public ImportMultisigRequest addPksItem(Ed25519PublicKey pksItem) {

python works nicely where it takes in an msig object to import a multisig account into a kmd wallet
https://github.com/algorand/py-algorand-sdk/blob/b079db660ae92d0dbf24dc04f28eb722711e426f/algosdk/kmd.py#L353

Finding transactions with start and end dates does not work

This code fails:

        LocalDate today = LocalDate.now();
        LocalDate yesterday = today.minusDays(1);  
        System.out.println("yesderday = " + yesterday);


        TransactionList txList2 = algodApiInstance.transactions(address, 
            null,
            null,  
            yesterday, today, 
            BigInteger.valueOf(5));
        for (Transaction tx : txList2.getTransactions()) {
            System.out.println("Tx3 = " + tx.toString()); 
        }

Templates do not use 64 bit integers

The Logic class and template utilities are using int instead of a 64 bit numeric data type. They should be modified to use BigInteger or long.

For example this method:

public static byte[] putUVarint(int value)

Java SDK has kmd.PublicKey class that doesn't appear to do anything

May be a swagger generation issue.
Several functions take PublicKey as input but the class has no fields and is distinct from Ed25519PublicKey, which does carry information.

There’s also a PrivateKey class that looks the same.

[https://github.com/algorand/java-algorand-sdk/blob/03dc8cd3e388850a03e4e32cf3267dcf00cd9011/src/main/java/com/algorand/algosdk/kmd/client/model/PublicKey.java|https://github.com/algorand/java-algorand-sdk/blob/03dc8cd3e388850a03e4e32cf3267dcf00cd9011/src/main/java/com/algorand/algosdk/kmd/client/model/PublicKey.java]

Other small thing if someone is on this: there appears to be a typo in listMultisg (should be listMultisig).

Implement algorand-sdk-testing tests for new features

  • Create Asset -> Ask for details -> Verify Equality
  • Create Asset -> Ask for details -> Verfy Equality -> Update Asset -> Verify Equality
  • Create -> Destroy
  • Create -> Approve Acceptance -> Transfer -> Verify Equality
  • Freeze account -> Transfer in and out -> Make sure they both fail
  • Unfreeze the account -> transfer in and out -> Make sure they succeed
  • Create -> Mint -> Veriy -> Burn -> Verfy
  • Create with Frozen by default -> Approve Acceptance -> Unfreeze -> Transfer in and out -> Verify
  • Create -> Approve acceptance -> Transfer in -> Revoke -> Verify
  • Create -> attempt to transfer to non-accepting account-> verify failure

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.