Updated on 2026-08-14

This commit is contained in:
Tangem 2019-01-13 15:11:29 +03:00
parent 07bc724ecd
commit 75f19d41f4
278 changed files with 19571 additions and 206 deletions

View file

@ -0,0 +1,45 @@
package org.stellar.sdk;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents an account in Stellar network with it's sequence number.
* Account object is required to build a {@link Transaction}.
* @see org.stellar.sdk.Transaction.Builder
*/
public class Account implements TransactionBuilderAccount {
private final KeyPair mKeyPair;
private Long mSequenceNumber;
/**
* Class constructor.
* @param keypair KeyPair associated with this Account
* @param sequenceNumber Current sequence number of the account (can be obtained using java-stellar-sdk or horizon server)
*/
public Account(KeyPair keypair, Long sequenceNumber) {
mKeyPair = checkNotNull(keypair, "keypair cannot be null");
mSequenceNumber = checkNotNull(sequenceNumber, "sequenceNumber cannot be null");
}
@Override
public KeyPair getKeypair() {
return mKeyPair;
}
@Override
public Long getSequenceNumber() {
return mSequenceNumber;
}
@Override
public Long getIncrementedSequenceNumber() {
return new Long(mSequenceNumber + 1);
}
/**
* Increments sequence number in this object by one.
*/
public void incrementSequenceNumber() {
mSequenceNumber++;
}
}

View file

@ -0,0 +1,32 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountFlags;
/**
* AccountFlag is the <code>enum</code> that can be used in {@link SetOptionsOperation}.
* @see <a href="https://www.stellar.org/developers/guides/concepts/accounts.html#flags" target="_blank">Account Flags</a>
*/
public enum AccountFlag {
/**
* Authorization required (0x1): Requires the issuing account to give other accounts permission before they can hold the issuing accounts credit.
*/
AUTH_REQUIRED_FLAG(AccountFlags.AUTH_REQUIRED_FLAG.getValue()),
/**
* Authorization revocable (0x2): Allows the issuing account to revoke its credit held by other accounts.
*/
AUTH_REVOCABLE_FLAG(AccountFlags.AUTH_REVOCABLE_FLAG.getValue()),
/**
* Authorization immutable (0x4): If this is set then none of the authorization flags can be set and the account can never be deleted.
*/
AUTH_IMMUTABLE_FLAG(AccountFlags.AUTH_IMMUTABLE_FLAG.getValue()),
;
private final int value;
AccountFlag(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}

View file

@ -0,0 +1,80 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.Operation.OperationBody;
import org.stellar.sdk.xdr.OperationType;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#account-merge" target="_blank">AccountMerge</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class AccountMergeOperation extends Operation {
private final KeyPair destination;
private AccountMergeOperation(KeyPair destination) {
this.destination = checkNotNull(destination, "destination cannot be null");
}
/**
* The account that receives the remaining XLM balance of the source account.
*/
public KeyPair getDestination() {
return destination;
}
@Override
OperationBody toOperationBody() {
OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
AccountID destination = new AccountID();
destination.setAccountID(this.destination.getXdrPublicKey());
body.setDestination(destination);
body.setDiscriminant(OperationType.ACCOUNT_MERGE);
return body;
}
/**
* Builds AccountMerge operation.
* @see AccountMergeOperation
*/
public static class Builder {
private final KeyPair destination;
private KeyPair mSourceAccount;
Builder(OperationBody op) {
destination = KeyPair.fromXdrPublicKey(op.getDestination().getAccountID());
}
/**
* Creates a new AccountMerge builder.
* @param destination The account that receives the remaining XLM balance of the source account.
*/
public Builder(KeyPair destination) {
this.destination = destination;
}
/**
* Set source account of this operation
* @param sourceAccount Source account
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = sourceAccount;
return this;
}
/**
* Builds an operation
*/
public AccountMergeOperation build() {
AccountMergeOperation operation = new AccountMergeOperation(destination);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,133 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.AllowTrustOp;
import org.stellar.sdk.xdr.AssetType;
import org.stellar.sdk.xdr.OperationType;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#allow-trust" target="_blank">AllowTrust</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class AllowTrustOperation extends Operation {
private final KeyPair trustor;
private final String assetCode;
private final boolean authorize;
private AllowTrustOperation(KeyPair trustor, String assetCode, boolean authorize) {
this.trustor = checkNotNull(trustor, "trustor cannot be null");
this.assetCode = checkNotNull(assetCode, "assetCode cannot be null");
this.authorize = authorize;
}
/**
* The account of the recipient of the trustline.
*/
public KeyPair getTrustor() {
return trustor;
}
/**
* The asset of the trustline the source account is authorizing. For example, if a gateway wants to allow another account to hold its USD credit, the type is USD.
*/
public String getAssetCode() {
return assetCode;
}
/**
* Flag indicating whether the trustline is authorized.
*/
public boolean getAuthorize() {
return authorize;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
AllowTrustOp op = new AllowTrustOp();
// trustor
AccountID trustor = new AccountID();
trustor.setAccountID(this.trustor.getXdrPublicKey());
op.setTrustor(trustor);
// asset
AllowTrustOp.AllowTrustOpAsset asset = new AllowTrustOp.AllowTrustOpAsset();
if (assetCode.length() <= 4) {
asset.setDiscriminant(AssetType.ASSET_TYPE_CREDIT_ALPHANUM4);
asset.setAssetCode4(Util.paddedByteArray(assetCode, 4));
} else {
asset.setDiscriminant(AssetType.ASSET_TYPE_CREDIT_ALPHANUM12);
asset.setAssetCode12(Util.paddedByteArray(assetCode, 12));
}
op.setAsset(asset);
// authorize
op.setAuthorize(authorize);
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.ALLOW_TRUST);
body.setAllowTrustOp(op);
return body;
}
/**
* Builds AllowTrust operation.
* @see AllowTrustOperation
*/
public static class Builder {
private final KeyPair trustor;
private final String assetCode;
private final boolean authorize;
private KeyPair mSourceAccount;
Builder(AllowTrustOp op) {
trustor = KeyPair.fromXdrPublicKey(op.getTrustor().getAccountID());
switch (op.getAsset().getDiscriminant()) {
case ASSET_TYPE_CREDIT_ALPHANUM4:
assetCode = new String(op.getAsset().getAssetCode4()).trim();
break;
case ASSET_TYPE_CREDIT_ALPHANUM12:
assetCode = new String(op.getAsset().getAssetCode12()).trim();
break;
default:
throw new RuntimeException("Unknown asset code");
}
authorize = op.getAuthorize();
}
/**
* Creates a new AllowTrust builder.
* @param trustor The account of the recipient of the trustline.
* @param assetCode The asset of the trustline the source account is authorizing. For example, if a gateway wants to allow another account to hold its USD credit, the type is USD.
* @param authorize Flag indicating whether the trustline is authorized.
*/
public Builder(KeyPair trustor, String assetCode, boolean authorize) {
this.trustor = trustor;
this.assetCode = assetCode;
this.authorize = authorize;
}
/**
* Set source account of this operation
* @param sourceAccount Source account
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = sourceAccount;
return this;
}
/**
* Builds an operation
*/
public AllowTrustOperation build() {
AllowTrustOperation operation = new AllowTrustOperation(trustor, assetCode, authorize);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,72 @@
package org.stellar.sdk;
/**
* Base Asset class.
* @see <a href="https://www.stellar.org/developers/learn/concepts/assets.html" target="_blank">Assets</a>
*/
public abstract class Asset {
Asset() {}
public static Asset create(String type, String code, String issuer) {
if (type.equals("native")) {
return new AssetTypeNative();
} else {
return Asset.createNonNativeAsset(code, KeyPair.fromAccountId(issuer));
}
}
/**
* Creates one of AssetTypeCreditAlphaNum4 or AssetTypeCreditAlphaNum12 object based on a <code>code</code> length
* @param code Asset code
* @param issuer Asset issuer
*/
public static Asset createNonNativeAsset(String code, KeyPair issuer) {
if (code.length() >= 1 && code.length() <= 4) {
return new AssetTypeCreditAlphaNum4(code, issuer);
} else if (code.length() >= 5 && code.length() <= 12) {
return new AssetTypeCreditAlphaNum12(code, issuer);
} else {
throw new AssetCodeLengthInvalidException();
}
}
/**
* Generates Asset object from a given XDR object
* @param xdr XDR object
*/
public static Asset fromXdr(org.stellar.sdk.xdr.Asset xdr) {
switch (xdr.getDiscriminant()) {
case ASSET_TYPE_NATIVE:
return new AssetTypeNative();
case ASSET_TYPE_CREDIT_ALPHANUM4:
String assetCode4 = Util.paddedByteArrayToString(xdr.getAlphaNum4().getAssetCode());
KeyPair issuer4 = KeyPair.fromXdrPublicKey(
xdr.getAlphaNum4().getIssuer().getAccountID());
return new AssetTypeCreditAlphaNum4(assetCode4, issuer4);
case ASSET_TYPE_CREDIT_ALPHANUM12:
String assetCode12 = Util.paddedByteArrayToString(xdr.getAlphaNum12().getAssetCode());
KeyPair issuer12 = KeyPair.fromXdrPublicKey(xdr.getAlphaNum12().getIssuer().getAccountID());
return new AssetTypeCreditAlphaNum12(assetCode12, issuer12);
default:
throw new IllegalArgumentException("Unknown asset type " + xdr.getDiscriminant());
}
}
/**
* Returns asset type. Possible types:
* <ul>
* <li><code>native</code></li>
* <li><code>credit_alphanum4</code></li>
* <li><code>credit_alphanum12</code></li>
* </ul>
*/
public abstract String getType();
@Override
public abstract boolean equals(Object object);
/**
* Generates XDR object from a given Asset object
*/
public abstract org.stellar.sdk.xdr.Asset toXdr();
}

View file

@ -0,0 +1,16 @@
package org.stellar.sdk;
/**
* Indicates that asset code is not valid for a specified asset class
* @see AssetTypeCreditAlphaNum4
* @see AssetTypeCreditAlphaNum12
*/
public class AssetCodeLengthInvalidException extends RuntimeException {
public AssetCodeLengthInvalidException() {
super();
}
public AssetCodeLengthInvalidException(String message) {
super(message);
}
}

View file

@ -0,0 +1,52 @@
package org.stellar.sdk;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Base class for AssetTypeCreditAlphaNum4 and AssetTypeCreditAlphaNum12 subclasses.
* @see <a href="https://www.stellar.org/developers/learn/concepts/assets.html" target="_blank">Assets</a>
*/
public abstract class AssetTypeCreditAlphaNum extends Asset {
protected final String mCode;
protected final KeyPair mIssuer;
public AssetTypeCreditAlphaNum(String code, KeyPair issuer) {
checkNotNull(code, "code cannot be null");
checkNotNull(issuer, "issuer cannot be null");
mCode = new String(code);
mIssuer = KeyPair.fromAccountId(issuer.getAccountId());
}
/**
* Returns asset code
*/
public String getCode() {
return new String(mCode);
}
/**
* Returns asset issuer
*/
public KeyPair getIssuer() {
return KeyPair.fromAccountId(mIssuer.getAccountId());
}
@Override
public int hashCode() {
return Arrays.hashCode(new Object[]{this.getCode(), this.getIssuer().getAccountId()});
}
@Override
public boolean equals(Object object) {
if (!this.getClass().equals(object.getClass())) {
return false;
}
AssetTypeCreditAlphaNum o = (AssetTypeCreditAlphaNum) object;
return this.getCode().equals(o.getCode()) &&
this.getIssuer().getAccountId().equals(o.getIssuer().getAccountId());
}
}

View file

@ -0,0 +1,41 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.AssetType;
/**
* Represents all assets with codes 5-12 characters long.
* @see <a href="https://www.stellar.org/developers/learn/concepts/assets.html" target="_blank">Assets</a>
*/
public final class AssetTypeCreditAlphaNum12 extends AssetTypeCreditAlphaNum {
/**
* Class constructor
* @param code Asset code
* @param issuer Asset issuer
*/
public AssetTypeCreditAlphaNum12(String code, KeyPair issuer) {
super(code, issuer);
if (code.length() < 5 || code.length() > 12) {
throw new AssetCodeLengthInvalidException();
}
}
@Override
public String getType() {
return "credit_alphanum12";
}
@Override
public org.stellar.sdk.xdr.Asset toXdr() {
org.stellar.sdk.xdr.Asset xdr = new org.stellar.sdk.xdr.Asset();
xdr.setDiscriminant(AssetType.ASSET_TYPE_CREDIT_ALPHANUM12);
org.stellar.sdk.xdr.Asset.AssetAlphaNum12 credit = new org.stellar.sdk.xdr.Asset.AssetAlphaNum12();
credit.setAssetCode(Util.paddedByteArray(mCode, 12));
AccountID accountID = new AccountID();
accountID.setAccountID(mIssuer.getXdrPublicKey());
credit.setIssuer(accountID);
xdr.setAlphaNum12(credit);
return xdr;
}
}

View file

@ -0,0 +1,41 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.AssetType;
/**
* Represents all assets with codes 1-4 characters long.
* @see <a href="https://www.stellar.org/developers/learn/concepts/assets.html" target="_blank">Assets</a>
*/
public final class AssetTypeCreditAlphaNum4 extends AssetTypeCreditAlphaNum {
/**
* Class constructor
* @param code Asset code
* @param issuer Asset issuer
*/
public AssetTypeCreditAlphaNum4(String code, KeyPair issuer) {
super(code, issuer);
if (code.length() < 1 || code.length() > 4) {
throw new AssetCodeLengthInvalidException();
}
}
@Override
public String getType() {
return "credit_alphanum4";
}
@Override
public org.stellar.sdk.xdr.Asset toXdr() {
org.stellar.sdk.xdr.Asset xdr = new org.stellar.sdk.xdr.Asset();
xdr.setDiscriminant(AssetType.ASSET_TYPE_CREDIT_ALPHANUM4);
org.stellar.sdk.xdr.Asset.AssetAlphaNum4 credit = new org.stellar.sdk.xdr.Asset.AssetAlphaNum4();
credit.setAssetCode(Util.paddedByteArray(mCode, 4));
AccountID accountID = new AccountID();
accountID.setAccountID(mIssuer.getXdrPublicKey());
credit.setIssuer(accountID);
xdr.setAlphaNum4(credit);
return xdr;
}
}

View file

@ -0,0 +1,34 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AssetType;
/**
* Represents Stellar native asset - <a href="https://www.stellar.org/developers/learn/concepts/assets.html" target="_blank">lumens (XLM)</a>
* @see <a href="https://www.stellar.org/developers/learn/concepts/assets.html" target="_blank">Assets</a>
*/
public final class AssetTypeNative extends Asset {
public AssetTypeNative() {}
@Override
public String getType() {
return "native";
}
@Override
public boolean equals(Object object) {
return this.getClass().equals(object.getClass());
}
@Override
public int hashCode() {
return 0;
}
@Override
public org.stellar.sdk.xdr.Asset toXdr() {
org.stellar.sdk.xdr.Asset xdr = new org.stellar.sdk.xdr.Asset();
xdr.setDiscriminant(AssetType.ASSET_TYPE_NATIVE);
return xdr;
}
}

View file

@ -0,0 +1,79 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.BumpSequenceOp;
import org.stellar.sdk.xdr.Int64;
import org.stellar.sdk.xdr.OperationType;
import org.stellar.sdk.xdr.SequenceNumber;
import static com.google.common.base.Preconditions.checkNotNull;
public class BumpSequenceOperation extends Operation {
private final long bumpTo;
private BumpSequenceOperation(long bumpTo) {
this.bumpTo = bumpTo;
}
public long getBumpTo() {
return bumpTo;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
BumpSequenceOp op = new BumpSequenceOp();
Int64 bumpTo = new Int64();
bumpTo.setInt64(this.bumpTo);
SequenceNumber sequenceNumber = new SequenceNumber();
sequenceNumber.setSequenceNumber(bumpTo);
op.setBumpTo(sequenceNumber);
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.BUMP_SEQUENCE);
body.setBumpSequenceOp(op);
return body;
}
public static class Builder {
private final long bumpTo;
private KeyPair mSourceAccount;
/**
* Construct a new BumpSequence builder from a BumpSequence XDR.
* @param op {@link BumpSequenceOp}
*/
Builder(BumpSequenceOp op) {
bumpTo = op.getBumpTo().getSequenceNumber().getInt64();
}
/**
* Creates a new BumpSequence builder.
* @param bumpTo Sequence number to bump to
*/
public Builder(long bumpTo) {
this.bumpTo = bumpTo;
}
/**
* Sets the source account for this operation.
* @param sourceAccount The operation's source account.
* @return Builder object so you can chain methods.
*/
public BumpSequenceOperation.Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
return this;
}
/**
* Builds an operation
*/
public BumpSequenceOperation build() {
BumpSequenceOperation operation = new BumpSequenceOperation(bumpTo);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,98 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.ChangeTrustOp;
import org.stellar.sdk.xdr.Int64;
import org.stellar.sdk.xdr.OperationType;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#change-trust" target="_blank">ChangeTrust</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class ChangeTrustOperation extends Operation {
private final Asset asset;
private final String limit;
private ChangeTrustOperation(Asset asset, String limit) {
this.asset = checkNotNull(asset, "asset cannot be null");
this.limit = checkNotNull(limit, "limit cannot be null");
}
/**
* The asset of the trustline. For example, if a gateway extends a trustline of up to 200 USD to a user, the line is USD.
*/
public Asset getAsset() {
return asset;
}
/**
* The limit of the trustline. For example, if a gateway extends a trustline of up to 200 USD to a user, the limit is 200.
*/
public String getLimit() {
return limit;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
ChangeTrustOp op = new ChangeTrustOp();
op.setLine(asset.toXdr());
Int64 limit = new Int64();
limit.setInt64(Operation.toXdrAmount(this.limit));
op.setLimit(limit);
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.CHANGE_TRUST);
body.setChangeTrustOp(op);
return body;
}
/**
* Builds ChangeTrust operation.
* @see ChangeTrustOperation
*/
public static class Builder {
private final Asset asset;
private final String limit;
private KeyPair mSourceAccount;
Builder(ChangeTrustOp op) {
asset = Asset.fromXdr(op.getLine());
limit = Operation.fromXdrAmount(op.getLimit().getInt64().longValue());
}
/**
* Creates a new ChangeTrust builder.
* @param asset The asset of the trustline. For example, if a gateway extends a trustline of up to 200 USD to a user, the line is USD.
* @param limit The limit of the trustline. For example, if a gateway extends a trustline of up to 200 USD to a user, the limit is 200.
* @throws ArithmeticException when limit has more than 7 decimal places.
*/
public Builder(Asset asset, String limit) {
this.asset = checkNotNull(asset, "asset cannot be null");
this.limit = checkNotNull(limit, "limit cannot be null");
}
/**
* Set source account of this operation
* @param sourceAccount Source account
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
return this;
}
/**
* Builds an operation
*/
public ChangeTrustOperation build() {
ChangeTrustOperation operation = new ChangeTrustOperation(asset, limit);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,105 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.CreateAccountOp;
import org.stellar.sdk.xdr.Int64;
import org.stellar.sdk.xdr.OperationType;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#create-account" target="_blank">CreateAccount</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class CreateAccountOperation extends Operation {
private final KeyPair destination;
private final String startingBalance;
private CreateAccountOperation(KeyPair destination, String startingBalance) {
this.destination = checkNotNull(destination, "destination cannot be null");
this.startingBalance = checkNotNull(startingBalance, "startingBalance cannot be null");
}
/**
* Amount of XLM to send to the newly created account.
*/
public String getStartingBalance() {
return startingBalance;
}
/**
* Account that is created and funded
*/
public KeyPair getDestination() {
return destination;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
CreateAccountOp op = new CreateAccountOp();
AccountID destination = new AccountID();
destination.setAccountID(this.destination.getXdrPublicKey());
op.setDestination(destination);
Int64 startingBalance = new Int64();
startingBalance.setInt64(Operation.toXdrAmount(this.startingBalance));
op.setStartingBalance(startingBalance);
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.CREATE_ACCOUNT);
body.setCreateAccountOp(op);
return body;
}
/**
* Builds CreateAccount operation.
* @see CreateAccountOperation
*/
public static class Builder {
private final KeyPair destination;
private final String startingBalance;
private KeyPair mSourceAccount;
/**
* Construct a new CreateAccount builder from a CreateAccountOp XDR.
* @param op {@link CreateAccountOp}
*/
Builder(CreateAccountOp op) {
destination = KeyPair.fromXdrPublicKey(op.getDestination().getAccountID());
startingBalance = Operation.fromXdrAmount(op.getStartingBalance().getInt64().longValue());
}
/**
* Creates a new CreateAccount builder.
* @param destination The destination keypair (uses only the public key).
* @param startingBalance The initial balance to start with in lumens.
* @throws ArithmeticException when startingBalance has more than 7 decimal places.
*/
public Builder(KeyPair destination, String startingBalance) {
this.destination = destination;
this.startingBalance = startingBalance;
}
/**
* Sets the source account for this operation.
* @param account The operation's source account.
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair account) {
mSourceAccount = account;
return this;
}
/**
* Builds an operation
*/
public CreateAccountOperation build() {
CreateAccountOperation operation = new CreateAccountOperation(destination, startingBalance);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,136 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.CreatePassiveOfferOp;
import org.stellar.sdk.xdr.Int64;
import org.stellar.sdk.xdr.OperationType;
import java.math.BigDecimal;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#create-passive-offer" target="_blank">CreatePassiveOffer</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class CreatePassiveOfferOperation extends Operation {
private final Asset selling;
private final Asset buying;
private final String amount;
private final String price;
private CreatePassiveOfferOperation(Asset selling, Asset buying, String amount, String price) {
this.selling = checkNotNull(selling, "selling cannot be null");
this.buying = checkNotNull(buying, "buying cannot be null");
this.amount = checkNotNull(amount, "amount cannot be null");
this.price = checkNotNull(price, "price cannot be null");
}
/**
* The asset being sold in this operation
*/
public Asset getSelling() {
return selling;
}
/**
* The asset being bought in this operation
*/
public Asset getBuying() {
return buying;
}
/**
* Amount of selling being sold.
*/
public String getAmount() {
return amount;
}
/**
* Price of 1 unit of selling in terms of buying.
*/
public String getPrice() {
return price;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
CreatePassiveOfferOp op = new CreatePassiveOfferOp();
op.setSelling(selling.toXdr());
op.setBuying(buying.toXdr());
Int64 amount = new Int64();
amount.setInt64(Operation.toXdrAmount(this.amount));
op.setAmount(amount);
Price price = Price.fromString(this.price);
op.setPrice(price.toXdr());
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.CREATE_PASSIVE_OFFER);
body.setCreatePassiveOfferOp(op);
return body;
}
/**
* Builds CreatePassiveOffer operation.
* @see CreatePassiveOfferOperation
*/
public static class Builder {
private final Asset selling;
private final Asset buying;
private final String amount;
private final String price;
private KeyPair mSourceAccount;
/**
* Construct a new CreatePassiveOffer builder from a CreatePassiveOfferOp XDR.
* @param op
*/
Builder(CreatePassiveOfferOp op) {
selling = Asset.fromXdr(op.getSelling());
buying = Asset.fromXdr(op.getBuying());
amount = Operation.fromXdrAmount(op.getAmount().getInt64().longValue());
int n = op.getPrice().getN().getInt32().intValue();
int d = op.getPrice().getD().getInt32().intValue();
price = new BigDecimal(n).divide(new BigDecimal(d)).toString();
}
/**
* Creates a new CreatePassiveOffer builder.
* @param selling The asset being sold in this operation
* @param buying The asset being bought in this operation
* @param amount Amount of selling being sold.
* @param price Price of 1 unit of selling in terms of buying.
* @throws ArithmeticException when amount has more than 7 decimal places.
*/
public Builder(Asset selling, Asset buying, String amount, String price) {
this.selling = checkNotNull(selling, "selling cannot be null");
this.buying = checkNotNull(buying, "buying cannot be null");
this.amount = checkNotNull(amount, "amount cannot be null");
this.price = checkNotNull(price, "price cannot be null");
}
/**
* Sets the source account for this operation.
* @param sourceAccount The operation's source account.
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
return this;
}
/**
* Builds an operation
*/
public CreatePassiveOfferOperation build() {
CreatePassiveOfferOperation operation = new CreatePassiveOfferOperation(selling, buying, amount, price);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,15 @@
package org.stellar.sdk;
/**
* Indicates that there was a problem decoding strkey encoded string.
* @see KeyPair
*/
public class FormatException extends RuntimeException {
public FormatException() {
super();
}
public FormatException(String message) {
super(message);
}
}

View file

@ -0,0 +1,16 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.OperationType;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#inflation" target="_blank">Inflation</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class InflationOperation extends Operation {
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.INFLATION);
return body;
}
}

View file

@ -0,0 +1,262 @@
package org.stellar.sdk;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.KeyPairGenerator;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.stellar.sdk.xdr.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Holds a Stellar keypair.
*/
public class KeyPair {
private static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.ED_25519_CURVE_SPEC;
private final EdDSAPublicKey mPublicKey;
private final EdDSAPrivateKey mPrivateKey;
/**
* Creates a new KeyPair without a private key. Useful to simply verify a signature from a
* given public address.
* @param publicKey
*/
public KeyPair(EdDSAPublicKey publicKey) {
this(publicKey, null);
}
/**
* Creates a new KeyPair from the given public and private keys.
* @param publicKey
* @param privateKey
*/
public KeyPair(EdDSAPublicKey publicKey, EdDSAPrivateKey privateKey) {
mPublicKey = checkNotNull(publicKey, "publicKey cannot be null");
mPrivateKey = privateKey;
}
/**
* Returns true if this Keypair is capable of signing
*/
public boolean canSign() {
return mPrivateKey != null;
}
/**
* Creates a new Stellar KeyPair from a strkey encoded Stellar secret seed.
* @param seed Char array containing strkey encoded Stellar secret seed.
* @return {@link KeyPair}
*/
public static KeyPair fromSecretSeed(char[] seed) {
byte[] decoded = StrKey.decodeStellarSecretSeed(seed);
KeyPair keypair = fromSecretSeed(decoded);
Arrays.fill(decoded, (byte) 0);
return keypair;
}
/**
* <strong>Insecure</strong> Creates a new Stellar KeyPair from a strkey encoded Stellar secret seed.
* This method is <u>insecure</u>. Use only if you are aware of security implications.
* @see <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#PBEEx" target="_blank">Using Password-Based Encryption</a>
* @param seed The strkey encoded Stellar secret seed.
* @return {@link KeyPair}
*/
public static KeyPair fromSecretSeed(String seed) {
char[] charSeed = seed.toCharArray();
byte[] decoded = StrKey.decodeStellarSecretSeed(charSeed);
KeyPair keypair = fromSecretSeed(decoded);
Arrays.fill(charSeed, ' ');
return keypair;
}
/**
* Creates a new Stellar keypair from a raw 32 byte secret seed.
* @param seed The 32 byte secret seed.
* @return {@link KeyPair}
*/
public static KeyPair fromSecretSeed(byte[] seed) {
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(seed, ed25519);
EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(privKeySpec.getA().toByteArray(), ed25519);
return new KeyPair(new EdDSAPublicKey(publicKeySpec), new EdDSAPrivateKey(privKeySpec));
}
/**
* Creates a new Stellar KeyPair from a strkey encoded Stellar account ID.
* @param accountId The strkey encoded Stellar account ID.
* @return {@link KeyPair}
*/
public static KeyPair fromAccountId(String accountId) {
byte[] decoded = StrKey.decodeStellarAccountId(accountId);
return fromPublicKey(decoded);
}
/**
* Creates a new Stellar keypair from a 32 byte address.
* @param publicKey The 32 byte public key.
* @return {@link KeyPair}
*/
public static KeyPair fromPublicKey(byte[] publicKey) {
EdDSAPublicKeySpec publicKeySpec = new EdDSAPublicKeySpec(publicKey, ed25519);
return new KeyPair(new EdDSAPublicKey(publicKeySpec));
}
/**
* Finds the KeyPair for the path m/44'/148'/accountNumber' using the method described in
* <a href="https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md">SEP-0005</a>.
*
* @param bip39Seed The output of BIP0039
* @param accountNumber The number of the account
* @return KeyPair with secret
*/
public static KeyPair fromBip39Seed(byte[] bip39Seed, int accountNumber) {
try {
return KeyPair.fromSecretSeed(SLIP10.deriveEd25519PrivateKey(bip39Seed, 44, 148, accountNumber));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Generates a random Stellar keypair.
* @return a random Stellar keypair.
*/
public static KeyPair random() {
java.security.KeyPair keypair = new KeyPairGenerator().generateKeyPair();
return new KeyPair((EdDSAPublicKey) keypair.getPublic(), (EdDSAPrivateKey) keypair.getPrivate());
}
/**
* Returns the human readable account ID encoded in strkey.
*/
public String getAccountId() {
return StrKey.encodeStellarAccountId(mPublicKey.getAbyte());
}
/**
* Returns the human readable secret seed encoded in strkey.
*/
public char[] getSecretSeed() {
return StrKey.encodeStellarSecretSeed(mPrivateKey.getSeed());
}
public byte[] getPublicKey() {
return mPublicKey.getAbyte();
}
public SignatureHint getSignatureHint() {
try {
ByteArrayOutputStream publicKeyBytesStream = new ByteArrayOutputStream();
XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(publicKeyBytesStream);
PublicKey.encode(xdrOutputStream, this.getXdrPublicKey());
byte[] publicKeyBytes = publicKeyBytesStream.toByteArray();
byte[] signatureHintBytes = Arrays.copyOfRange(publicKeyBytes, publicKeyBytes.length - 4, publicKeyBytes.length);
SignatureHint signatureHint = new SignatureHint();
signatureHint.setSignatureHint(signatureHintBytes);
return signatureHint;
} catch (IOException e) {
throw new AssertionError(e);
}
}
public PublicKey getXdrPublicKey() {
PublicKey publicKey = new PublicKey();
publicKey.setDiscriminant(PublicKeyType.PUBLIC_KEY_TYPE_ED25519);
Uint256 uint256 = new Uint256();
uint256.setUint256(getPublicKey());
publicKey.setEd25519(uint256);
return publicKey;
}
public SignerKey getXdrSignerKey() {
SignerKey signerKey = new SignerKey();
signerKey.setDiscriminant(SignerKeyType.SIGNER_KEY_TYPE_ED25519);
Uint256 uint256 = new Uint256();
uint256.setUint256(getPublicKey());
signerKey.setEd25519(uint256);
return signerKey;
}
public static KeyPair fromXdrPublicKey(PublicKey key) {
return KeyPair.fromPublicKey(key.getEd25519().getUint256());
}
public static KeyPair fromXdrSignerKey(SignerKey key) {
return KeyPair.fromPublicKey(key.getEd25519().getUint256());
}
/**
* Sign the provided data with the keypair's private key.
* @param data The data to sign.
* @return signed bytes, null if the private key for this keypair is null.
*/
public byte[] sign(byte[] data) {
if (mPrivateKey == null) {
throw new RuntimeException("KeyPair does not contain secret key. Use KeyPair.fromSecretSeed method to create a new KeyPair with a secret key.");
}
try {
Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
sgr.initSign(mPrivateKey);
sgr.update(data);
return sgr.sign();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
/**
* Sign the provided data with the keypair's private key and returns {@link DecoratedSignature}.
* @param data
*/
public DecoratedSignature signDecorated(byte[] data) {
byte[] signatureBytes = this.sign(data);
org.stellar.sdk.xdr.Signature signature = new org.stellar.sdk.xdr.Signature();
signature.setSignature(signatureBytes);
DecoratedSignature decoratedSignature = new DecoratedSignature();
decoratedSignature.setHint(this.getSignatureHint());
decoratedSignature.setSignature(signature);
return decoratedSignature;
}
/**
* Verify the provided data and signature match this keypair's public key.
* @param data The data that was signed.
* @param signature The signature.
* @return True if they match, false otherwise.
* @throws RuntimeException
*/
public boolean verify(byte[] data, byte[] signature) {
try {
Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512"));
sgr.initVerify(mPublicKey);
sgr.update(data);
return sgr.verify(signature);
} catch (SignatureException e) {
return false;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}

View file

@ -0,0 +1,107 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.DataValue;
import org.stellar.sdk.xdr.ManageDataOp;
import org.stellar.sdk.xdr.OperationType;
import org.stellar.sdk.xdr.String64;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#manage-data" target="_blank">ManageData</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class ManageDataOperation extends Operation {
private final String name;
private final byte[] value;
private ManageDataOperation(String name, byte[] value) {
this.name = checkNotNull(name, "name cannot be null");
this.value = value;
}
/**
* The name of the data value
*/
public String getName() {
return name;
}
/**
* Data value
*/
public byte[] getValue() {
return value;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
ManageDataOp op = new ManageDataOp();
String64 name = new String64();
name.setString64(this.name);
op.setDataName(name);
if (value != null) {
DataValue dataValue = new DataValue();
dataValue.setDataValue(this.value);
op.setDataValue(dataValue);
}
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.MANAGE_DATA);
body.setManageDataOp(op);
return body;
}
public static class Builder {
private final String name;
private final byte[] value;
private KeyPair mSourceAccount;
/**
* Construct a new ManageOffer builder from a ManageDataOp XDR.
* @param op {@link ManageDataOp}
*/
Builder(ManageDataOp op) {
name = op.getDataName().getString64();
if (op.getDataValue() != null) {
value = op.getDataValue().getDataValue();
} else {
value = null;
}
}
/**
* Creates a new ManageData builder. If you want to delete data entry pass null as a <code>value</code> param.
* @param name The name of data entry
* @param value The value of data entry. <code>null</code>null will delete data entry.
*/
public Builder(String name, byte[] value) {
this.name = checkNotNull(name, "name cannot be null");
this.value = value;
}
/**
* Sets the source account for this operation.
* @param sourceAccount The operation's source account.
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
return this;
}
/**
* Builds an operation
*/
public ManageDataOperation build() {
ManageDataOperation operation = new ManageDataOperation(name, value);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,165 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.CreateAccountOp;
import org.stellar.sdk.xdr.Int64;
import org.stellar.sdk.xdr.ManageOfferOp;
import org.stellar.sdk.xdr.OperationType;
import org.stellar.sdk.xdr.Uint64;
import java.math.BigDecimal;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#manage-offer" target="_blank">ManageOffer</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class ManageOfferOperation extends Operation {
private final Asset selling;
private final Asset buying;
private final String amount;
private final String price;
private final long offerId;
private ManageOfferOperation(Asset selling, Asset buying, String amount, String price, long offerId) {
this.selling = checkNotNull(selling, "selling cannot be null");
this.buying = checkNotNull(buying, "buying cannot be null");
this.amount = checkNotNull(amount, "amount cannot be null");
this.price = checkNotNull(price, "price cannot be null");
// offerId can be null
this.offerId = offerId;
}
/**
* The asset being sold in this operation
*/
public Asset getSelling() {
return selling;
}
/**
* The asset being bought in this operation
*/
public Asset getBuying() {
return buying;
}
/**
* Amount of selling being sold.
*/
public String getAmount() {
return amount;
}
/**
* Price of 1 unit of selling in terms of buying.
*/
public String getPrice() {
return price;
}
/**
* The ID of the offer.
*/
public long getOfferId() {
return offerId;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
ManageOfferOp op = new ManageOfferOp();
op.setSelling(selling.toXdr());
op.setBuying(buying.toXdr());
Int64 amount = new Int64();
amount.setInt64(Operation.toXdrAmount(this.amount));
op.setAmount(amount);
Price price = Price.fromString(this.price);
op.setPrice(price.toXdr());
Uint64 offerId = new Uint64();
offerId.setUint64(Long.valueOf(this.offerId));
op.setOfferID(offerId);
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.MANAGE_OFFER);
body.setManageOfferOp(op);
return body;
}
/**
* Builds ManageOffer operation. If you want to update existing offer use
* {@link org.stellar.sdk.ManageOfferOperation.Builder#setOfferId(long)}.
* @see ManageOfferOperation
*/
public static class Builder {
private final Asset selling;
private final Asset buying;
private final String amount;
private final String price;
private long offerId = 0;
private KeyPair mSourceAccount;
/**
* Construct a new CreateAccount builder from a CreateAccountOp XDR.
* @param op {@link CreateAccountOp}
*/
Builder(ManageOfferOp op) {
selling = Asset.fromXdr(op.getSelling());
buying = Asset.fromXdr(op.getBuying());
amount = Operation.fromXdrAmount(op.getAmount().getInt64().longValue());
int n = op.getPrice().getN().getInt32().intValue();
int d = op.getPrice().getD().getInt32().intValue();
price = new BigDecimal(n).divide(new BigDecimal(d)).toString();
offerId = op.getOfferID().getUint64().longValue();
}
/**
* Creates a new ManageOffer builder. If you want to update existing offer use
* {@link org.stellar.sdk.ManageOfferOperation.Builder#setOfferId(long)}.
* @param selling The asset being sold in this operation
* @param buying The asset being bought in this operation
* @param amount Amount of selling being sold.
* @param price Price of 1 unit of selling in terms of buying.
* @throws ArithmeticException when amount has more than 7 decimal places.
*/
public Builder(Asset selling, Asset buying, String amount, String price) {
this.selling = checkNotNull(selling, "selling cannot be null");
this.buying = checkNotNull(buying, "buying cannot be null");
this.amount = checkNotNull(amount, "amount cannot be null");
this.price = checkNotNull(price, "price cannot be null");
}
/**
* Sets offer ID. <code>0</code> creates a new offer. Set to existing offer ID to change it.
* @param offerId
*/
public Builder setOfferId(long offerId) {
this.offerId = offerId;
return this;
}
/**
* Sets the source account for this operation.
* @param sourceAccount The operation's source account.
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
return this;
}
/**
* Builds an operation
*/
public ManageOfferOperation build() {
ManageOfferOperation operation = new ManageOfferOperation(selling, buying, amount, price, offerId);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,93 @@
package org.stellar.sdk;
import com.google.common.io.BaseEncoding;
/**
* <p>The memo contains optional extra information. It is the responsibility of the client to interpret this value. Memos can be one of the following types:</p>
* <ul>
* <li><code>MEMO_NONE</code>: Empty memo.</li>
* <li><code>MEMO_TEXT</code>: A string up to 28-bytes long.</li>
* <li><code>MEMO_ID</code>: A 64 bit unsigned integer.</li>
* <li><code>MEMO_HASH</code>: A 32 byte hash.</li>
* <li><code>MEMO_RETURN</code>: A 32 byte hash intended to be interpreted as the hash of the transaction the sender is refunding.</li>
* </ul>
* <p>Use static methods to generate any of above types.</p>
* @see Transaction
*/
public abstract class Memo {
/**
* Creates new MemoNone instance.
*/
public static MemoNone none() {
return new MemoNone();
}
/**
* Creates new {@link MemoText} instance.
* @param text
*/
public static MemoText text(String text) {
return new MemoText(text);
}
/**
* Creates new {@link MemoId} instance.
* @param id
*/
public static MemoId id(long id) {
return new MemoId(id);
}
/**
* Creates new {@link MemoHash} instance from byte array.
* @param bytes
*/
public static MemoHash hash(byte[] bytes) {
return new MemoHash(bytes);
}
/**
* Creates new {@link MemoHash} instance from hex-encoded string
* @param hexString
*/
public static MemoHash hash(String hexString) {
return new MemoHash(hexString);
}
/**
* Creates new {@link MemoReturnHash} instance from byte array.
* @param bytes
*/
public static MemoReturnHash returnHash(byte[] bytes) {
return new MemoReturnHash(bytes);
}
/**
* Creates new {@link MemoReturnHash} instance from hex-encoded string.
* @param hexString
*/
public static MemoReturnHash returnHash(String hexString) {
// We change to lowercase because we want to decode both: upper cased and lower cased alphabets.
return new MemoReturnHash(BaseEncoding.base16().lowerCase().decode(hexString.toLowerCase()));
}
public static Memo fromXdr(org.stellar.sdk.xdr.Memo memo) {
switch (memo.getDiscriminant()) {
case MEMO_NONE:
return none();
case MEMO_ID:
return id(memo.getId().getUint64().longValue());
case MEMO_TEXT:
return text(memo.getText());
case MEMO_HASH:
return hash(memo.getHash().getHash());
case MEMO_RETURN:
return returnHash(memo.getRetHash().getHash());
default:
throw new RuntimeException("Unknown memo type");
}
}
abstract org.stellar.sdk.xdr.Memo toXdr();
abstract public boolean equals(Object o);
}

View file

@ -0,0 +1,28 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.MemoType;
/**
* Represents MEMO_HASH.
*/
public class MemoHash extends MemoHashAbstract {
public MemoHash(byte[] bytes) {
super(bytes);
}
public MemoHash(String hexString) {
super(hexString);
}
@Override
org.stellar.sdk.xdr.Memo toXdr() {
org.stellar.sdk.xdr.Memo memo = new org.stellar.sdk.xdr.Memo();
memo.setDiscriminant(MemoType.MEMO_HASH);
org.stellar.sdk.xdr.Hash hash = new org.stellar.sdk.xdr.Hash();
hash.setHash(bytes);
memo.setHash(hash);
return memo;
}
}

View file

@ -0,0 +1,69 @@
package org.stellar.sdk;
import com.google.common.base.Objects;
import com.google.common.io.BaseEncoding;
abstract class MemoHashAbstract extends Memo {
protected byte[] bytes;
public MemoHashAbstract(byte[] bytes) {
if (bytes.length < 32) {
bytes = Util.paddedByteArray(bytes, 32);
} else if (bytes.length > 32) {
throw new MemoTooLongException("MEMO_HASH can contain 32 bytes at max.");
}
this.bytes = bytes;
}
public MemoHashAbstract(String hexString) {
// We change to lowercase because we want to decode both: upper cased and lower cased alphabets.
this(BaseEncoding.base16().lowerCase().decode(hexString.toLowerCase()));
}
/**
* Returns 32 bytes long array contained in this memo.
*/
public byte[] getBytes() {
return bytes;
}
/**
* <p>Returns hex representation of bytes contained in this memo.</p>
*
* <p>Example:</p>
* <code>
* MemoHash memo = new MemoHash("4142434445");
* memo.getHexValue(); // 4142434445000000000000000000000000000000000000000000000000000000
* memo.getTrimmedHexValue(); // 4142434445
* </code>
*/
public String getHexValue() {
return BaseEncoding.base16().lowerCase().encode(this.bytes);
}
/**
* <p>Returns hex representation of bytes contained in this memo until null byte (0x00) is found.</p>
*
* <p>Example:</p>
* <code>
* MemoHash memo = new MemoHash("4142434445");
* memo.getHexValue(); // 4142434445000000000000000000000000000000000000000000000000000000
* memo.getTrimmedHexValue(); // 4142434445
* </code>
*/
public String getTrimmedHexValue() {
return this.getHexValue().split("00")[0];
}
@Override
abstract org.stellar.sdk.xdr.Memo toXdr();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MemoHashAbstract that = (MemoHashAbstract) o;
return Objects.equal(bytes, that.bytes);
}
}

View file

@ -0,0 +1,40 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.MemoType;
import org.stellar.sdk.xdr.Uint64;
/**
* Represents MEMO_ID.
*/
public class MemoId extends Memo {
private long id;
public MemoId(long id) {
if (Long.compareUnsigned(id, 0) < 0) {
throw new IllegalArgumentException("id must be a positive number");
}
this.id = id;
}
public long getId() {
return id;
}
@Override
org.stellar.sdk.xdr.Memo toXdr() {
org.stellar.sdk.xdr.Memo memo = new org.stellar.sdk.xdr.Memo();
memo.setDiscriminant(MemoType.MEMO_ID);
Uint64 idXdr = new Uint64();
idXdr.setUint64(id);
memo.setId(idXdr);
return memo;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MemoId memoId = (MemoId) o;
return id == memoId.id;
}
}

View file

@ -0,0 +1,22 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.MemoType;
/**
* Represents MEMO_NONE.
*/
public class MemoNone extends Memo {
@Override
org.stellar.sdk.xdr.Memo toXdr() {
org.stellar.sdk.xdr.Memo memo = new org.stellar.sdk.xdr.Memo();
memo.setDiscriminant(MemoType.MEMO_NONE);
return memo;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return true;
}
}

View file

@ -0,0 +1,29 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.Memo;
import org.stellar.sdk.xdr.MemoType;
/**
* Represents MEMO_RETURN.
*/
public class MemoReturnHash extends MemoHashAbstract {
public MemoReturnHash(byte[] bytes) {
super(bytes);
}
public MemoReturnHash(String hexString) {
super(hexString);
}
@Override
Memo toXdr() {
org.stellar.sdk.xdr.Memo memo = new org.stellar.sdk.xdr.Memo();
memo.setDiscriminant(MemoType.MEMO_RETURN);
org.stellar.sdk.xdr.Hash hash = new org.stellar.sdk.xdr.Hash();
hash.setHash(bytes);
memo.setRetHash(hash);
return memo;
}
}

View file

@ -0,0 +1,44 @@
package org.stellar.sdk;
import com.google.common.base.Objects;
import org.stellar.sdk.xdr.MemoType;
import java.nio.charset.Charset;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents MEMO_TEXT.
*/
public class MemoText extends Memo {
private String text;
public MemoText(String text) {
this.text = checkNotNull(text, "text cannot be null");
int length = text.getBytes((Charset.forName("UTF-8"))).length;
if (length > 28) {
throw new MemoTooLongException("text must be <= 28 bytes. length=" + String.valueOf(length));
}
}
public String getText() {
return text;
}
@Override
org.stellar.sdk.xdr.Memo toXdr() {
org.stellar.sdk.xdr.Memo memo = new org.stellar.sdk.xdr.Memo();
memo.setDiscriminant(MemoType.MEMO_TEXT);
memo.setText(text);
return memo;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MemoText memoText = (MemoText) o;
return Objects.equal(text, memoText.text);
}
}

View file

@ -0,0 +1,15 @@
package org.stellar.sdk;
/**
* Indicates that value passed to Memo
* @see Memo
*/
public class MemoTooLongException extends RuntimeException {
public MemoTooLongException() {
super();
}
public MemoTooLongException(String message) {
super(message);
}
}

View file

@ -0,0 +1,71 @@
package org.stellar.sdk;
import java.nio.charset.Charset;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Network class is used to specify which Stellar network you want to use.
* Each network has a <code>networkPassphrase</code> which is hashed to
* every transaction id.
* There is no default network. You need to specify network when initializing your app by calling
* {@link Network#use(Network)}, {@link Network#usePublicNetwork()} or {@link Network#useTestNetwork()}.
*/
public class Network {
private final static String PUBLIC = "Public Global Stellar Network ; September 2015";
private final static String TESTNET = "Test SDF Network ; September 2015";
private static Network current;
private final String networkPassphrase;
/**
* Creates a new Network object to represent a network with a given passphrase
* @param networkPassphrase
*/
public Network(String networkPassphrase) {
this.networkPassphrase = checkNotNull(networkPassphrase, "networkPassphrase cannot be null");
}
/**
* Returns network passphrase
*/
public String getNetworkPassphrase() {
return networkPassphrase;
}
/**
* Returns network id (SHA-256 hashed <code>networkPassphrase</code>).
*/
public byte[] getNetworkId() {
return Util.hash(current.getNetworkPassphrase().getBytes(Charset.forName("UTF-8")));
}
/**
* Returns currently used Network object.
*/
public static Network current() {
return current;
}
/**
* Use <code>network</code> as a current network.
* @param network Network object to set as current network
*/
public static void use(Network network) {
current = network;
}
/**
* Use Stellar Public Network
*/
public static void usePublicNetwork() {
Network.use(new Network(PUBLIC));
}
/**
* Use Stellar Test Network.
*/
public static void useTestNetwork() {
Network.use(new Network(TESTNET));
}
}

View file

@ -0,0 +1,10 @@
package org.stellar.sdk;
/**
* Indicates that no network was selected.
*/
public class NoNetworkSelectedException extends RuntimeException {
public NoNetworkSelectedException() {
super("No network selected. Use `Network.use`, `Network.usePublicNetwork` or `Network.useTestNetwork` helper methods to select network.");
}
}

View file

@ -0,0 +1,14 @@
package org.stellar.sdk;
/**
* Indicates that the object that has to be signed has not enough signatures.
*/
public class NotEnoughSignaturesException extends RuntimeException {
public NotEnoughSignaturesException() {
super();
}
public NotEnoughSignaturesException(String message) {
super(message);
}
}

View file

@ -0,0 +1,134 @@
package org.stellar.sdk;
import com.google.common.io.BaseEncoding;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.XdrDataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstract class for operations.
*/
public abstract class Operation {
Operation() {}
private KeyPair mSourceAccount;
private static final BigDecimal ONE = new BigDecimal(10).pow(7);
protected static long toXdrAmount(String value) {
value = checkNotNull(value, "value cannot be null");
BigDecimal amount = new BigDecimal(value).multiply(Operation.ONE);
return amount.longValueExact();
}
protected static String fromXdrAmount(long value) {
BigDecimal amount = new BigDecimal(value).divide(Operation.ONE);
return amount.toPlainString();
}
/**
* Generates Operation XDR object.
*/
public org.stellar.sdk.xdr.Operation toXdr() {
org.stellar.sdk.xdr.Operation xdr = new org.stellar.sdk.xdr.Operation();
if (getSourceAccount() != null) {
AccountID sourceAccount = new AccountID();
sourceAccount.setAccountID(getSourceAccount().getXdrPublicKey());
xdr.setSourceAccount(sourceAccount);
}
xdr.setBody(toOperationBody());
return xdr;
}
/**
* Returns base64-encoded Operation XDR object.
*/
public String toXdrBase64() {
try {
org.stellar.sdk.xdr.Operation operation = this.toXdr();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(outputStream);
org.stellar.sdk.xdr.Operation.encode(xdrOutputStream, operation);
BaseEncoding base64Encoding = BaseEncoding.base64();
return base64Encoding.encode(outputStream.toByteArray());
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Returns new Operation object from Operation XDR object.
* @param xdr XDR object
*/
public static Operation fromXdr(org.stellar.sdk.xdr.Operation xdr) {
org.stellar.sdk.xdr.Operation.OperationBody body = xdr.getBody();
Operation operation;
switch (body.getDiscriminant()) {
case CREATE_ACCOUNT:
operation = new CreateAccountOperation.Builder(body.getCreateAccountOp()).build();
break;
case PAYMENT:
operation = new PaymentOperation.Builder(body.getPaymentOp()).build();
break;
case PATH_PAYMENT:
operation = new PathPaymentOperation.Builder(body.getPathPaymentOp()).build();
break;
case MANAGE_OFFER:
operation = new ManageOfferOperation.Builder(body.getManageOfferOp()).build();
break;
case CREATE_PASSIVE_OFFER:
operation = new CreatePassiveOfferOperation.Builder(body.getCreatePassiveOfferOp()).build();
break;
case SET_OPTIONS:
operation = new SetOptionsOperation.Builder(body.getSetOptionsOp()).build();
break;
case CHANGE_TRUST:
operation = new ChangeTrustOperation.Builder(body.getChangeTrustOp()).build();
break;
case ALLOW_TRUST:
operation = new AllowTrustOperation.Builder(body.getAllowTrustOp()).build();
break;
case ACCOUNT_MERGE:
operation = new AccountMergeOperation.Builder(body).build();
break;
case MANAGE_DATA:
operation = new ManageDataOperation.Builder(body.getManageDataOp()).build();
break;
case BUMP_SEQUENCE:
operation = new BumpSequenceOperation.Builder(body.getBumpSequenceOp()).build();
break;
default:
throw new RuntimeException("Unknown operation body " + body.getDiscriminant());
}
if (xdr.getSourceAccount() != null) {
operation.setSourceAccount(KeyPair.fromXdrPublicKey(xdr.getSourceAccount().getAccountID()));
}
return operation;
}
/**
* Returns operation source account.
*/
public KeyPair getSourceAccount() {
return mSourceAccount;
}
/**
* Sets operation source account.
* @param keypair
*/
void setSourceAccount(KeyPair keypair) {
mSourceAccount = checkNotNull(keypair, "keypair cannot be null");
}
/**
* Generates OperationBody XDR object
* @return OperationBody XDR object
*/
abstract org.stellar.sdk.xdr.Operation.OperationBody toOperationBody();
}

View file

@ -0,0 +1,192 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.Int64;
import org.stellar.sdk.xdr.OperationType;
import org.stellar.sdk.xdr.PathPaymentOp;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#path-payment" target="_blank">PathPayment</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class PathPaymentOperation extends Operation {
private final Asset sendAsset;
private final String sendMax;
private final KeyPair destination;
private final Asset destAsset;
private final String destAmount;
private final Asset[] path;
private PathPaymentOperation(Asset sendAsset, String sendMax, KeyPair destination,
Asset destAsset, String destAmount, Asset[] path) {
this.sendAsset = checkNotNull(sendAsset, "sendAsset cannot be null");
this.sendMax = checkNotNull(sendMax, "sendMax cannot be null");
this.destination = checkNotNull(destination, "destination cannot be null");
this.destAsset = checkNotNull(destAsset, "destAsset cannot be null");
this.destAmount = checkNotNull(destAmount, "destAmount cannot be null");
if (path == null) {
this.path = new Asset[0];
} else {
checkArgument(path.length <= 5, "The maximum number of assets in the path is 5");
this.path = path;
}
}
/**
* The asset deducted from the sender's account.
*/
public Asset getSendAsset() {
return sendAsset;
}
/**
* The maximum amount of send asset to deduct (excluding fees)
*/
public String getSendMax() {
return sendMax;
}
/**
* Account that receives the payment.
*/
public KeyPair getDestination() {
return destination;
}
/**
* The asset the destination account receives.
*/
public Asset getDestAsset() {
return destAsset;
}
/**
* The amount of destination asset the destination account receives.
*/
public String getDestAmount() {
return destAmount;
}
/**
* The assets (other than send asset and destination asset) involved in the offers the path takes. For example, if you can only find a path from USD to EUR through XLM and BTC, the path would be USD -&raquo; XLM -&raquo; BTC -&raquo; EUR and the path would contain XLM and BTC.
*/
public Asset[] getPath() {
return path;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
PathPaymentOp op = new PathPaymentOp();
// sendAsset
op.setSendAsset(sendAsset.toXdr());
// sendMax
Int64 sendMax = new Int64();
sendMax.setInt64(Operation.toXdrAmount(this.sendMax));
op.setSendMax(sendMax);
// destination
AccountID destination = new AccountID();
destination.setAccountID(this.destination.getXdrPublicKey());
op.setDestination(destination);
// destAsset
op.setDestAsset(destAsset.toXdr());
// destAmount
Int64 destAmount = new Int64();
destAmount.setInt64(Operation.toXdrAmount(this.destAmount));
op.setDestAmount(destAmount);
// path
org.stellar.sdk.xdr.Asset[] path = new org.stellar.sdk.xdr.Asset[this.path.length];
for (int i = 0; i < this.path.length; i++) {
path[i] = this.path[i].toXdr();
}
op.setPath(path);
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.PATH_PAYMENT);
body.setPathPaymentOp(op);
return body;
}
/**
* Builds PathPayment operation.
* @see PathPaymentOperation
*/
public static class Builder {
private final Asset sendAsset;
private final String sendMax;
private final KeyPair destination;
private final Asset destAsset;
private final String destAmount;
private Asset[] path;
private KeyPair mSourceAccount;
Builder(PathPaymentOp op) {
sendAsset = Asset.fromXdr(op.getSendAsset());
sendMax = Operation.fromXdrAmount(op.getSendMax().getInt64().longValue());
destination = KeyPair.fromXdrPublicKey(op.getDestination().getAccountID());
destAsset = Asset.fromXdr(op.getDestAsset());
destAmount = Operation.fromXdrAmount(op.getDestAmount().getInt64().longValue());
path = new Asset[op.getPath().length];
for (int i = 0; i < op.getPath().length; i++) {
path[i] = Asset.fromXdr(op.getPath()[i]);
}
}
/**
* Creates a new PathPaymentOperation builder.
* @param sendAsset The asset deducted from the sender's account.
* @param sendMax The asset deducted from the sender's account.
* @param destination Payment destination
* @param destAsset The asset the destination account receives.
* @param destAmount The amount of destination asset the destination account receives.
* @throws ArithmeticException when sendMax or destAmount has more than 7 decimal places.
*/
public Builder(Asset sendAsset, String sendMax, KeyPair destination,
Asset destAsset, String destAmount) {
this.sendAsset = checkNotNull(sendAsset, "sendAsset cannot be null");
this.sendMax = checkNotNull(sendMax, "sendMax cannot be null");
this.destination = checkNotNull(destination, "destination cannot be null");
this.destAsset = checkNotNull(destAsset, "destAsset cannot be null");
this.destAmount = checkNotNull(destAmount, "destAmount cannot be null");
}
/**
* Sets path for this operation
* @param path The assets (other than send asset and destination asset) involved in the offers the path takes. For example, if you can only find a path from USD to EUR through XLM and BTC, the path would be USD -&raquo; XLM -&raquo; BTC -&raquo; EUR and the path field would contain XLM and BTC.
* @return Builder object so you can chain methods.
*/
public Builder setPath(Asset[] path) {
checkNotNull(path, "path cannot be null");
checkArgument(path.length <= 5, "The maximum number of assets in the path is 5");
this.path = path;
return this;
}
/**
* Sets the source account for this operation.
* @param sourceAccount The operation's source account.
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
return this;
}
/**
* Builds an operation
*/
public PathPaymentOperation build() {
PathPaymentOperation operation = new PathPaymentOperation(sendAsset, sendMax, destination,
destAsset, destAmount, path);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,123 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.AccountID;
import org.stellar.sdk.xdr.Int64;
import org.stellar.sdk.xdr.OperationType;
import org.stellar.sdk.xdr.PaymentOp;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#payment" target="_blank">Payment</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">List of Operations</a>
*/
public class PaymentOperation extends Operation {
private final KeyPair destination;
private final Asset asset;
private final String amount;
private PaymentOperation(KeyPair destination, Asset asset, String amount) {
this.destination = checkNotNull(destination, "destination cannot be null");
this.asset = checkNotNull(asset, "asset cannot be null");
this.amount = checkNotNull(amount, "amount cannot be null");
}
/**
* Account that receives the payment.
*/
public KeyPair getDestination() {
return destination;
}
/**
* Asset to send to the destination account.
*/
public Asset getAsset() {
return asset;
}
/**
* Amount of the asset to send.
*/
public String getAmount() {
return amount;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
PaymentOp op = new PaymentOp();
// destination
AccountID destination = new AccountID();
destination.setAccountID(this.destination.getXdrPublicKey());
op.setDestination(destination);
// asset
op.setAsset(asset.toXdr());
// amount
Int64 amount = new Int64();
amount.setInt64(Operation.toXdrAmount(this.amount));
op.setAmount(amount);
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.PAYMENT);
body.setPaymentOp(op);
return body;
}
/**
* Builds Payment operation.
* @see PathPaymentOperation
*/
public static class Builder {
private final KeyPair destination;
private final Asset asset;
private final String amount;
private KeyPair mSourceAccount;
/**
* Construct a new PaymentOperation builder from a PaymentOp XDR.
* @param op {@link PaymentOp}
*/
Builder(PaymentOp op) {
destination = KeyPair.fromXdrPublicKey(op.getDestination().getAccountID());
asset = Asset.fromXdr(op.getAsset());
amount = Operation.fromXdrAmount(op.getAmount().getInt64().longValue());
}
/**
* Creates a new PaymentOperation builder.
* @param destination The destination keypair (uses only the public key).
* @param asset The asset to send.
* @param amount The amount to send in lumens.
* @throws ArithmeticException when amount has more than 7 decimal places.
*/
public Builder(KeyPair destination, Asset asset, String amount) {
this.destination = destination;
this.asset = asset;
this.amount = amount;
}
/**
* Sets the source account for this operation.
* @param account The operation's source account.
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair account) {
mSourceAccount = account;
return this;
}
/**
* Builds an operation
*/
public PaymentOperation build() {
PaymentOperation operation = new PaymentOperation(destination, asset, amount);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,110 @@
package org.stellar.sdk;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.xdr.Int32;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents Price. Price in Stellar is represented as a fraction.
*/
public class Price {
@SerializedName("n")
private final int n;
@SerializedName("d")
private final int d;
/**
* Create a new price. Price in Stellar is represented as a fraction.
* @param n numerator
* @param d denominator
*/
public Price(int n, int d) {
this.n = n;
this.d = d;
}
/**
* Returns numerator.
*/
public int getNumerator() {
return n;
}
/**
* Returns denominator
*/
public int getDenominator() {
return d;
}
/**
* Approximates <code>price</code> to a fraction.
* Please remember that this function can give unexpected results for values that cannot be represented as a
* fraction with 32-bit numerator and denominator. It's safer to create a Price object using the constructor.
* @param price Ex. "1.25"
*/
public static Price fromString(String price) {
checkNotNull(price, "price cannot be null");
BigDecimal maxInt = new BigDecimal(Integer.MAX_VALUE);
BigDecimal number = new BigDecimal(price);
BigDecimal a;
BigDecimal f;
List<BigDecimal[]> fractions = new ArrayList<BigDecimal[]>();
fractions.add(new BigDecimal[]{new BigDecimal(0), new BigDecimal(1)});
fractions.add(new BigDecimal[]{new BigDecimal(1), new BigDecimal(0)});
int i = 2;
while (true) {
if (number.compareTo(maxInt) > 0) {
break;
}
a = number.setScale(0, BigDecimal.ROUND_FLOOR);
f = number.subtract(a);
BigDecimal h = a.multiply(fractions.get(i - 1)[0]).add(fractions.get(i - 2)[0]);
BigDecimal k = a.multiply(fractions.get(i - 1)[1]).add(fractions.get(i - 2)[1]);
if (h.compareTo(maxInt) > 0 || k.compareTo(maxInt) > 0) {
break;
}
fractions.add(new BigDecimal[]{h, k});
if (f.compareTo(BigDecimal.ZERO) == 0) {
break;
}
number = new BigDecimal(1).divide(f, 20, BigDecimal.ROUND_HALF_UP);
i = i + 1;
}
BigDecimal n = fractions.get(fractions.size()-1)[0];
BigDecimal d = fractions.get(fractions.size()-1)[1];
return new Price(n.intValue(), d.intValue());
}
/**
* Generates Price XDR object.
*/
public org.stellar.sdk.xdr.Price toXdr() {
org.stellar.sdk.xdr.Price xdr = new org.stellar.sdk.xdr.Price();
Int32 n = new Int32();
Int32 d = new Int32();
n.setInt32(this.n);
d.setInt32(this.d);
xdr.setN(n);
xdr.setD(d);
return xdr;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Price)) {
return false;
}
Price price = (Price) object;
return this.getNumerator() == price.getNumerator() &&
this.getDenominator() == price.getDenominator();
}
}

View file

@ -0,0 +1,65 @@
package org.stellar.sdk;
import javax.crypto.Mac;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
final class SLIP10 {
private SLIP10() {
}
private static final String hmacSHA512algorithm = "HmacSHA512";
/**
* Derives only the private key for ED25519 in the manor defined in
* <a href="https://github.com/satoshilabs/slips/blob/master/slip-0010.md">SLIP-0010</a>.
*
* @param seed Seed, the BIP0039 output.
* @param indexes an array of indexes that define the path. E.g. for m/1'/2'/3', pass 1, 2, 3.
* As with Ed25519 non-hardened child indexes are not supported, this function treats all indexes
* as hardened.
* @return Private key.
* @throws NoSuchAlgorithmException If it cannot find the HmacSHA512 algorithm by name.
* @throws ShortBufferException Occurrence not expected.
* @throws InvalidKeyException Occurrence not expected.
*/
static byte[] deriveEd25519PrivateKey(final byte[] seed, final int... indexes)
throws NoSuchAlgorithmException, ShortBufferException, InvalidKeyException {
final byte[] I = new byte[64];
final Mac mac = Mac.getInstance(hmacSHA512algorithm);
// I = HMAC-SHA512(Key = bytes("ed25519 seed"), Data = seed)
mac.init(new SecretKeySpec("ed25519 seed".getBytes(Charset.forName("UTF-8")), hmacSHA512algorithm));
mac.update(seed);
mac.doFinal(I, 0);
for (int i : indexes) {
// I = HMAC-SHA512(Key = c_par, Data = 0x00 || ser256(k_par) || ser32(i'))
// which is simply:
// I = HMAC-SHA512(Key = Ir, Data = 0x00 || Il || ser32(i'))
// Key = Ir
mac.init(new SecretKeySpec(I, 32, 32, hmacSHA512algorithm));
// Data = 0x00
mac.update((byte) 0x00);
// Data += Il
mac.update(I, 0, 32);
// Data += ser32(i')
mac.update((byte) (i >> 24 | 0x80));
mac.update((byte) (i >> 16));
mac.update((byte) (i >> 8));
mac.update((byte) i);
// Write to I
mac.doFinal(I, 0);
}
final byte[] Il = new byte[32];
// copy head 32 bytes of I into Il
System.arraycopy(I, 0, Il, 0, 32);
return Il;
}
}

View file

@ -0,0 +1,203 @@
package org.stellar.sdk;
import com.google.gson.reflect.TypeToken;
import okhttp3.*;
import okhttp3.Response;
import org.stellar.sdk.requests.*;
import org.stellar.sdk.responses.*;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.concurrent.TimeUnit;
/**
* Main class used to connect to Horizon server.
*/
public class Server {
private HttpUrl serverURI;
private OkHttpClient httpClient;
/**
* submitHttpClient is used only for submitting transactions. The read timeout is longer.
*/
private OkHttpClient submitHttpClient;
/**
* HORIZON_SUBMIT_TIMEOUT is a time in seconds after Horizon sends a timeout response
* after internal txsub timeout.
*/
private static final int HORIZON_SUBMIT_TIMEOUT = 60;
public Server(String uri) {
serverURI = HttpUrl.parse(uri);
httpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
submitHttpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(HORIZON_SUBMIT_TIMEOUT + 5, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
}
public OkHttpClient getHttpClient() {
return httpClient;
}
public OkHttpClient getSubmitHttpClient() {
return submitHttpClient;
}
public void setHttpClient(OkHttpClient httpClient) {
this.httpClient = httpClient;
}
public void setSubmitHttpClient(OkHttpClient submitHttpClient) {
this.submitHttpClient = submitHttpClient;
}
/**
* Returns {@link RootResponse}.
*/
public RootResponse root() throws IOException {
TypeToken type = new TypeToken<RootResponse>() {};
ResponseHandler<RootResponse> responseHandler = new ResponseHandler<RootResponse>(type);
Request request = new Request.Builder().get().url(serverURI).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Returns {@link AccountsRequestBuilder} instance.
*/
public AccountsRequestBuilder accounts() {
return new AccountsRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link AssetsRequestBuilder} instance.
*/
public AssetsRequestBuilder assets() {
return new AssetsRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link EffectsRequestBuilder} instance.
*/
public EffectsRequestBuilder effects() {
return new EffectsRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link LedgersRequestBuilder} instance.
*/
public LedgersRequestBuilder ledgers() {
return new LedgersRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link OffersRequestBuilder} instance.
*/
public OffersRequestBuilder offers() {
return new OffersRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link OperationsRequestBuilder} instance.
*/
public OperationsRequestBuilder operations() {
return new OperationsRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link OperationFeeStatsResponse} instance.
*/
public OperationFeeStatsRequestBuilder operationFeeStats() {
return new OperationFeeStatsRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link OrderBookRequestBuilder} instance.
*/
public OrderBookRequestBuilder orderBook() {
return new OrderBookRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link TradesRequestBuilder} instance.
*/
public TradesRequestBuilder trades() {
return new TradesRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link TradeAggregationsRequestBuilder} instance.
*/
public TradeAggregationsRequestBuilder tradeAggregations(Asset baseAsset, Asset counterAsset, long startTime, long endTime, long resolution, long offset) {
return new TradeAggregationsRequestBuilder(httpClient, serverURI, baseAsset, counterAsset, startTime, endTime, resolution, offset);
}
/**
* Returns {@link PathsRequestBuilder} instance.
*/
public PathsRequestBuilder paths() {
return new PathsRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link PaymentsRequestBuilder} instance.
*/
public PaymentsRequestBuilder payments() {
return new PaymentsRequestBuilder(httpClient, serverURI);
}
/**
* Returns {@link TransactionsRequestBuilder} instance.
*/
public TransactionsRequestBuilder transactions() {
return new TransactionsRequestBuilder(httpClient, serverURI);
}
/**
* Submits transaction to the network.
* @param transaction transaction to submit to the network.
* @return {@link SubmitTransactionResponse}
* @throws SubmitTransactionTimeoutResponseException When Horizon returns a <code>Timeout</code> or connection timeout occured.
* @throws SubmitTransactionUnknownResponseException When unknown Horizon response is returned.
* @throws IOException
*/
public SubmitTransactionResponse submitTransaction(Transaction transaction) throws IOException {
HttpUrl transactionsURI = serverURI.newBuilder().addPathSegment("transactions").build();
RequestBody requestBody = new FormBody.Builder().add("tx", transaction.toEnvelopeXdrBase64()).build();
Request submitTransactionRequest = new Request.Builder().url(transactionsURI).post(requestBody).build();
Response response = null;
SubmitTransactionResponse submitTransactionResponse = null;
try {
response = this.submitHttpClient.newCall(submitTransactionRequest).execute();
switch (response.code()) {
case 200:
case 400:
submitTransactionResponse = GsonSingleton.getInstance().fromJson(response.body().string(), SubmitTransactionResponse.class);
break;
case 504:
throw new SubmitTransactionTimeoutResponseException();
default:
throw new SubmitTransactionUnknownResponseException(response.code(), response.body().string());
}
} catch (SocketTimeoutException e) {
throw new SubmitTransactionTimeoutResponseException();
} finally {
if (response != null) {
response.close();
}
}
return submitTransactionResponse;
}
}

View file

@ -0,0 +1,343 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.*;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html#set-options">SetOptions</a> operation.
* @see <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html">List of Operations</a>
*/
public class SetOptionsOperation extends Operation {
private final KeyPair inflationDestination;
private final Integer clearFlags;
private final Integer setFlags;
private final Integer masterKeyWeight;
private final Integer lowThreshold;
private final Integer mediumThreshold;
private final Integer highThreshold;
private final String homeDomain;
private final SignerKey signer;
private final Integer signerWeight;
private SetOptionsOperation(KeyPair inflationDestination, Integer clearFlags, Integer setFlags,
Integer masterKeyWeight, Integer lowThreshold, Integer mediumThreshold,
Integer highThreshold, String homeDomain, SignerKey signer, Integer signerWeight) {
this.inflationDestination = inflationDestination;
this.clearFlags = clearFlags;
this.setFlags = setFlags;
this.masterKeyWeight = masterKeyWeight;
this.lowThreshold = lowThreshold;
this.mediumThreshold = mediumThreshold;
this.highThreshold = highThreshold;
this.homeDomain = homeDomain;
this.signer = signer;
this.signerWeight = signerWeight;
}
/**
* Account of the inflation destination.
*/
public KeyPair getInflationDestination() {
return inflationDestination;
}
/**
* Indicates which flags to clear. For details about the flags, please refer to the <a href="https://www.stellar.org/developers/learn/concepts/accounts.html" target="_blank">accounts doc</a>.
* You can also use {@link AccountFlag} enum.
*/
public Integer getClearFlags() {
return clearFlags;
}
/**
* Indicates which flags to set. For details about the flags, please refer to the <a href="https://www.stellar.org/developers/learn/concepts/accounts.html" target="_blank">accounts doc</a>.
* You can also use {@link AccountFlag} enum.
*/
public Integer getSetFlags() {
return setFlags;
}
/**
* Weight of the master key.
*/
public Integer getMasterKeyWeight() {
return masterKeyWeight;
}
/**
* A number from 0-255 representing the threshold this account sets on all operations it performs that have <a href="https://www.stellar.org/developers/learn/concepts/multi-sig.html" target="_blank">a low threshold</a>.
*/
public Integer getLowThreshold() {
return lowThreshold;
}
/**
* A number from 0-255 representing the threshold this account sets on all operations it performs that have <a href="https://www.stellar.org/developers/learn/concepts/multi-sig.html" target="_blank">a medium threshold</a>.
*/
public Integer getMediumThreshold() {
return mediumThreshold;
}
/**
* A number from 0-255 representing the threshold this account sets on all operations it performs that have <a href="https://www.stellar.org/developers/learn/concepts/multi-sig.html" target="_blank">a high threshold</a>.
*/
public Integer getHighThreshold() {
return highThreshold;
}
/**
* The home domain of an account.
*/
public String getHomeDomain() {
return homeDomain;
}
/**
* Additional signer added/removed in this operation.
*/
public SignerKey getSigner() {
return signer;
}
/**
* Additional signer weight. The signer is deleted if the weight is 0.
*/
public Integer getSignerWeight() {
return signerWeight;
}
@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody() {
SetOptionsOp op = new SetOptionsOp();
if (inflationDestination != null) {
AccountID inflationDestination = new AccountID();
inflationDestination.setAccountID(this.inflationDestination.getXdrPublicKey());
op.setInflationDest(inflationDestination);
}
if (clearFlags != null) {
Uint32 clearFlags = new Uint32();
clearFlags.setUint32(this.clearFlags);
op.setClearFlags(clearFlags);
}
if (setFlags != null) {
Uint32 setFlags = new Uint32();
setFlags.setUint32(this.setFlags);
op.setSetFlags(setFlags);
}
if (masterKeyWeight != null) {
Uint32 uint32 = new Uint32();
uint32.setUint32(masterKeyWeight);
op.setMasterWeight(uint32);
}
if (lowThreshold != null) {
Uint32 uint32 = new Uint32();
uint32.setUint32(lowThreshold);
op.setLowThreshold(uint32);
}
if (mediumThreshold != null) {
Uint32 uint32 = new Uint32();
uint32.setUint32(mediumThreshold);
op.setMedThreshold(uint32);
}
if (highThreshold != null) {
Uint32 uint32 = new Uint32();
uint32.setUint32(highThreshold);
op.setHighThreshold(uint32);
}
if (homeDomain != null) {
String32 homeDomain = new String32();
homeDomain.setString32(this.homeDomain);
op.setHomeDomain(homeDomain);
}
if (signer != null) {
org.stellar.sdk.xdr.Signer signer = new org.stellar.sdk.xdr.Signer();
Uint32 weight = new Uint32();
weight.setUint32(signerWeight & 0xFF);
signer.setKey(this.signer);
signer.setWeight(weight);
op.setSigner(signer);
}
org.stellar.sdk.xdr.Operation.OperationBody body = new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.SET_OPTIONS);
body.setSetOptionsOp(op);
return body;
}
/**
* Builds SetOptions operation.
* @see SetOptionsOperation
*/
public static class Builder {
private KeyPair inflationDestination;
private Integer clearFlags;
private Integer setFlags;
private Integer masterKeyWeight;
private Integer lowThreshold;
private Integer mediumThreshold;
private Integer highThreshold;
private String homeDomain;
private SignerKey signer;
private Integer signerWeight;
private KeyPair sourceAccount;
Builder(SetOptionsOp op) {
if (op.getInflationDest() != null) {
inflationDestination = KeyPair.fromXdrPublicKey(
op.getInflationDest().getAccountID());
}
if (op.getClearFlags() != null) {
clearFlags = op.getClearFlags().getUint32();
}
if (op.getSetFlags() != null) {
setFlags = op.getSetFlags().getUint32();
}
if (op.getMasterWeight() != null) {
masterKeyWeight = op.getMasterWeight().getUint32().intValue();
}
if (op.getLowThreshold() != null) {
lowThreshold = op.getLowThreshold().getUint32().intValue();
}
if (op.getMedThreshold() != null) {
mediumThreshold = op.getMedThreshold().getUint32().intValue();
}
if (op.getHighThreshold() != null) {
highThreshold = op.getHighThreshold().getUint32().intValue();
}
if (op.getHomeDomain() != null) {
homeDomain = op.getHomeDomain().getString32();
}
if (op.getSigner() != null) {
signer = op.getSigner().getKey();
signerWeight = op.getSigner().getWeight().getUint32().intValue() & 0xFF;
}
}
/**
* Creates a new SetOptionsOperation builder.
*/
public Builder() {}
/**
* Sets the inflation destination for the account.
* @param inflationDestination The inflation destination account.
* @return Builder object so you can chain methods.
*/
public Builder setInflationDestination(KeyPair inflationDestination) {
this.inflationDestination = inflationDestination;
return this;
}
/**
* Clears the given flags from the account.
* @param clearFlags For details about the flags, please refer to the <a href="https://www.stellar.org/developers/learn/concepts/accounts.html" target="_blank">accounts doc</a>.
* @return Builder object so you can chain methods.
*/
public Builder setClearFlags(int clearFlags) {
this.clearFlags = clearFlags;
return this;
}
/**
* Sets the given flags on the account.
* @param setFlags For details about the flags, please refer to the <a href="https://www.stellar.org/developers/learn/concepts/accounts.html" target="_blank">accounts doc</a>.
* @return Builder object so you can chain methods.
*/
public Builder setSetFlags(int setFlags) {
this.setFlags = setFlags;
return this;
}
/**
* Weight of the master key.
* @param masterKeyWeight Number between 0 and 255
* @return Builder object so you can chain methods.
*/
public Builder setMasterKeyWeight(int masterKeyWeight) {
this.masterKeyWeight = masterKeyWeight;
return this;
}
/**
* A number from 0-255 representing the threshold this account sets on all operations it performs that have a low threshold.
* @param lowThreshold Number between 0 and 255
* @return Builder object so you can chain methods.
*/
public Builder setLowThreshold(int lowThreshold) {
this.lowThreshold = lowThreshold;
return this;
}
/**
* A number from 0-255 representing the threshold this account sets on all operations it performs that have a medium threshold.
* @param mediumThreshold Number between 0 and 255
* @return Builder object so you can chain methods.
*/
public Builder setMediumThreshold(int mediumThreshold) {
this.mediumThreshold = mediumThreshold;
return this;
}
/**
* A number from 0-255 representing the threshold this account sets on all operations it performs that have a high threshold.
* @param highThreshold Number between 0 and 255
* @return Builder object so you can chain methods.
*/
public Builder setHighThreshold(int highThreshold) {
this.highThreshold = highThreshold;
return this;
}
/**
* Sets the account's home domain address used in <a href="https://www.stellar.org/developers/learn/concepts/federation.html" target="_blank">Federation</a>.
* @param homeDomain A string of the address which can be up to 32 characters.
* @return Builder object so you can chain methods.
*/
public Builder setHomeDomain(String homeDomain) {
if (homeDomain.length() > 32) {
throw new IllegalArgumentException("Home domain must be <= 32 characters");
}
this.homeDomain = homeDomain;
return this;
}
/**
* Add, update, or remove a signer from the account. Signer is deleted if the weight = 0;
* @param signer The signer key. Use {@link org.stellar.sdk.Signer} helper to create this object.
* @param weight The weight to attach to the signer (0-255).
* @return Builder object so you can chain methods.
*/
public Builder setSigner(SignerKey signer, Integer weight) {
checkNotNull(signer, "signer cannot be null");
checkNotNull(weight, "weight cannot be null");
this.signer = signer;
signerWeight = weight & 0xFF;
return this;
}
/**
* Sets the source account for this operation.
* @param sourceAccount The operation's source account.
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(KeyPair sourceAccount) {
this.sourceAccount = sourceAccount;
return this;
}
/**
* Builds an operation
*/
public SetOptionsOperation build() {
SetOptionsOperation operation = new SetOptionsOperation(inflationDestination, clearFlags,
setFlags, masterKeyWeight, lowThreshold, mediumThreshold, highThreshold,
homeDomain, signer, signerWeight);
if (sourceAccount != null) {
operation.setSourceAccount(sourceAccount);
}
return operation;
}
}
}

View file

@ -0,0 +1,83 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.SignerKey;
import org.stellar.sdk.xdr.SignerKeyType;
import org.stellar.sdk.xdr.Uint256;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Signer is a helper class that creates {@link org.stellar.sdk.xdr.SignerKey} objects.
*/
public class Signer {
/**
* Create <code>ed25519PublicKey</code> {@link org.stellar.sdk.xdr.SignerKey} from
* a {@link org.stellar.sdk.KeyPair}
* @param keyPair
* @return org.stellar.sdk.xdr.SignerKey
*/
public static SignerKey ed25519PublicKey(KeyPair keyPair) {
checkNotNull(keyPair, "keyPair cannot be null");
return keyPair.getXdrSignerKey();
}
/**
* Create <code>sha256Hash</code> {@link org.stellar.sdk.xdr.SignerKey} from
* a sha256 hash of a preimage.
* @param hash
* @return org.stellar.sdk.xdr.SignerKey
*/
public static SignerKey sha256Hash(byte[] hash) {
checkNotNull(hash, "hash cannot be null");
SignerKey signerKey = new SignerKey();
Uint256 value = Signer.createUint256(hash);
signerKey.setDiscriminant(SignerKeyType.SIGNER_KEY_TYPE_HASH_X);
signerKey.setHashX(value);
return signerKey;
}
/**
* Create <code>preAuthTx</code> {@link org.stellar.sdk.xdr.SignerKey} from
* a {@link org.stellar.sdk.xdr.Transaction} hash.
* @param tx
* @return org.stellar.sdk.xdr.SignerKey
*/
public static SignerKey preAuthTx(Transaction tx) {
checkNotNull(tx, "tx cannot be null");
SignerKey signerKey = new SignerKey();
Uint256 value = Signer.createUint256(tx.hash());
signerKey.setDiscriminant(SignerKeyType.SIGNER_KEY_TYPE_PRE_AUTH_TX);
signerKey.setPreAuthTx(value);
return signerKey;
}
/**
* Create <code>preAuthTx</code> {@link org.stellar.sdk.xdr.SignerKey} from
* a transaction hash.
* @param hash
* @return org.stellar.sdk.xdr.SignerKey
*/
public static SignerKey preAuthTx(byte[] hash) {
checkNotNull(hash, "hash cannot be null");
SignerKey signerKey = new SignerKey();
Uint256 value = Signer.createUint256(hash);
signerKey.setDiscriminant(SignerKeyType.SIGNER_KEY_TYPE_PRE_AUTH_TX);
signerKey.setPreAuthTx(value);
return signerKey;
}
private static Uint256 createUint256(byte[] hash) {
if (hash.length != 32) {
throw new RuntimeException("hash must be 32 bytes long");
}
Uint256 value = new Uint256();
value.setUint256(hash);
return value;
}
}

View file

@ -0,0 +1,157 @@
package org.stellar.sdk;
import com.google.common.io.BaseEncoding;
import java.io.*;
import java.util.Arrays;
class StrKey {
public enum VersionByte {
ACCOUNT_ID((byte)(6 << 3)), // G
SEED((byte)(18 << 3)), // S
PRE_AUTH_TX((byte)(19 << 3)), // T
SHA256_HASH((byte)(23 << 3)); // X
private final byte value;
VersionByte(byte value) {
this.value = value;
}
public int getValue() {
return value;
}
}
private static BaseEncoding base32Encoding = BaseEncoding.base32().upperCase().omitPadding();
public static String encodeStellarAccountId(byte[] data) {
char[] encoded = encodeCheck(VersionByte.ACCOUNT_ID, data);
return String.valueOf(encoded);
}
public static byte[] decodeStellarAccountId(String data) {
return decodeCheck(VersionByte.ACCOUNT_ID, data.toCharArray());
}
public static char[] encodeStellarSecretSeed(byte[] data) {
return encodeCheck(VersionByte.SEED, data);
}
public static byte[] decodeStellarSecretSeed(char[] data) {
return decodeCheck(VersionByte.SEED, data);
}
public static String encodePreAuthTx(byte[] data) {
char[] encoded = encodeCheck(VersionByte.PRE_AUTH_TX, data);
return String.valueOf(encoded);
}
public static byte[] decodePreAuthTx(String data) {
return decodeCheck(VersionByte.PRE_AUTH_TX, data.toCharArray());
}
public static String encodeSha256Hash(byte[] data) {
char[] encoded = encodeCheck(VersionByte.SHA256_HASH, data);
return String.valueOf(encoded);
}
public static byte[] decodeSha256Hash(String data) {
return decodeCheck(VersionByte.SHA256_HASH, data.toCharArray());
}
protected static char[] encodeCheck(VersionByte versionByte, byte[] data) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(versionByte.getValue());
outputStream.write(data);
byte payload[] = outputStream.toByteArray();
byte checksum[] = StrKey.calculateChecksum(payload);
outputStream.write(checksum);
byte unencoded[] = outputStream.toByteArray();
// Why not use base32Encoding.encode here?
// We don't want secret seed to be stored as String in memory because of security reasons. It's impossible
// to erase it from memory when we want it to be erased (ASAP).
CharArrayWriter charArrayWriter = new CharArrayWriter(unencoded.length);
OutputStream charOutputStream = StrKey.base32Encoding.encodingStream(charArrayWriter);
charOutputStream.write(unencoded);
char[] charsEncoded = charArrayWriter.toCharArray();
if (VersionByte.SEED == versionByte) {
Arrays.fill(unencoded, (byte) 0);
Arrays.fill(payload, (byte) 0);
Arrays.fill(checksum, (byte) 0);
// Clean charArrayWriter internal buffer
int bufferSize = charArrayWriter.size();
char[] zeros = new char[bufferSize];
Arrays.fill(zeros, '0');
charArrayWriter.reset();
charArrayWriter.write(zeros);
}
return charsEncoded;
} catch (IOException e) {
throw new AssertionError(e);
}
}
protected static byte[] decodeCheck(VersionByte versionByte, char[] encoded) {
byte[] bytes = new byte[encoded.length];
for (int i = 0; i < encoded.length; i++) {
if (encoded[i] > 127) {
throw new IllegalArgumentException("Illegal characters in encoded char array.");
}
bytes[i] = (byte) encoded[i];
}
byte[] decoded = StrKey.base32Encoding.decode(java.nio.CharBuffer.wrap(encoded));
byte decodedVersionByte = decoded[0];
byte[] payload = Arrays.copyOfRange(decoded, 0, decoded.length-2);
byte[] data = Arrays.copyOfRange(payload, 1, payload.length);
byte[] checksum = Arrays.copyOfRange(decoded, decoded.length-2, decoded.length);
if (decodedVersionByte != versionByte.getValue()) {
throw new FormatException("Version byte is invalid");
}
byte[] expectedChecksum = StrKey.calculateChecksum(payload);
if (!Arrays.equals(expectedChecksum, checksum)) {
throw new FormatException("Checksum invalid");
}
if (VersionByte.SEED.getValue() == decodedVersionByte) {
Arrays.fill(bytes, (byte) 0);
Arrays.fill(decoded, (byte) 0);
Arrays.fill(payload, (byte) 0);
}
return data;
}
protected static byte[] calculateChecksum(byte[] bytes) {
// This code calculates CRC16-XModem checksum
// Ported from https://github.com/alexgorbatchev/node-crc
int crc = 0x0000;
int count = bytes.length;
int i = 0;
int code;
while (count > 0) {
code = crc >>> 8 & 0xFF;
code ^= bytes[i++] & 0xFF;
code ^= code >>> 4;
crc = crc << 8 & 0xFFFF;
crc ^= code;
code = code << 5 & 0xFFFF;
crc ^= code;
code = code << 7 & 0xFFFF;
crc ^= code;
count--;
}
// little-endian
return new byte[] {
(byte)crc,
(byte)(crc >>> 8)};
}
}

View file

@ -0,0 +1,66 @@
package org.stellar.sdk;
import org.stellar.sdk.xdr.Uint64;
/**
* <p>TimeBounds represents the time interval that a transaction is valid.</p>
* @see Transaction
*/
final public class TimeBounds {
final private long mMinTime;
final private long mMaxTime;
/**
* @param minTime 64bit Unix timestamp
* @param maxTime 64bit Unix timestamp
*/
public TimeBounds(long minTime, long maxTime) {
if (maxTime > 0 && minTime >= maxTime) {
throw new IllegalArgumentException("minTime must be >= maxTime");
}
mMinTime = minTime;
mMaxTime = maxTime;
}
public long getMinTime() {
return mMinTime;
}
public long getMaxTime() {
return mMaxTime;
}
public static TimeBounds fromXdr(org.stellar.sdk.xdr.TimeBounds timeBounds) {
if (timeBounds == null) {
return null;
}
return new TimeBounds(
timeBounds.getMinTime().getUint64().longValue(),
timeBounds.getMaxTime().getUint64().longValue()
);
}
public org.stellar.sdk.xdr.TimeBounds toXdr() {
org.stellar.sdk.xdr.TimeBounds timeBounds = new org.stellar.sdk.xdr.TimeBounds();
Uint64 minTime = new Uint64();
Uint64 maxTime = new Uint64();
minTime.setUint64(mMinTime);
maxTime.setUint64(mMaxTime);
timeBounds.setMinTime(minTime);
timeBounds.setMaxTime(maxTime);
return timeBounds;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimeBounds that = (TimeBounds) o;
if (mMinTime != that.mMinTime) return false;
return mMaxTime == that.mMaxTime;
}
}

View file

@ -0,0 +1,381 @@
package org.stellar.sdk;
import com.google.common.io.BaseEncoding;
import org.stellar.sdk.xdr.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents <a href="https://www.stellar.org/developers/learn/concepts/transactions.html" target="_blank">Transaction</a> in Stellar network.
*/
public class Transaction {
private static final int BASE_FEE = 100;
protected final int mFee;
protected final KeyPair mSourceAccount;
protected final long mSequenceNumber;
protected final Operation[] mOperations;
protected final Memo mMemo;
protected final TimeBounds mTimeBounds;
protected List<DecoratedSignature> mSignatures;
Transaction(KeyPair sourceAccount, int fee, long sequenceNumber, Operation[] operations, Memo memo, TimeBounds timeBounds) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
mSequenceNumber = checkNotNull(sequenceNumber, "sequenceNumber cannot be null");
mOperations = checkNotNull(operations, "operations cannot be null");
checkArgument(operations.length > 0, "At least one operation required");
mFee = fee;
mSignatures = new ArrayList<DecoratedSignature>();
mMemo = memo != null ? memo : Memo.none();
mTimeBounds = timeBounds;
}
/**
* Adds a new signature ed25519PublicKey to this transaction.
* @param signer {@link KeyPair} object representing a signer
*/
public void sign(KeyPair signer) {
checkNotNull(signer, "signer cannot be null");
byte[] txHash = this.hash();
mSignatures.add(signer.signDecorated(txHash));
}
/**
* Adds a new sha256Hash signature to this transaction by revealing preimage.
* @param preimage the sha256 hash of preimage should be equal to signer hash
*/
public void sign(byte[] preimage) {
checkNotNull(preimage, "preimage cannot be null");
org.stellar.sdk.xdr.Signature signature = new org.stellar.sdk.xdr.Signature();
signature.setSignature(preimage);
byte[] hash = Util.hash(preimage);
byte[] signatureHintBytes = Arrays.copyOfRange(hash, hash.length - 4, hash.length);
SignatureHint signatureHint = new SignatureHint();
signatureHint.setSignatureHint(signatureHintBytes);
DecoratedSignature decoratedSignature = new DecoratedSignature();
decoratedSignature.setHint(signatureHint);
decoratedSignature.setSignature(signature);
mSignatures.add(decoratedSignature);
}
/**
* Returns transaction hash.
*/
public byte[] hash() {
return Util.hash(this.signatureBase());
}
/**
* Returns signature base.
*/
public byte[] signatureBase() {
if (Network.current() == null) {
throw new NoNetworkSelectedException();
}
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Hashed NetworkID
outputStream.write(Network.current().getNetworkId());
// Envelope Type - 4 bytes
outputStream.write(ByteBuffer.allocate(4).putInt(EnvelopeType.ENVELOPE_TYPE_TX.getValue()).array());
// Transaction XDR bytes
ByteArrayOutputStream txOutputStream = new ByteArrayOutputStream();
XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(txOutputStream);
org.stellar.sdk.xdr.Transaction.encode(xdrOutputStream, this.toXdr());
outputStream.write(txOutputStream.toByteArray());
return outputStream.toByteArray();
} catch (IOException exception) {
return null;
}
}
public KeyPair getSourceAccount() {
return mSourceAccount;
}
public long getSequenceNumber() {
return mSequenceNumber;
}
public List<DecoratedSignature> getSignatures() {
return mSignatures;
}
public Memo getMemo() {
return mMemo;
}
/**
* @return TimeBounds, or null (representing no time restrictions)
*/
public TimeBounds getTimeBounds() {
return mTimeBounds;
}
/**
* Returns fee paid for transaction in stroops (1 stroop = 0.0000001 XLM).
*/
public int getFee() {
return mFee;
}
/**
* Returns operations in this transaction.
*/
public Operation[] getOperations() {
return mOperations;
}
/**
* Generates Transaction XDR object.
*/
public org.stellar.sdk.xdr.Transaction toXdr() {
// fee
org.stellar.sdk.xdr.Uint32 fee = new org.stellar.sdk.xdr.Uint32();
fee.setUint32(mFee);
// sequenceNumber
org.stellar.sdk.xdr.Int64 sequenceNumberUint = new org.stellar.sdk.xdr.Int64();
sequenceNumberUint.setInt64(mSequenceNumber);
org.stellar.sdk.xdr.SequenceNumber sequenceNumber = new org.stellar.sdk.xdr.SequenceNumber();
sequenceNumber.setSequenceNumber(sequenceNumberUint);
// sourceAccount
org.stellar.sdk.xdr.AccountID sourceAccount = new org.stellar.sdk.xdr.AccountID();
sourceAccount.setAccountID(mSourceAccount.getXdrPublicKey());
// operations
org.stellar.sdk.xdr.Operation[] operations = new org.stellar.sdk.xdr.Operation[mOperations.length];
for (int i = 0; i < mOperations.length; i++) {
operations[i] = mOperations[i].toXdr();
}
// ext
org.stellar.sdk.xdr.Transaction.TransactionExt ext = new org.stellar.sdk.xdr.Transaction.TransactionExt();
ext.setDiscriminant(0);
org.stellar.sdk.xdr.Transaction transaction = new org.stellar.sdk.xdr.Transaction();
transaction.setFee(fee);
transaction.setSeqNum(sequenceNumber);
transaction.setSourceAccount(sourceAccount);
transaction.setOperations(operations);
transaction.setMemo(mMemo.toXdr());
transaction.setTimeBounds(mTimeBounds == null ? null : mTimeBounds.toXdr());
transaction.setExt(ext);
return transaction;
}
/**
* Creates a <code>Transaction</code> instance from previously build <code>TransactionEnvelope</code>
* @param envelope Base-64 encoded <code>TransactionEnvelope</code>
* @return
* @throws IOException
*/
public static Transaction fromEnvelopeXdr(String envelope) throws IOException {
BaseEncoding base64Encoding = BaseEncoding.base64();
byte[] bytes = base64Encoding.decode(envelope);
TransactionEnvelope transactionEnvelope = TransactionEnvelope.decode(new XdrDataInputStream(new ByteArrayInputStream(bytes)));
return fromEnvelopeXdr(transactionEnvelope);
}
/**
* Creates a <code>Transaction</code> instance from previously build <code>TransactionEnvelope</code>
* @param envelope
* @return
*/
public static Transaction fromEnvelopeXdr(TransactionEnvelope envelope) {
org.stellar.sdk.xdr.Transaction tx = envelope.getTx();
int mFee = tx.getFee().getUint32();
KeyPair mSourceAccount = KeyPair.fromXdrPublicKey(tx.getSourceAccount().getAccountID());
Long mSequenceNumber = tx.getSeqNum().getSequenceNumber().getInt64();
Memo mMemo = Memo.fromXdr(tx.getMemo());
TimeBounds mTimeBounds = TimeBounds.fromXdr(tx.getTimeBounds());
Operation[] mOperations = new Operation[tx.getOperations().length];
for (int i = 0; i < tx.getOperations().length; i++) {
mOperations[i] = Operation.fromXdr(tx.getOperations()[i]);
}
Transaction transaction = new Transaction(mSourceAccount, mFee, mSequenceNumber, mOperations, mMemo, mTimeBounds);
for (DecoratedSignature signature : envelope.getSignatures()) {
transaction.mSignatures.add(signature);
}
return transaction;
}
/**
* Generates TransactionEnvelope XDR object. Transaction need to have at least one signature.
*/
public org.stellar.sdk.xdr.TransactionEnvelope toEnvelopeXdr() {
if (mSignatures.size() == 0) {
throw new NotEnoughSignaturesException("Transaction must be signed by at least one signer. Use transaction.sign().");
}
org.stellar.sdk.xdr.TransactionEnvelope xdr = new org.stellar.sdk.xdr.TransactionEnvelope();
org.stellar.sdk.xdr.Transaction transaction = this.toXdr();
xdr.setTx(transaction);
DecoratedSignature[] signatures = new DecoratedSignature[mSignatures.size()];
signatures = mSignatures.toArray(signatures);
xdr.setSignatures(signatures);
return xdr;
}
/**
* Returns base64-encoded TransactionEnvelope XDR object. Transaction need to have at least one signature.
*/
public String toEnvelopeXdrBase64() {
try {
org.stellar.sdk.xdr.TransactionEnvelope envelope = this.toEnvelopeXdr();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(outputStream);
org.stellar.sdk.xdr.TransactionEnvelope.encode(xdrOutputStream, envelope);
BaseEncoding base64Encoding = BaseEncoding.base64();
return base64Encoding.encode(outputStream.toByteArray());
} catch (IOException e) {
throw new AssertionError(e);
}
}
/**
* Builds a new Transaction object.
*/
public static class Builder {
private final TransactionBuilderAccount mSourceAccount;
private Memo mMemo;
private TimeBounds mTimeBounds;
List<Operation> mOperations;
private boolean timeoutSet;
public static final long TIMEOUT_INFINITE = 0;
/**
* Construct a new transaction builder.
* @param sourceAccount The source account for this transaction. This account is the account
* who will use a sequence number. When build() is called, the account object's sequence number
* will be incremented.
*/
public Builder(TransactionBuilderAccount sourceAccount) {
checkNotNull(sourceAccount, "sourceAccount cannot be null");
mSourceAccount = sourceAccount;
mOperations = Collections.synchronizedList(new ArrayList<Operation>());
}
public int getOperationsCount() {
return mOperations.size();
}
/**
* Adds a new <a href="https://www.stellar.org/developers/learn/concepts/list-of-operations.html" target="_blank">operation</a> to this transaction.
* @param operation
* @return Builder object so you can chain methods.
* @see Operation
*/
public Builder addOperation(Operation operation) {
checkNotNull(operation, "operation cannot be null");
mOperations.add(operation);
return this;
}
/**
* Adds a <a href="https://www.stellar.org/developers/learn/concepts/transactions.html" target="_blank">memo</a> to this transaction.
* @param memo
* @return Builder object so you can chain methods.
* @see Memo
*/
public Builder addMemo(Memo memo) {
if (mMemo != null) {
throw new RuntimeException("Memo has been already added.");
}
checkNotNull(memo, "memo cannot be null");
mMemo = memo;
return this;
}
/**
* Adds a <a href="https://www.stellar.org/developers/learn/concepts/transactions.html" target="_blank">time-bounds</a> to this transaction.
* @param timeBounds
* @return Builder object so you can chain methods.
* @see TimeBounds
*/
public Builder addTimeBounds(TimeBounds timeBounds) {
if (mTimeBounds != null) {
throw new RuntimeException("TimeBounds has been already added.");
}
checkNotNull(timeBounds, "timeBounds cannot be null");
mTimeBounds = timeBounds;
return this;
}
/**
* Because of the distributed nature of the Stellar network it is possible that the status of your transaction
* will be determined after a long time if the network is highly congested.
* If you want to be sure to receive the status of the transaction within a given period you should set the
* {@link TimeBounds} with <code>maxTime</code> on the transaction (this is what <code>setTimeout</code> does
* internally; if there's <code>minTime</code> set but no <code>maxTime</code> it will be added).
* Call to <code>Builder.setTimeout</code> is required if Transaction does not have <code>max_time</code> set.
* If you don't want to set timeout, use <code>TIMEOUT_INFINITE</code>. In general you should set
* <code>TIMEOUT_INFINITE</code> only in smart contracts.
* Please note that Horizon may still return <code>504 Gateway Timeout</code> error, even for short timeouts.
* In such case you need to resubmit the same transaction again without making any changes to receive a status.
* This method is using the machine system time (UTC), make sure it is set correctly.
* @param timeout Timeout in seconds.
* @see TimeBounds
* @return
*/
public Builder setTimeout(long timeout) {
if (mTimeBounds != null && mTimeBounds.getMaxTime() > 0) {
throw new RuntimeException("TimeBounds.max_time has been already set - setting timeout would overwrite it.");
}
if (timeout < 0) {
throw new RuntimeException("timeout cannot be negative");
}
timeoutSet = true;
if (timeout > 0) {
long timeoutTimestamp = System.currentTimeMillis() / 1000L + timeout;
if (mTimeBounds == null) {
mTimeBounds = new TimeBounds(0, timeoutTimestamp);
} else {
mTimeBounds = new TimeBounds(mTimeBounds.getMinTime(), timeoutTimestamp);
}
}
return this;
}
/**
* Builds a transaction. It will increment sequence number of the source account.
*/
public Transaction build() {
// Ensure setTimeout called or maxTime is set
if ((mTimeBounds == null || mTimeBounds != null && mTimeBounds.getMaxTime() == 0) && !timeoutSet) {
throw new RuntimeException("TimeBounds has to be set or you must call setTimeout(TIMEOUT_INFINITE).");
}
Operation[] operations = new Operation[mOperations.size()];
operations = mOperations.toArray(operations);
Transaction transaction = new Transaction(mSourceAccount.getKeypair(), operations.length * BASE_FEE, mSourceAccount.getIncrementedSequenceNumber(), operations, mMemo, mTimeBounds);
// Increment sequence number when there were no exceptions when creating a transaction
mSourceAccount.incrementSequenceNumber();
return transaction;
}
}
}

View file

@ -0,0 +1,26 @@
package org.stellar.sdk;
/**
* Specifies interface for Account object used in {@link org.stellar.sdk.Transaction.Builder}
*/
public interface TransactionBuilderAccount {
/**
* Returns keypair associated with this Account
*/
KeyPair getKeypair();
/**
* Returns current sequence number ot this Account.
*/
Long getSequenceNumber();
/**
* Returns sequence number incremented by one, but does not increment internal counter.
*/
Long getIncrementedSequenceNumber();
/**
* Increments sequence number in this object by one.
*/
void incrementSequenceNumber();
}

View file

@ -0,0 +1,75 @@
package org.stellar.sdk;
import org.stellar.sdk.responses.AccountResponse;
import org.stellar.sdk.xdr.DecoratedSignature;
import org.stellar.sdk.xdr.PublicKey;
import org.stellar.sdk.xdr.PublicKeyType;
import org.stellar.sdk.xdr.SignatureHint;
import org.stellar.sdk.xdr.Uint256;
import org.stellar.sdk.xdr.XdrDataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
public class TransactionEx extends Transaction {
public TransactionEx(KeyPair sourceAccount, int fee, long sequenceNumber, Operation[] operations, Memo memo, TimeBounds timeBounds) {
super(sourceAccount, fee, sequenceNumber, operations, memo, timeBounds);
}
/**
* Builds a transaction. It will increment sequence number of the source account.
*/
public static TransactionEx buildEx(int timeout, AccountResponse sourceAccount, Operation operation)
{
long timeoutTimestamp = System.currentTimeMillis() / 1000L + timeout;
TimeBounds mTimeBounds = new TimeBounds(0, timeoutTimestamp);
Operation[] operations = new Operation[1];
operations[0] = operation;
TransactionEx transaction = new TransactionEx(sourceAccount.getKeypair(), operations.length * 100, sourceAccount.getIncrementedSequenceNumber(), operations, Memo.text(""), mTimeBounds);
// Increment sequence number when there were no exceptions when creating a transaction
sourceAccount.incrementSequenceNumber();
return transaction;
}
public PublicKey getXdrPublicKey() {
PublicKey publicKey = new PublicKey();
publicKey.setDiscriminant(PublicKeyType.PUBLIC_KEY_TYPE_ED25519);
Uint256 uint256 = new Uint256();
uint256.setUint256(mSourceAccount.getPublicKey());
publicKey.setEd25519(uint256);
return publicKey;
}
public SignatureHint getSignatureHint() {
try {
ByteArrayOutputStream publicKeyBytesStream = new ByteArrayOutputStream();
XdrDataOutputStream xdrOutputStream = new XdrDataOutputStream(publicKeyBytesStream);
PublicKey.encode(xdrOutputStream, this.getXdrPublicKey());
byte[] publicKeyBytes = publicKeyBytesStream.toByteArray();
byte[] signatureHintBytes = Arrays.copyOfRange(publicKeyBytes, publicKeyBytes.length - 4, publicKeyBytes.length);
SignatureHint signatureHint = new SignatureHint();
signatureHint.setSignatureHint(signatureHintBytes);
return signatureHint;
} catch (IOException e) {
throw new AssertionError(e);
}
}
public void setSign(byte[] signFromCard) {
// byte[] txHash = this.hash();
byte[] signatureBytes = signFromCard;//this.sign(txHash);
org.stellar.sdk.xdr.Signature signature = new org.stellar.sdk.xdr.Signature();
signature.setSignature(signatureBytes);
DecoratedSignature decoratedSignature = new DecoratedSignature();
decoratedSignature.setHint(this.getSignatureHint());
decoratedSignature.setSignature(signature);
mSignatures.add(decoratedSignature);
}
}

View file

@ -0,0 +1,73 @@
package org.stellar.sdk;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
class Util {
public static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public static byte[] hexToBytes(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
/**
* Returns SHA-256 hash of <code>data</code>.
* @param data
*/
public static byte[] hash(byte[] data) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(data);
return md.digest();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 not implemented");
}
}
/**
* Pads <code>bytes</code> array to <code>length</code> with zeros.
* @param bytes
* @param length
*/
static byte[] paddedByteArray(byte[] bytes, int length) {
byte[] finalBytes = new byte[length];
Arrays.fill(finalBytes, (byte) 0);
System.arraycopy(bytes, 0, finalBytes, 0, bytes.length);
return finalBytes;
}
/**
* Pads <code>string</code> to <code>length</code> with zeros.
* @param string
* @param length
*/
static byte[] paddedByteArray(String string, int length) {
return Util.paddedByteArray(string.getBytes(), length);
}
/**
* Remove zeros from the end of <code>bytes</code> array.
* @param bytes
*/
static String paddedByteArrayToString(byte[] bytes) {
return new String(bytes).split("\0")[0];
}
}

View file

@ -0,0 +1,107 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.AccountResponse;
import org.stellar.sdk.responses.Page;
import java.io.IOException;
/**
* Builds requests connected to accounts.
*/
public class AccountsRequestBuilder extends RequestBuilder {
public AccountsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "accounts");
}
/**
* Requests specific <code>uri</code> and returns {@link AccountResponse}.
* This method is helpful for getting the links.
* @throws IOException
*/
public AccountResponse account(HttpUrl uri) throws IOException {
TypeToken type = new TypeToken<AccountResponse>() {};
ResponseHandler<AccountResponse> responseHandler = new ResponseHandler<AccountResponse>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Requests <code>GET /accounts/{account}</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/accounts-single.html">Account Details</a>
* @param account Account to fetch
* @throws IOException
*/
public AccountResponse account(KeyPair account) throws IOException {
this.setSegments("accounts", account.getAccountId());
return this.account(this.buildUri());
}
/**
* Requests specific <code>uri</code> and returns {@link Page} of {@link AccountResponse}.
* This method is helpful for getting the next set of results.
* @return {@link Page} of {@link AccountResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<AccountResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<AccountResponse>>() {};
ResponseHandler<Page<AccountResponse>> responseHandler = new ResponseHandler<Page<AccountResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link EventListener} implementation with {@link AccountResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<AccountResponse> stream(final EventListener<AccountResponse> listener) {
return SSEStream.create(httpClient,this,AccountResponse.class,listener);
}
/**
* Build and execute request. <strong>Warning!</strong> {@link AccountResponse}s in {@link Page} will contain only <code>keypair</code> field.
* @return {@link Page} of {@link AccountResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<AccountResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public AccountsRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public AccountsRequestBuilder limit(int number) {
super.limit(number);
return this;
}
@Override
public AccountsRequestBuilder order(Order direction) {
super.order(direction);
return this;
}
}

View file

@ -0,0 +1,41 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.AssetResponse;
import java.io.IOException;
public class AssetsRequestBuilder extends RequestBuilder {
public AssetsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "assets");
}
public AssetsRequestBuilder assetCode(String assetCode) {
uriBuilder.setQueryParameter("asset_code", assetCode);
return this;
}
public AssetsRequestBuilder assetIssuer(String assetIssuer) {
uriBuilder.setQueryParameter("asset_issuer", assetIssuer);
return this;
}
public static Page<AssetResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<AssetResponse>>() {};
ResponseHandler<Page<AssetResponse>> responseHandler = new ResponseHandler<Page<AssetResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
public Page<AssetResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
}

View file

@ -0,0 +1,124 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.effects.EffectResponse;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Builds requests connected to effects.
*/
public class EffectsRequestBuilder extends RequestBuilder {
public EffectsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "effects");
}
/**
* Builds request to <code>GET /accounts/{account}/effects</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/effects-for-account.html">Effects for Account</a>
* @param account Account for which to get effects
*/
public EffectsRequestBuilder forAccount(KeyPair account) {
account = checkNotNull(account, "account cannot be null");
this.setSegments("accounts", account.getAccountId(), "effects");
return this;
}
/**
* Builds request to <code>GET /ledgers/{ledgerSeq}/effects</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/effects-for-ledger.html">Effects for Ledger</a>
* @param ledgerSeq Ledger for which to get effects
*/
public EffectsRequestBuilder forLedger(long ledgerSeq) {
this.setSegments("ledgers", String.valueOf(ledgerSeq), "effects");
return this;
}
/**
* Builds request to <code>GET /transactions/{transactionId}/effects</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/effects-for-transaction.html">Effect for Transaction</a>
* @param transactionId Transaction ID for which to get effects
*/
public EffectsRequestBuilder forTransaction(String transactionId) {
transactionId = checkNotNull(transactionId, "transactionId cannot be null");
this.setSegments("transactions", transactionId, "effects");
return this;
}
/**
* Builds request to <code>GET /operation/{operationId}/effects</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/effects-for-operation.html">Effect for Operation</a>
* @param operationId Operation ID for which to get effects
*/
public EffectsRequestBuilder forOperation(long operationId) {
this.setSegments("operations", String.valueOf(operationId), "effects");
return this;
}
/**
* Requests specific <code>uri</code> and returns {@link Page} of {@link EffectResponse}.
* This method is helpful for getting the next set of results.
* @return {@link Page} of {@link EffectResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<EffectResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<EffectResponse>>() {};
ResponseHandler<Page<EffectResponse>> responseHandler = new ResponseHandler<Page<EffectResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link EventListener} implementation with {@link EffectResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<EffectResponse> stream(final EventListener<EffectResponse> listener) {
return SSEStream.create(httpClient,this,EffectResponse.class,listener);
}
/**
* Build and execute request.
* @return {@link Page} of {@link EffectResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<EffectResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public EffectsRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public EffectsRequestBuilder limit(int number) {
super.limit(number);
return this;
}
@Override
public EffectsRequestBuilder order(Order direction) {
super.order(direction);
return this;
}
}

View file

@ -0,0 +1,23 @@
package org.stellar.sdk.requests;
/**
* Exception thrown when request returned an non-success HTTP code.
*/
public class ErrorResponse extends RuntimeException {
private int code;
private String body;
public ErrorResponse(int code, String body) {
super("Error response from the server.");
this.code = code;
this.body = body;
}
public int getCode() {
return code;
}
public String getBody() {
return body;
}
}

View file

@ -0,0 +1,12 @@
package org.stellar.sdk.requests;
/**
* This interface is used in {@link RequestBuilder} classes <code>stream</code> method.
*/
public interface EventListener<T> {
/**
* This method will be called when new event is sent by a server.
* @param object object deserialized from the event data
*/
void onEvent(T object);
}

View file

@ -0,0 +1,106 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.responses.LedgerResponse;
import org.stellar.sdk.responses.Page;
import java.io.IOException;
/**
* Builds requests connected to ledgers.
*/
public class LedgersRequestBuilder extends RequestBuilder {
public LedgersRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "ledgers");
}
/**
* Requests specific <code>uri</code> and returns {@link LedgerResponse}.
* This method is helpful for getting the links.
* @throws IOException
*/
public LedgerResponse ledger(HttpUrl uri) throws IOException {
TypeToken type = new TypeToken<LedgerResponse>() {};
ResponseHandler<LedgerResponse> responseHandler = new ResponseHandler<LedgerResponse>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Requests <code>GET /ledgers/{ledgerSeq}</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/ledgers-single.html">Ledger Details</a>
* @param ledgerSeq Ledger to fetch
* @throws IOException
*/
public LedgerResponse ledger(long ledgerSeq) throws IOException {
this.setSegments("ledgers", String.valueOf(ledgerSeq));
return this.ledger(this.buildUri());
}
/**
* Requests specific <code>uri</code> and returns {@link Page} of {@link LedgerResponse}.
* This method is helpful for getting the next set of results.
* @return {@link Page} of {@link LedgerResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<LedgerResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<LedgerResponse>>() {};
ResponseHandler<Page<LedgerResponse>> responseHandler = new ResponseHandler<Page<LedgerResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link EventListener} implementation with {@link LedgerResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<LedgerResponse> stream(final EventListener<LedgerResponse> listener) {
return SSEStream.create(httpClient,this,LedgerResponse.class,listener);
}
/**
* Build and execute request.
* @return {@link Page} of {@link LedgerResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<LedgerResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public LedgersRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public LedgersRequestBuilder limit(int number) {
super.limit(number);
return this;
}
@Override
public LedgersRequestBuilder order(Order direction) {
super.order(direction);
return this;
}
}

View file

@ -0,0 +1,81 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.OfferResponse;
import org.stellar.sdk.responses.Page;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Builds requests connected to offers.
*/
public class OffersRequestBuilder extends RequestBuilder {
public OffersRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "offers");
}
/**
* Builds request to <code>GET /accounts/{account}/offers</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/offers-for-account.html">Offers for Account</a>
* @param account Account for which to get offers
*/
public OffersRequestBuilder forAccount(KeyPair account) {
account = checkNotNull(account, "account cannot be null");
this.setSegments("accounts", account.getAccountId(), "offers");
return this;
}
/**
* Requests specific <code>uri</code> and returns {@link Page} of {@link OfferResponse}.
* This method is helpful for getting the next set of results.
* @return {@link Page} of {@link OfferResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<OfferResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<OfferResponse>>() {};
ResponseHandler<Page<OfferResponse>> responseHandler = new ResponseHandler<Page<OfferResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Build and execute request.
* @return {@link Page} of {@link OfferResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<OfferResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public OffersRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public OffersRequestBuilder limit(int number) {
super.limit(number);
return this;
}
@Override
public OffersRequestBuilder order(Order direction) {
super.order(direction);
return this;
}
}

View file

@ -0,0 +1,32 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.responses.OperationFeeStatsResponse;
import java.io.IOException;
public class OperationFeeStatsRequestBuilder extends RequestBuilder {
public OperationFeeStatsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "operation_fee_stats");
}
/**
* Requests <code>GET /operation_fee_stats</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/operation-fee-stats.html">Operation Fee Stats</a>
* @throws IOException
* @throws TooManyRequestsException
*/
public OperationFeeStatsResponse execute() throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<OperationFeeStatsResponse>() {};
ResponseHandler<OperationFeeStatsResponse> responseHandler = new ResponseHandler<OperationFeeStatsResponse>(type);
Request request = new Request.Builder().get().url(this.buildUri()).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
}

View file

@ -0,0 +1,140 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.operations.OperationResponse;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Builds requests connected to operations.
*/
public class OperationsRequestBuilder extends RequestBuilder {
public OperationsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "operations");
}
/**
* Requests specific <code>uri</code> and returns {@link OperationResponse}.
* This method is helpful for getting the links.
* @throws IOException
*/
public OperationResponse operation(HttpUrl uri) throws IOException {
TypeToken type = new TypeToken<OperationResponse>() {};
ResponseHandler<OperationResponse> responseHandler = new ResponseHandler<OperationResponse>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Requests <code>GET /operations/{operationId}</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/operations-single.html">Operation Details</a>
* @param operationId Operation to fetch
* @throws IOException
*/
public OperationResponse operation(long operationId) throws IOException {
this.setSegments("operation", String.valueOf(operationId));
return this.operation(this.buildUri());
}
/**
* Builds request to <code>GET /accounts/{account}/operations</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/operations-for-account.html">Operations for Account</a>
* @param account Account for which to get operations
*/
public OperationsRequestBuilder forAccount(KeyPair account) {
account = checkNotNull(account, "account cannot be null");
this.setSegments("accounts", account.getAccountId(), "operations");
return this;
}
/**
* Builds request to <code>GET /ledgers/{ledgerSeq}/operations</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/operations-for-ledger.html">Operations for Ledger</a>
* @param ledgerSeq Ledger for which to get operations
*/
public OperationsRequestBuilder forLedger(long ledgerSeq) {
this.setSegments("ledgers", String.valueOf(ledgerSeq), "operations");
return this;
}
/**
* Builds request to <code>GET /transactions/{transactionId}/operations</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/operations-for-transaction.html">Operations for Transaction</a>
* @param transactionId Transaction ID for which to get operations
*/
public OperationsRequestBuilder forTransaction(String transactionId) {
transactionId = checkNotNull(transactionId, "transactionId cannot be null");
this.setSegments("transactions", transactionId, "operations");
return this;
}
/**
* Requests specific <code>uri</code> and returns {@link Page} of {@link OperationResponse}.
* This method is helpful for getting the next set of results.
* @return {@link Page} of {@link OperationResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<OperationResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<OperationResponse>>() {};
ResponseHandler<Page<OperationResponse>> responseHandler = new ResponseHandler<Page<OperationResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link OperationResponse} implementation with {@link OperationResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<OperationResponse> stream(final EventListener<OperationResponse> listener) {
return SSEStream.create(httpClient,this,OperationResponse.class,listener);
}
/**
* Build and execute request.
* @return {@link Page} of {@link OperationResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<OperationResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public OperationsRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public OperationsRequestBuilder limit(int number) {
super.limit(number);
return this;
}
@Override
public OperationsRequestBuilder order(Order direction) {
super.order(direction);
return this;
}
}

View file

@ -0,0 +1,79 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeCreditAlphaNum;
import org.stellar.sdk.responses.OrderBookResponse;
import java.io.IOException;
/**
* Builds requests connected to order book.
*/
public class OrderBookRequestBuilder extends RequestBuilder {
public OrderBookRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "order_book");
}
public OrderBookRequestBuilder buyingAsset(Asset asset) {
uriBuilder.setQueryParameter("buying_asset_type", asset.getType());
if (asset instanceof AssetTypeCreditAlphaNum) {
AssetTypeCreditAlphaNum creditAlphaNumAsset = (AssetTypeCreditAlphaNum) asset;
uriBuilder.setQueryParameter("buying_asset_code", creditAlphaNumAsset.getCode());
uriBuilder.setQueryParameter("buying_asset_issuer", creditAlphaNumAsset.getIssuer().getAccountId());
}
return this;
}
public OrderBookRequestBuilder sellingAsset(Asset asset) {
uriBuilder.setQueryParameter("selling_asset_type", asset.getType());
if (asset instanceof AssetTypeCreditAlphaNum) {
AssetTypeCreditAlphaNum creditAlphaNumAsset = (AssetTypeCreditAlphaNum) asset;
uriBuilder.setQueryParameter("selling_asset_code", creditAlphaNumAsset.getCode());
uriBuilder.setQueryParameter("selling_asset_issuer", creditAlphaNumAsset.getIssuer().getAccountId());
}
return this;
}
public static OrderBookResponse execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<OrderBookResponse>() {};
ResponseHandler<OrderBookResponse> responseHandler = new ResponseHandler<OrderBookResponse>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link OrderBookResponse} implementation with {@link OrderBookResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<OrderBookResponse> stream(final EventListener<OrderBookResponse> listener) {
return SSEStream.create(httpClient,this,OrderBookResponse.class,listener);
}
public OrderBookResponse execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public RequestBuilder cursor(String cursor) {
throw new RuntimeException("Not implemented yet.");
}
@Override
public RequestBuilder order(Order direction) {
throw new RuntimeException("Not implemented yet.");
}
}

View file

@ -0,0 +1,71 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeCreditAlphaNum;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.PathResponse;
import java.io.IOException;
/**
* Builds requests connected to paths.
*/
public class PathsRequestBuilder extends RequestBuilder {
public PathsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "paths");
}
public PathsRequestBuilder destinationAccount(KeyPair account) {
uriBuilder.setQueryParameter("destination_account", account.getAccountId());
return this;
}
public PathsRequestBuilder sourceAccount(KeyPair account) {
uriBuilder.setQueryParameter("source_account", account.getAccountId());
return this;
}
public PathsRequestBuilder destinationAmount(String amount) {
uriBuilder.setQueryParameter("destination_amount", amount);
return this;
}
public PathsRequestBuilder destinationAsset(Asset asset) {
uriBuilder.setQueryParameter("destination_asset_type", asset.getType());
if (asset instanceof AssetTypeCreditAlphaNum) {
AssetTypeCreditAlphaNum creditAlphaNumAsset = (AssetTypeCreditAlphaNum) asset;
uriBuilder.setQueryParameter("destination_asset_code", creditAlphaNumAsset.getCode());
uriBuilder.setQueryParameter("destination_asset_issuer", creditAlphaNumAsset.getIssuer().getAccountId());
}
return this;
}
/**
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<PathResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<PathResponse>>() {};
ResponseHandler<Page<PathResponse>> responseHandler = new ResponseHandler<Page<PathResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<PathResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
}

View file

@ -0,0 +1,114 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.operations.OperationResponse;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Builds requests connected to payments.
*/
public class PaymentsRequestBuilder extends RequestBuilder {
public PaymentsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "payments");
}
/**
* Builds request to <code>GET /accounts/{account}/payments</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/payments-for-account.html">Payments for Account</a>
* @param account Account for which to get payments
*/
public PaymentsRequestBuilder forAccount(KeyPair account) {
account = checkNotNull(account, "account cannot be null");
this.setSegments("accounts", account.getAccountId(), "payments");
return this;
}
/**
* Builds request to <code>GET /ledgers/{ledgerSeq}/payments</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/payments-for-ledger.html">Payments for Ledger</a>
* @param ledgerSeq Ledger for which to get payments
*/
public PaymentsRequestBuilder forLedger(long ledgerSeq) {
this.setSegments("ledgers", String.valueOf(ledgerSeq), "payments");
return this;
}
/**
* Builds request to <code>GET /transactions/{transactionId}/payments</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/payments-for-transaction.html">Payments for Transaction</a>
* @param transactionId Transaction ID for which to get payments
*/
public PaymentsRequestBuilder forTransaction(String transactionId) {
transactionId = checkNotNull(transactionId, "transactionId cannot be null");
this.setSegments("transactions", transactionId, "payments");
return this;
}
/**
* Requests specific <code>uri</code> and returns {@link Page} of {@link OperationResponse}.
* This method is helpful for getting the next set of results.
* @return {@link Page} of {@link OperationResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<OperationResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<OperationResponse>>() {};
ResponseHandler<Page<OperationResponse>> responseHandler = new ResponseHandler<Page<OperationResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link EventListener} implementation with {@link OperationResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<OperationResponse> stream(final EventListener<OperationResponse> listener) {
return SSEStream.create(httpClient,this,OperationResponse.class,listener);
}
/**
* Build and execute request.
* @return {@link Page} of {@link OperationResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<OperationResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public PaymentsRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public PaymentsRequestBuilder limit(int number) {
super.limit(number);
return this;
}
@Override
public PaymentsRequestBuilder order(Order direction) {
super.order(direction);
return this;
}
}

View file

@ -0,0 +1,97 @@
package org.stellar.sdk.requests;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import java.util.ArrayList;
/**
* Abstract class for request builders.
*/
public abstract class RequestBuilder {
protected HttpUrl.Builder uriBuilder;
protected OkHttpClient httpClient;
private ArrayList<String> segments;
private boolean segmentsAdded;
RequestBuilder(OkHttpClient httpClient, HttpUrl serverURI, String defaultSegment) {
this.httpClient = httpClient;
uriBuilder = serverURI.newBuilder();
segments = new ArrayList<String>();
if (defaultSegment != null) {
this.setSegments(defaultSegment);
}
segmentsAdded = false; // Allow overwriting segments
}
protected RequestBuilder setSegments(String... segments) {
if (segmentsAdded) {
throw new RuntimeException("URL segments have been already added.");
}
segmentsAdded = true;
// Remove default segments
this.segments.clear();
for (String segment : segments) {
this.segments.add(segment);
}
return this;
}
/**
* Sets <code>cursor</code> parameter on the request.
* A cursor is a value that points to a specific location in a collection of resources.
* The cursor attribute itself is an opaque value meaning that users should not try to parse it.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/page.html">Page documentation</a>
* @param cursor
*/
public RequestBuilder cursor(String cursor) {
uriBuilder.setQueryParameter("cursor", cursor);
return this;
}
/**
* Sets <code>limit</code> parameter on the request.
* It defines maximum number of records to return.
* For range and default values check documentation of the endpoint requested.
* @param number maxium number of records to return
*/
public RequestBuilder limit(int number) {
uriBuilder.setQueryParameter("limit", String.valueOf(number));
return this;
}
/**
* Sets <code>order</code> parameter on the request.
* @param direction {@link org.stellar.sdk.requests.RequestBuilder.Order}
*/
public RequestBuilder order(Order direction) {
uriBuilder.setQueryParameter("order", direction.getValue());
return this;
}
HttpUrl buildUri() {
if (segments.size() > 0) {
for (String segment : segments) {
uriBuilder.addPathSegment(segment);
}
}
return uriBuilder.build();
}
/**
* Represents possible <code>order</code> parameter values.
*/
public enum Order {
ASC("asc"),
DESC("desc");
private final String value;
Order(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}

View file

@ -0,0 +1,54 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import org.stellar.sdk.responses.GsonSingleton;
import org.stellar.sdk.responses.TypedResponse;
import java.io.IOException;
import okhttp3.Response;
public class ResponseHandler<T> {
private TypeToken<T> type;
/**
* "Generics on a type are typically erased at runtime, except when the type is compiled with the
* generic parameter bound. In that case, the compiler inserts the generic type information into
* the compiled class. In other cases, that is not possible."
* More info: http://stackoverflow.com/a/14506181
* @param type
*/
public ResponseHandler(TypeToken<T> type) {
this.type = type;
}
public T handleResponse(final Response response) throws IOException, TooManyRequestsException {
try {
// Too Many Requests
if (response.code() == 429) {
int retryAfter = Integer.parseInt(response.header("Retry-After"));
throw new TooManyRequestsException(retryAfter);
}
String content = response.body().string();
// Other errors
if (response.code() >= 300) {
throw new ErrorResponse(response.code(), content);
}
T object = GsonSingleton.getInstance().fromJson(content, type.getType());
if (object instanceof org.stellar.sdk.responses.Response) {
((org.stellar.sdk.responses.Response) object).setHeaders(response.headers());
}
if(object instanceof TypedResponse) {
((TypedResponse) object).setType(type);
}
return object;
} finally {
response.close();
}
}
}

View file

@ -0,0 +1,195 @@
package org.stellar.sdk.requests;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.sse.RealEventSource;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import org.stellar.sdk.responses.GsonSingleton;
import javax.annotation.Nullable;
import java.io.Closeable;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SSEStream<T extends org.stellar.sdk.responses.Response> implements Closeable {
private final OkHttpClient okHttpClient;
private final RequestBuilder requestBuilder;
private final Class<T> responseClass;
private final EventListener<T> listener;
private final AtomicBoolean isStopped = new AtomicBoolean(false);
private final AtomicBoolean serverSideClosed = new AtomicBoolean(true); // make sure we start correctly
private final AtomicReference<String> lastEventId = new AtomicReference<String>(null);
private ExecutorService executorService;
private EventSource eventSource = null;
private final Lock lock = new ReentrantLock();
private SSEStream(final OkHttpClient okHttpClient, final RequestBuilder requestBuilder, final Class<T> responseClass, final EventListener<T> listener) {
// Create a new client with no read timeout
this.okHttpClient = okHttpClient.newBuilder().readTimeout(0, TimeUnit.MILLISECONDS).build();
this.requestBuilder = requestBuilder;
this.responseClass = responseClass;
this.listener = listener;
executorService = Executors.newSingleThreadExecutor();
requestBuilder.buildUri(); // call this once to add the segments
}
private void start() {
if (isStopped.get()) {
throw new IllegalStateException("Already stopped");
}
executorService.submit(new Runnable() {
@Override
public void run() {
while (!isStopped.get()) {
try {
Thread.sleep(200);
if (serverSideClosed.get()) {
// don't restart until true again
serverSideClosed.set(false);
if (!isStopped.get()) {
lock.lock();
try {
// check again if somebody called close in between
if (!isStopped.get()) {
restart();
}
} finally {
lock.unlock();
}
}
}
} catch (InterruptedException e) {
throw new IllegalStateException("interrupted", e);
}
}
}
});
}
public String lastPagingToken() {
return lastEventId.get();
}
private void restart() {
eventSource = doStreamRequest(this,okHttpClient, requestBuilder, responseClass, listener, requestBuilder.uriBuilder.build().toString(), new CloseListener() {
@Override
public void closed(EventSource source) {
serverSideClosed.set(true);
}
});
}
public void close() {
isStopped.set(true);
if (eventSource != null) {
eventSource.cancel();
} executorService.shutdownNow();
}
static <T extends org.stellar.sdk.responses.Response> SSEStream<T> create(
final OkHttpClient okHttpClient,
final RequestBuilder requestBuilder,
final Class<T> responseClass,
final EventListener<T> listener) {
SSEStream<T> stream = new SSEStream<T>(okHttpClient, requestBuilder, responseClass, listener);
stream.start();
return stream;
}
private static <T extends org.stellar.sdk.responses.Response> EventSource doStreamRequest(
final SSEStream<T> stream,
final OkHttpClient okHttpClient,
final RequestBuilder requestBuilder,
final Class<T> responseClass,
final EventListener<T> listener,
String url,
final CloseListener closeListener) {
Request.Builder builder = new Request.Builder()
.url(url)
.header("Accept", "text/event-stream");
String lastEventId = stream.lastEventId.get();
if(lastEventId != null) {
builder.header("Last-Event-ID", lastEventId);
}
Request request = builder
.build();
RealEventSource eventSource = new RealEventSource(request, new StellarEventSourceListener<T>(stream,closeListener, responseClass, requestBuilder, listener));
eventSource.connect(okHttpClient);
return eventSource;
}
private interface CloseListener {
void closed(EventSource source);
}
private static class StellarEventSourceListener<T extends org.stellar.sdk.responses.Response> extends EventSourceListener {
private SSEStream<T> stream;
private final CloseListener closeListener;
private final Class<T> responseClass;
private final RequestBuilder requestBuilder;
private final EventListener<T> listener;
StellarEventSourceListener(SSEStream<T> stream, CloseListener closeListener, Class<T> responseClass, RequestBuilder requestBuilder, EventListener<T> listener) {
this.stream = stream;
this.closeListener = closeListener;
this.responseClass = responseClass;
this.requestBuilder = requestBuilder;
this.listener = listener;
}
@Override
public void onClosed(EventSource eventSource) {
if (closeListener != null) {
closeListener.closed(eventSource);
}
}
@Override
public void onOpen(EventSource eventSource, Response response) {
}
@Override
public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) {
int code = -1;
if (response != null) {
code = response.code();
}
if (t != null) {
if (t instanceof SocketException) {
// not a failure, server disconnected
} else {
throw new IllegalStateException("Failed " + code, t);
}
} else {
throw new IllegalStateException("Failed " + code);
}
}
@Override
public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
if (data.equals("\"hello\"") || data.equals("\"byebye\"")) {
return;
}
T event = GsonSingleton.getInstance().fromJson(data, responseClass);
String pagingToken = event.getPagingToken();
requestBuilder.cursor(pagingToken);
stream.lastEventId.set(id);
listener.onEvent(event);
}
}
}

View file

@ -0,0 +1,21 @@
package org.stellar.sdk.requests;
/**
* Exception thrown when too many requests were sent to the Horizon server.
* @see <a href="https://www.stellar.org/developers/horizon/learn/rate-limiting.html" target="_blank">Rate Limiting</a>
*/
public class TooManyRequestsException extends RuntimeException {
private int retryAfter;
public TooManyRequestsException(int retryAfter) {
super("The rate limit for the requesting IP address is over its alloted limit.");
this.retryAfter = retryAfter;
}
/**
* Returns number of seconds a client should wait before sending requests again.
*/
public int getRetryAfter() {
return retryAfter;
}
}

View file

@ -0,0 +1,61 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeCreditAlphaNum;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.TradeAggregationResponse;
import java.io.IOException;
/**
* Builds requests connected to trades.
*/
public class TradeAggregationsRequestBuilder extends RequestBuilder {
public TradeAggregationsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI, Asset baseAsset, Asset counterAsset, long startTime, long endTime, long resolution, long offset) {
super(httpClient, serverURI, "trade_aggregations");
this.baseAsset(baseAsset);
this.counterAsset(counterAsset);
uriBuilder.setQueryParameter("start_time", String.valueOf(startTime));
uriBuilder.setQueryParameter("end_time", String.valueOf(endTime));
uriBuilder.setQueryParameter("resolution", String.valueOf(resolution));
uriBuilder.setQueryParameter("offset", String.valueOf(offset));
}
private void baseAsset(Asset asset) {
uriBuilder.setQueryParameter("base_asset_type", asset.getType());
if (asset instanceof AssetTypeCreditAlphaNum) {
AssetTypeCreditAlphaNum creditAlphaNumAsset = (AssetTypeCreditAlphaNum) asset;
uriBuilder.setQueryParameter("base_asset_code", creditAlphaNumAsset.getCode());
uriBuilder.setQueryParameter("base_asset_issuer", creditAlphaNumAsset.getIssuer().getAccountId());
}
}
private void counterAsset(Asset asset) {
uriBuilder.setQueryParameter("counter_asset_type", asset.getType());
if (asset instanceof AssetTypeCreditAlphaNum) {
AssetTypeCreditAlphaNum creditAlphaNumAsset = (AssetTypeCreditAlphaNum) asset;
uriBuilder.setQueryParameter("counter_asset_code", creditAlphaNumAsset.getCode());
uriBuilder.setQueryParameter("counter_asset_issuer", creditAlphaNumAsset.getIssuer().getAccountId());
}
}
public static Page<TradeAggregationResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<TradeAggregationResponse>>() {};
ResponseHandler<Page<TradeAggregationResponse>> responseHandler = new ResponseHandler<Page<TradeAggregationResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
public Page<TradeAggregationResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
}

View file

@ -0,0 +1,101 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeCreditAlphaNum;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.TradeResponse;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Builds requests connected to trades.
*/
public class TradesRequestBuilder extends RequestBuilder {
public TradesRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "trades");
}
public TradesRequestBuilder baseAsset(Asset asset) {
uriBuilder.setQueryParameter("base_asset_type", asset.getType());
if (asset instanceof AssetTypeCreditAlphaNum) {
AssetTypeCreditAlphaNum creditAlphaNumAsset = (AssetTypeCreditAlphaNum) asset;
uriBuilder.setQueryParameter("base_asset_code", creditAlphaNumAsset.getCode());
uriBuilder.setQueryParameter("base_asset_issuer", creditAlphaNumAsset.getIssuer().getAccountId());
}
return this;
}
public TradesRequestBuilder counterAsset(Asset asset) {
uriBuilder.setQueryParameter("counter_asset_type", asset.getType());
if (asset instanceof AssetTypeCreditAlphaNum) {
AssetTypeCreditAlphaNum creditAlphaNumAsset = (AssetTypeCreditAlphaNum) asset;
uriBuilder.setQueryParameter("counter_asset_code", creditAlphaNumAsset.getCode());
uriBuilder.setQueryParameter("counter_asset_issuer", creditAlphaNumAsset.getIssuer().getAccountId());
}
return this;
}
/**
* Builds request to <code>GET /accounts/{account}/trades</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/endpoints/trades-for-account.html">Trades for Account</a>
* @param account Account for which to get trades
*/
public TradesRequestBuilder forAccount(KeyPair account) {
account = checkNotNull(account, "account cannot be null");
this.setSegments("accounts", account.getAccountId(), "trades");
return this;
}
public static Page<TradeResponse> execute(OkHttpClient httpClient, HttpUrl uri)
throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<TradeResponse>>() {};
ResponseHandler<Page<TradeResponse>> responseHandler = new ResponseHandler<Page<TradeResponse>>(
type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
public Page<TradeResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
public TradesRequestBuilder offerId(String offerId) {
uriBuilder.setQueryParameter("offer_id", offerId);
return this;
}
@Override
public TradesRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public TradesRequestBuilder limit(int number) {
super.limit(number);
return this;
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link EventListener} implementation with {@link TradeResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<TradeResponse> stream(final EventListener<TradeResponse> listener) {
return SSEStream.create(httpClient,this,TradeResponse.class,listener);
}
}

View file

@ -0,0 +1,129 @@
package org.stellar.sdk.requests;
import com.google.gson.reflect.TypeToken;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.Page;
import org.stellar.sdk.responses.TransactionResponse;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Builds requests connected to transactions.
*/
public class TransactionsRequestBuilder extends RequestBuilder {
public TransactionsRequestBuilder(OkHttpClient httpClient, HttpUrl serverURI) {
super(httpClient, serverURI, "transactions");
}
/**
* Requests specific <code>uri</code> and returns {@link TransactionResponse}.
* This method is helpful for getting the links.
* @throws IOException
*/
public TransactionResponse transaction(HttpUrl uri) throws IOException {
TypeToken type = new TypeToken<TransactionResponse>() {};
ResponseHandler<TransactionResponse> responseHandler = new ResponseHandler<TransactionResponse>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Requests <code>GET /transactions/{transactionId}</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/transactions-single.html">Transaction Details</a>
* @param transactionId Transaction to fetch
* @throws IOException
*/
public TransactionResponse transaction(String transactionId) throws IOException {
this.setSegments("transactions", transactionId);
return this.transaction(this.buildUri());
}
/**
* Builds request to <code>GET /accounts/{account}/transactions</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/transactions-for-account.html">Transactions for Account</a>
* @param account Account for which to get transactions
*/
public TransactionsRequestBuilder forAccount(KeyPair account) {
account = checkNotNull(account, "account cannot be null");
this.setSegments("accounts", account.getAccountId(), "transactions");
return this;
}
/**
* Builds request to <code>GET /ledgers/{ledgerSeq}/transactions</code>
* @see <a href="https://www.stellar.org/developers/horizon/reference/transactions-for-ledger.html">Transactions for Ledger</a>
* @param ledgerSeq Ledger for which to get transactions
*/
public TransactionsRequestBuilder forLedger(long ledgerSeq) {
this.setSegments("ledgers", String.valueOf(ledgerSeq), "transactions");
return this;
}
/**
* Requests specific <code>uri</code> and returns {@link Page} of {@link TransactionResponse}.
* This method is helpful for getting the next set of results.
* @return {@link Page} of {@link TransactionResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public static Page<TransactionResponse> execute(OkHttpClient httpClient, HttpUrl uri) throws IOException, TooManyRequestsException {
TypeToken type = new TypeToken<Page<TransactionResponse>>() {};
ResponseHandler<Page<TransactionResponse>> responseHandler = new ResponseHandler<Page<TransactionResponse>>(type);
Request request = new Request.Builder().get().url(uri).build();
Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
/**
* Allows to stream SSE events from horizon.
* Certain endpoints in Horizon can be called in streaming mode using Server-Sent Events.
* This mode will keep the connection to horizon open and horizon will continue to return
* responses as ledgers close.
* @see <a href="http://www.w3.org/TR/eventsource/" target="_blank">Server-Sent Events</a>
* @see <a href="https://www.stellar.org/developers/horizon/learn/responses.html" target="_blank">Response Format documentation</a>
* @param listener {@link EventListener} implementation with {@link TransactionResponse} type
* @return EventSource object, so you can <code>close()</code> connection when not needed anymore
*/
public SSEStream<TransactionResponse> stream(final EventListener<TransactionResponse> listener) {
return SSEStream.create(httpClient,this,TransactionResponse.class,listener);
}
/**
* Build and execute request.
* @return {@link Page} of {@link TransactionResponse}
* @throws TooManyRequestsException when too many requests were sent to the Horizon server.
* @throws IOException
*/
public Page<TransactionResponse> execute() throws IOException, TooManyRequestsException {
return this.execute(this.httpClient, this.buildUri());
}
@Override
public TransactionsRequestBuilder cursor(String token) {
super.cursor(token);
return this;
}
@Override
public TransactionsRequestBuilder limit(int number) {
super.limit(number);
return this;
}
@Override
public TransactionsRequestBuilder order(Order direction) {
super.order(direction);
return this;
}
}

View file

@ -0,0 +1,351 @@
package org.stellar.sdk.responses;
import com.google.common.io.BaseEncoding;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeNative;
import org.stellar.sdk.KeyPair;
import java.util.HashMap;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents account response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/account.html" target="_blank">Account documentation</a>
* @see org.stellar.sdk.requests.AccountsRequestBuilder
* @see org.stellar.sdk.Server#accounts()
*/
public class AccountResponse extends Response implements org.stellar.sdk.TransactionBuilderAccount {
@SerializedName("account_id") /* KeyPairTypeAdapter used */
private KeyPair keypair;
@SerializedName("sequence")
private Long sequenceNumber;
@SerializedName("paging_token")
private String pagingToken;
@SerializedName("subentry_count")
private Integer subentryCount;
@SerializedName("inflation_destination")
private String inflationDestination;
@SerializedName("home_domain")
private String homeDomain;
@SerializedName("thresholds")
private Thresholds thresholds;
@SerializedName("flags")
private Flags flags;
@SerializedName("balances")
private Balance[] balances;
@SerializedName("signers")
private Signer[] signers;
@SerializedName("data")
private Data data;
@SerializedName("_links")
private Links links;
AccountResponse(KeyPair keypair) {
this.keypair = keypair;
}
public AccountResponse(KeyPair keypair, Long sequenceNumber) {
this.keypair = keypair;
this.sequenceNumber = sequenceNumber;
}
@Override
public KeyPair getKeypair() {
return keypair;
}
@Override
public Long getSequenceNumber() {
return sequenceNumber;
}
@Override
public Long getIncrementedSequenceNumber() {
return new Long(sequenceNumber + 1);
}
@Override
public void incrementSequenceNumber() {
sequenceNumber++;
}
public String getPagingToken() {
return pagingToken;
}
public Integer getSubentryCount() {
return subentryCount;
}
public String getInflationDestination() {
return inflationDestination;
}
public String getHomeDomain() {
return homeDomain;
}
public Thresholds getThresholds() {
return thresholds;
}
public Flags getFlags() {
return flags;
}
public Balance[] getBalances() {
return balances;
}
public Signer[] getSigners() {
return signers;
}
public Data getData() {
return data;
}
/**
* Represents account thresholds.
*/
public static class Thresholds {
@SerializedName("low_threshold")
private final int lowThreshold;
@SerializedName("med_threshold")
private final int medThreshold;
@SerializedName("high_threshold")
private final int highThreshold;
Thresholds(int lowThreshold, int medThreshold, int highThreshold) {
this.lowThreshold = lowThreshold;
this.medThreshold = medThreshold;
this.highThreshold = highThreshold;
}
public int getLowThreshold() {
return lowThreshold;
}
public int getMedThreshold() {
return medThreshold;
}
public int getHighThreshold() {
return highThreshold;
}
}
/**
* Represents account flags.
*/
public static class Flags {
@SerializedName("auth_required")
private final boolean authRequired;
@SerializedName("auth_revocable")
private final boolean authRevocable;
@SerializedName("auth_immutable")
private final boolean authImmutable;
Flags(boolean authRequired, boolean authRevocable, boolean authImmutable) {
this.authRequired = authRequired;
this.authRevocable = authRevocable;
this.authImmutable = authImmutable;
}
public boolean getAuthRequired() {
return authRequired;
}
public boolean getAuthRevocable() {
return authRevocable;
}
public boolean getAuthImmutable() {
return authImmutable;
}
}
/**
* Represents account balance.
*/
public static class Balance {
@SerializedName("asset_type")
private final String assetType;
@SerializedName("asset_code")
private final String assetCode;
@SerializedName("asset_issuer")
private final String assetIssuer;
@SerializedName("limit")
private final String limit;
@SerializedName("balance")
private final String balance;
@SerializedName("buying_liabilities")
private final String buyingLiabilities;
@SerializedName("selling_liabilities")
private final String sellingLiabilities;
Balance(String assetType, String assetCode, String assetIssuer, String balance, String limit, String buyingLiabilities, String sellingLiabilities) {
this.assetType = checkNotNull(assetType, "assertType cannot be null");
this.balance = checkNotNull(balance, "balance cannot be null");
this.limit = limit;
this.assetCode = assetCode;
this.assetIssuer = assetIssuer;
this.buyingLiabilities = checkNotNull(buyingLiabilities, "buyingLiabilities cannot be null");
this.sellingLiabilities = checkNotNull(sellingLiabilities, "sellingLiabilities cannot be null");
}
public Asset getAsset() {
if (assetType.equals("native")) {
return new AssetTypeNative();
} else {
return Asset.createNonNativeAsset(assetCode, getAssetIssuer());
}
}
public String getAssetType() {
return assetType;
}
public String getAssetCode() {
return assetCode;
}
public KeyPair getAssetIssuer() {
return KeyPair.fromAccountId(assetIssuer);
}
public String getBalance() {
return balance;
}
public String getBuyingLiabilities() {
return buyingLiabilities;
}
public String getSellingLiabilities() {
return sellingLiabilities;
}
public String getLimit() {
return limit;
}
}
/**
* Represents account signers.
*/
public static class Signer {
@SerializedName("key")
private final String key;
@SerializedName("type")
private final String type;
@SerializedName("weight")
private final int weight;
Signer(String key, String type, int weight) {
this.key = checkNotNull(key, "key cannot be null");
this.type = checkNotNull(type, "type cannot be null");
this.weight = checkNotNull(weight, "weight cannot be null");
}
/**
* @deprecated Use {@link Signer#getKey()}
* @return
*/
public String getAccountId() {
return key;
}
public String getKey() {
return key;
}
public int getWeight() {
return weight;
}
public String getType() {
return type;
}
}
public Links getLinks() {
return links;
}
/**
* Data connected to account.
*/
public static class Data extends HashMap<String,String> {
@Override
public int size() {
return super.size();
}
/**
* Gets base64-encoded value for a given key.
* @param key Data entry name
* @return base64-encoded value
*/
public String get(String key) {
return super.get(key);
}
/**
* Gets raw value for a given key.
* @param key Data entry name
* @return raw value
*/
public byte[] getDecoded(String key) {
BaseEncoding base64Encoding = BaseEncoding.base64();
return base64Encoding.decode(this.get(key));
}
}
/**
* Links connected to account.
*/
public static class Links {
@SerializedName("effects")
private final Link effects;
@SerializedName("offers")
private final Link offers;
@SerializedName("operations")
private final Link operations;
@SerializedName("self")
private final Link self;
@SerializedName("transactions")
private final Link transactions;
Links(Link effects, Link offers, Link operations, Link self, Link transactions) {
this.effects = effects;
this.offers = offers;
this.operations = operations;
this.self = self;
this.transactions = transactions;
}
public Link getEffects() {
return effects;
}
public Link getOffers() {
return offers;
}
public Link getOperations() {
return operations;
}
public Link getSelf() {
return self;
}
public Link getTransactions() {
return transactions;
}
}
}

View file

@ -0,0 +1,26 @@
package org.stellar.sdk.responses;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeNative;
import org.stellar.sdk.KeyPair;
import java.lang.reflect.Type;
class AssetDeserializer implements JsonDeserializer<Asset> {
@Override
public Asset deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String type = json.getAsJsonObject().get("asset_type").getAsString();
if (type.equals("native")) {
return new AssetTypeNative();
} else {
String code = json.getAsJsonObject().get("asset_code").getAsString();
String issuer = json.getAsJsonObject().get("asset_issuer").getAsString();
return Asset.createNonNativeAsset(code, KeyPair.fromAccountId(issuer));
}
}
}

View file

@ -0,0 +1,109 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
public class AssetResponse extends Response {
@SerializedName("asset_type")
private final String assetType;
@SerializedName("asset_code")
private final String assetCode;
@SerializedName("asset_issuer")
private final String assetIssuer;
@SerializedName("paging_token")
private final String pagingToken;
@SerializedName("amount")
private final String amount;
@SerializedName("num_accounts")
private final int numAccounts;
@SerializedName("flags")
private final AssetResponse.Flags flags;
@SerializedName("_links")
private final AssetResponse.Links links;
public AssetResponse(String assetType, String assetCode, String assetIssuer, String pagingToken, String amount, int numAccounts, Flags flags, Links links) {
this.assetType = assetType;
this.assetCode = assetCode;
this.assetIssuer = assetIssuer;
this.pagingToken = pagingToken;
this.amount = amount;
this.numAccounts = numAccounts;
this.flags = flags;
this.links = links;
}
public String getAssetType() {
return assetType;
}
public String getAssetCode() {
return assetCode;
}
public String getAssetIssuer() {
return assetIssuer;
}
public Asset getAsset() {
return Asset.create(this.assetType, this.assetCode, this.assetIssuer);
}
public String getPagingToken() {
return pagingToken;
}
public String getAmount() {
return amount;
}
public int getNumAccounts() {
return numAccounts;
}
public Flags getFlags() {
return flags;
}
public Links getLinks() {
return links;
}
/**
* Flags describe asset flags.
*/
public static class Flags {
@SerializedName("auth_required")
private final boolean authRequired;
@SerializedName("auth_revocable")
private final boolean authRevocable;
public Flags(boolean authRequired, boolean authRevocable) {
this.authRequired = authRequired;
this.authRevocable = authRevocable;
}
public boolean isAuthRequired() {
return authRequired;
}
public boolean isAuthRevocable() {
return authRevocable;
}
}
/**
* Links connected to asset.
*/
public static class Links {
@SerializedName("toml")
private final Link toml;
public Links(Link toml) {
this.toml = toml;
}
public Link getToml() {
return toml;
}
}
}

View file

@ -0,0 +1,83 @@
package org.stellar.sdk.responses;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.effects.*;
import java.lang.reflect.Type;
class EffectDeserializer implements JsonDeserializer<EffectResponse> {
@Override
public EffectResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Create new Gson object with adapters needed in Operation
Gson gson = new GsonBuilder()
.registerTypeAdapter(KeyPair.class, new KeyPairTypeAdapter().nullSafe())
.create();
int type = json.getAsJsonObject().get("type_i").getAsInt();
switch (type) {
// Account effects
case 0:
return gson.fromJson(json, AccountCreatedEffectResponse.class);
case 1:
return gson.fromJson(json, AccountRemovedEffectResponse.class);
case 2:
return gson.fromJson(json, AccountCreditedEffectResponse.class);
case 3:
return gson.fromJson(json, AccountDebitedEffectResponse.class);
case 4:
return gson.fromJson(json, AccountThresholdsUpdatedEffectResponse.class);
case 5:
return gson.fromJson(json, AccountHomeDomainUpdatedEffectResponse.class);
case 6:
return gson.fromJson(json, AccountFlagsUpdatedEffectResponse.class);
case 7:
return gson.fromJson(json, AccountInflationDestinationUpdatedEffectResponse.class);
// Signer effects
case 10:
return gson.fromJson(json, SignerCreatedEffectResponse.class);
case 11:
return gson.fromJson(json, SignerRemovedEffectResponse.class);
case 12:
return gson.fromJson(json, SignerUpdatedEffectResponse.class);
// Trustline effects
case 20:
return gson.fromJson(json, TrustlineCreatedEffectResponse.class);
case 21:
return gson.fromJson(json, TrustlineRemovedEffectResponse.class);
case 22:
return gson.fromJson(json, TrustlineUpdatedEffectResponse.class);
case 23:
return gson.fromJson(json, TrustlineAuthorizedEffectResponse.class);
case 24:
return gson.fromJson(json, TrustlineDeauthorizedEffectResponse.class);
// Trading effects
case 30:
return gson.fromJson(json, OfferCreatedEffectResponse.class);
case 31:
return gson.fromJson(json, OfferRemovedEffectResponse.class);
case 32:
return gson.fromJson(json, OfferUpdatedEffectResponse.class);
case 33:
return gson.fromJson(json, TradeEffectResponse.class);
// Data effects
case 40:
return gson.fromJson(json, DataCreatedEffectResponse.class);
case 41:
return gson.fromJson(json, DataRemovedEffectResponse.class);
case 42:
return gson.fromJson(json, DataUpdatedEffectResponse.class);
// Bump Sequence effects
case 43:
return gson.fromJson(json, SequenceBumpedEffectResponse.class);
default:
throw new RuntimeException("Invalid operation type");
}
}
}

View file

@ -0,0 +1,51 @@
package org.stellar.sdk.responses;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.stellar.sdk.Asset;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.effects.EffectResponse;
import org.stellar.sdk.responses.operations.OperationResponse;
public class GsonSingleton {
private static Gson instance = null;
protected GsonSingleton() {}
public static Gson getInstance() {
if (instance == null) {
TypeToken accountPageType = new TypeToken<Page<AccountResponse>>() {};
TypeToken assetPageType = new TypeToken<Page<AssetResponse>>() {};
TypeToken effectPageType = new TypeToken<Page<EffectResponse>>() {};
TypeToken ledgerPageType = new TypeToken<Page<LedgerResponse>>() {};
TypeToken offerPageType = new TypeToken<Page<OfferResponse>>() {};
TypeToken operationPageType = new TypeToken<Page<OperationResponse>>() {};
TypeToken pathPageType = new TypeToken<Page<PathResponse>>() {};
TypeToken tradePageType = new TypeToken<Page<TradeResponse>>() {};
TypeToken tradeAggregationPageType = new TypeToken<Page<TradeAggregationResponse>>() {};
TypeToken transactionPageType = new TypeToken<Page<TransactionResponse>>() {};
instance = new GsonBuilder()
.registerTypeAdapter(Asset.class, new AssetDeserializer())
.registerTypeAdapter(KeyPair.class, new KeyPairTypeAdapter().nullSafe())
.registerTypeAdapter(OperationResponse.class, new OperationDeserializer())
.registerTypeAdapter(EffectResponse.class, new EffectDeserializer())
.registerTypeAdapter(TransactionResponse.class, new TransactionDeserializer())
.registerTypeAdapter(accountPageType.getType(), new PageDeserializer<AccountResponse>(accountPageType))
.registerTypeAdapter(assetPageType.getType(), new PageDeserializer<AssetResponse>(assetPageType))
.registerTypeAdapter(effectPageType.getType(), new PageDeserializer<AccountResponse>(effectPageType))
.registerTypeAdapter(ledgerPageType.getType(), new PageDeserializer<LedgerResponse>(ledgerPageType))
.registerTypeAdapter(offerPageType.getType(), new PageDeserializer<OfferResponse>(offerPageType))
.registerTypeAdapter(operationPageType.getType(), new PageDeserializer<OperationResponse>(operationPageType))
.registerTypeAdapter(pathPageType.getType(), new PageDeserializer<PathResponse>(pathPageType))
.registerTypeAdapter(tradePageType.getType(), new PageDeserializer<TradeResponse>(tradePageType))
.registerTypeAdapter(tradeAggregationPageType.getType(), new PageDeserializer<TradeAggregationResponse>(tradeAggregationPageType))
.registerTypeAdapter(transactionPageType.getType(), new PageDeserializer<TransactionResponse>(transactionPageType))
.create();
}
return instance;
}
}

View file

@ -0,0 +1,21 @@
package org.stellar.sdk.responses;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.stellar.sdk.KeyPair;
import java.io.IOException;
class KeyPairTypeAdapter extends TypeAdapter<KeyPair> {
@Override
public void write(JsonWriter out, KeyPair value) throws IOException {
// Don't need this.
}
@Override
public KeyPair read(JsonReader in) throws IOException {
return KeyPair.fromAccountId(in.nextString());
}
}

View file

@ -0,0 +1,171 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
/**
* Represents ledger response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/ledger.html" target="_blank">Ledger documentation</a>
* @see org.stellar.sdk.requests.LedgersRequestBuilder
* @see org.stellar.sdk.Server#ledgers()
*/
public class LedgerResponse extends Response {
@SerializedName("sequence")
private final Long sequence;
@SerializedName("hash")
private final String hash;
@SerializedName("paging_token")
private final String pagingToken;
@SerializedName("prev_hash")
private final String prevHash;
@SerializedName("transaction_count")
private final Integer transactionCount;
@SerializedName("operation_count")
private final Integer operationCount;
@SerializedName("closed_at")
private final String closedAt;
@SerializedName("total_coins")
private final String totalCoins;
@SerializedName("fee_pool")
private final String feePool;
@SerializedName("base_fee")
private final Long baseFee;
@SerializedName("base_reserve")
private final String baseReserve;
@SerializedName("base_fee_in_stroops")
private final String baseFeeInStroops;
@SerializedName("base_reserve_in_stroops")
private final String baseReserveInStroops;
@SerializedName("max_tx_set_size")
private final Integer maxTxSetSize;
@SerializedName("protocol_version")
private final Integer protocolVersion;
@SerializedName("header_xdr")
private final String headerXdr;
@SerializedName("_links")
private final Links links;
LedgerResponse(Long sequence, String hash, String pagingToken, String prevHash, Integer transactionCount, Integer operationCount, String closedAt, String totalCoins, String feePool, Long baseFee, String baseReserve, String baseFeeInStroops, String baseReserveInStroops, Integer maxTxSetSize, Integer protocolVersion, String headerXdr, Links links) {
this.sequence = sequence;
this.hash = hash;
this.pagingToken = pagingToken;
this.prevHash = prevHash;
this.transactionCount = transactionCount;
this.operationCount = operationCount;
this.closedAt = closedAt;
this.totalCoins = totalCoins;
this.feePool = feePool;
this.baseFee = baseFee;
this.baseFeeInStroops = baseFeeInStroops;
this.baseReserve = baseReserve;
this.baseReserveInStroops = baseReserveInStroops;
this.maxTxSetSize = maxTxSetSize;
this.protocolVersion = protocolVersion;
this.headerXdr = headerXdr;
this.links = links;
}
public Long getSequence() {
return sequence;
}
public String getHash() {
return hash;
}
public String getPagingToken() {
return pagingToken;
}
public String getPrevHash() {
return prevHash;
}
public Integer getTransactionCount() {
return transactionCount;
}
public Integer getOperationCount() {
return operationCount;
}
public String getClosedAt() {
return closedAt;
}
public String getTotalCoins() {
return totalCoins;
}
public String getFeePool() {
return feePool;
}
public Long getBaseFee() {
return baseFee;
}
public String getBaseReserve() {
return baseReserve;
}
public String getBaseFeeInStroops() {
return baseFeeInStroops;
}
public String getBaseReserveInStroops() {
return baseReserveInStroops;
}
public Integer getMaxTxSetSize() {
return maxTxSetSize;
}
public Integer getProtocolVersion() {
return protocolVersion;
}
public String getHeaderXdr() {
return headerXdr;
}
public Links getLinks() {
return links;
}
/**
* Links connected to ledger.
*/
public static class Links {
@SerializedName("effects")
private final Link effects;
@SerializedName("operations")
private final Link operations;
@SerializedName("self")
private final Link self;
@SerializedName("transactions")
private final Link transactions;
Links(Link effects, Link operations, Link self, Link transactions) {
this.effects = effects;
this.operations = operations;
this.self = self;
this.transactions = transactions;
}
public Link getEffects() {
return effects;
}
public Link getOperations() {
return operations;
}
public Link getSelf() {
return self;
}
public Link getTransactions() {
return transactions;
}
}
}

View file

@ -0,0 +1,39 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import java.net.URI;
import java.net.URISyntaxException;
/**
* Represents links in responses.
*/
public class Link {
@SerializedName("href")
private final String href;
@SerializedName("templated")
private final boolean templated;
Link(String href, boolean templated) {
this.href = href;
this.templated = templated;
}
public String getHref() {
// TODO templated
return href;
}
public URI getUri() {
// TODO templated
try {
return new URI(href);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
public boolean isTemplated() {
return templated;
}
}

View file

@ -0,0 +1,97 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
import org.stellar.sdk.KeyPair;
/**
* Represents offer response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/offer.html" target="_blank">Offer documentation</a>
* @see org.stellar.sdk.requests.OffersRequestBuilder
* @see org.stellar.sdk.Server#offers()
*/
public class OfferResponse extends Response {
@SerializedName("id")
private final Long id;
@SerializedName("paging_token")
private final String pagingToken;
@SerializedName("seller")
private final KeyPair seller;
@SerializedName("selling")
private final Asset selling;
@SerializedName("buying")
private final Asset buying;
@SerializedName("amount")
private final String amount;
@SerializedName("price")
private final String price;
@SerializedName("_links")
private final Links links;
OfferResponse(Long id, String pagingToken, KeyPair seller, Asset selling, Asset buying, String amount, String price, Links links) {
this.id = id;
this.pagingToken = pagingToken;
this.seller = seller;
this.selling = selling;
this.buying = buying;
this.amount = amount;
this.price = price;
this.links = links;
}
public Long getId() {
return id;
}
public String getPagingToken() {
return pagingToken;
}
public KeyPair getSeller() {
return seller;
}
public Asset getSelling() {
return selling;
}
public Asset getBuying() {
return buying;
}
public String getAmount() {
return amount;
}
public String getPrice() {
return price;
}
public Links getLinks() {
return links;
}
/**
* Links connected to ledger.
*/
public static class Links {
@SerializedName("self")
private final Link self;
@SerializedName("offer_maker")
private final Link offerMaker;
public Links(Link self, Link offerMaker) {
this.self = self;
this.offerMaker = offerMaker;
}
public Link getSelf() {
return self;
}
public Link getOfferMaker() {
return offerMaker;
}
}
}

View file

@ -0,0 +1,53 @@
package org.stellar.sdk.responses;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.operations.*;
import java.lang.reflect.Type;
class OperationDeserializer implements JsonDeserializer<OperationResponse> {
@Override
public OperationResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Create new Gson object with adapters needed in Operation
Gson gson = new GsonBuilder()
.registerTypeAdapter(KeyPair.class, new KeyPairTypeAdapter().nullSafe())
.create();
int type = json.getAsJsonObject().get("type_i").getAsInt();
switch (type) {
case 0:
return gson.fromJson(json, CreateAccountOperationResponse.class);
case 1:
return gson.fromJson(json, PaymentOperationResponse.class);
case 2:
return gson.fromJson(json, PathPaymentOperationResponse.class);
case 3:
return gson.fromJson(json, ManageOfferOperationResponse.class);
case 4:
return gson.fromJson(json, CreatePassiveOfferOperationResponse.class);
case 5:
return gson.fromJson(json, SetOptionsOperationResponse.class);
case 6:
return gson.fromJson(json, ChangeTrustOperationResponse.class);
case 7:
return gson.fromJson(json, AllowTrustOperationResponse.class);
case 8:
return gson.fromJson(json, AccountMergeOperationResponse.class);
case 9:
return gson.fromJson(json, InflationOperationResponse.class);
case 10:
return gson.fromJson(json, ManageDataOperationResponse.class);
case 11:
return gson.fromJson(json, BumpSequenceOperationResponse.class);
default:
throw new RuntimeException("Invalid operation type");
}
}
}

View file

@ -0,0 +1,37 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
public class OperationFeeStatsResponse extends Response {
@SerializedName("min_accepted_fee")
private final Long min;
@SerializedName("mode_accepted_fee")
private final Long mode;
@SerializedName("last_ledger_base_fee")
private final Long lastLedgerBaseFee;
@SerializedName("last_ledger")
private final Long lastLedger;
public OperationFeeStatsResponse(Long min, Long mode, Long lastLedgerBaseFee, Long lastLedger) {
this.min = min;
this.mode = mode;
this.lastLedgerBaseFee = lastLedgerBaseFee;
this.lastLedger = lastLedger;
}
public Long getMin() {
return min;
}
public Long getMode() {
return mode;
}
public Long getLastLedgerBaseFee() {
return lastLedgerBaseFee;
}
public Long getLastLedger() {
return lastLedger;
}
}

View file

@ -0,0 +1,77 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
import org.stellar.sdk.Price;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents order book response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/orderbook.html" target="_blank">Order book documentation</a>
* @see org.stellar.sdk.requests.OrderBookRequestBuilder
* @see org.stellar.sdk.Server#orderBook()
*/
public class OrderBookResponse extends Response {
@SerializedName("base")
private final Asset base;
@SerializedName("counter")
private final Asset counter;
@SerializedName("asks")
private final Row[] asks;
@SerializedName("bids")
private final Row[] bids;
public OrderBookResponse(Asset base, Asset counter, Row[] asks, Row[] bids) {
this.base = base;
this.counter = counter;
this.asks = asks;
this.bids = bids;
}
public Asset getBase() {
return base;
}
public Asset getCounter() {
return counter;
}
public Row[] getAsks() {
return asks;
}
public Row[] getBids() {
return bids;
}
/**
* Represents order book row.
*/
public static class Row {
@SerializedName("amount")
private final String amount;
@SerializedName("price")
private final String price;
@SerializedName("price_r")
private final Price priceR;
Row(String amount, String price, Price priceR) {
this.amount = checkNotNull(amount, "amount cannot be null");
this.price = checkNotNull(price, "price cannot be null");
this.priceR = checkNotNull(priceR, "priceR cannot be null");
}
public String getAmount() {
return amount;
}
public String getPrice() {
return price;
}
public Price getPriceR() {
return priceR;
}
}
}

View file

@ -0,0 +1,93 @@
package org.stellar.sdk.responses;
import static java.util.Objects.requireNonNull;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import org.stellar.sdk.requests.ResponseHandler;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import okhttp3.OkHttpClient;
import okhttp3.Request;
/**
* Represents page of objects.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/page.html" target="_blank">Page documentation</a>
*/
public class Page<T> extends Response implements TypedResponse<Page<T>> {
@SerializedName("records")
private ArrayList<T> records;
@SerializedName("links")
private Links links;
private TypeToken<Page<T>> type;
Page() {}
public ArrayList<T> getRecords() {
return records;
}
public Links getLinks() {
return links;
}
/**
* @return The next page of results or null when there is no link for the next page of results
* @throws URISyntaxException
* @throws IOException
*/
public Page<T> getNextPage(OkHttpClient httpClient) throws URISyntaxException, IOException {
if (this.getLinks().getNext() == null) {
return null;
}
TypeToken<Page<T>> type = requireNonNull(this.type, "type cannot be null, is it being correctly set after the creation of this " + getClass().getSimpleName() + "?");
ResponseHandler<Page<T>> responseHandler = new ResponseHandler<Page<T>>(type);
String url = this.getLinks().getNext().getHref();
Request request = new Request.Builder().get().url(url).build();
okhttp3.Response response = httpClient.newCall(request).execute();
return responseHandler.handleResponse(response);
}
@Override
public void setType(TypeToken<Page<T>> type) {
this.type = type;
}
/**
* Links connected to page response.
*/
public static class Links {
@SerializedName("next")
private final Link next;
@SerializedName("prev")
private final Link prev;
@SerializedName("self")
private final Link self;
Links(Link next, Link prev, Link self) {
this.next = next;
this.prev = prev;
this.self = self;
}
public Link getNext() {
return next;
}
public Link getPrev() {
return prev;
}
public Link getSelf() {
return self;
}
}
}

View file

@ -0,0 +1,51 @@
package org.stellar.sdk.responses;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import org.stellar.sdk.Asset;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.effects.EffectResponse;
import org.stellar.sdk.responses.operations.OperationResponse;
import java.lang.reflect.Type;
class PageDeserializer<E> implements JsonDeserializer<Page<E>> {
private TypeToken<Page<E>> pageType;
/**
* "Generics on a type are typically erased at runtime, except when the type is compiled with the
* generic parameter bound. In that case, the compiler inserts the generic type information into
* the compiled class. In other cases, that is not possible."
* More info: http://stackoverflow.com/a/14506181
* @param pageType
*/
public PageDeserializer(TypeToken<Page<E>> pageType) {
this.pageType = pageType;
}
@Override
public Page<E> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Flatten the object so it has two fields `records` and `links`
JsonObject newJson = new JsonObject();
newJson.add("records", json.getAsJsonObject().get("_embedded").getAsJsonObject().get("records"));
newJson.add("links", json.getAsJsonObject().get("_links"));
// Create new Gson object with adapters needed in Page
Gson gson = new GsonBuilder()
.registerTypeAdapter(Asset.class, new AssetDeserializer())
.registerTypeAdapter(KeyPair.class, new KeyPairTypeAdapter().nullSafe())
.registerTypeAdapter(OperationResponse.class, new OperationDeserializer())
.registerTypeAdapter(EffectResponse.class, new EffectDeserializer())
.registerTypeAdapter(TransactionResponse.class, new TransactionDeserializer())
.create();
return gson.fromJson(newJson, pageType.getType());
}
}

View file

@ -0,0 +1,104 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeNative;
import org.stellar.sdk.KeyPair;
import java.util.ArrayList;
/**
* Represents path response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/path.html" target="_blank">Path documentation</a>
* @see org.stellar.sdk.requests.PathsRequestBuilder
* @see org.stellar.sdk.Server#paths()
*/
public class PathResponse extends Response {
@SerializedName("destination_amount")
private final String destinationAmount;
@SerializedName("destination_asset_type")
private final String destinationAssetType;
@SerializedName("destination_asset_code")
private final String destinationAssetCode;
@SerializedName("destination_asset_issuer")
private final String destinationAssetIssuer;
@SerializedName("source_amount")
private final String sourceAmount;
@SerializedName("source_asset_type")
private final String sourceAssetType;
@SerializedName("source_asset_code")
private final String sourceAssetCode;
@SerializedName("source_asset_issuer")
private final String sourceAssetIssuer;
@SerializedName("path")
private final ArrayList<Asset> path;
@SerializedName("_links")
private final Links links;
PathResponse(String destinationAmount, String destinationAssetType, String destinationAssetCode, String destinationAssetIssuer, String sourceAmount, String sourceAssetType, String sourceAssetCode, String sourceAssetIssuer, ArrayList<Asset> path, Links links) {
this.destinationAmount = destinationAmount;
this.destinationAssetType = destinationAssetType;
this.destinationAssetCode = destinationAssetCode;
this.destinationAssetIssuer = destinationAssetIssuer;
this.sourceAmount = sourceAmount;
this.sourceAssetType = sourceAssetType;
this.sourceAssetCode = sourceAssetCode;
this.sourceAssetIssuer = sourceAssetIssuer;
this.path = path;
this.links = links;
}
public String getDestinationAmount() {
return destinationAmount;
}
public String getSourceAmount() {
return sourceAmount;
}
public ArrayList<Asset> getPath() {
return path;
}
public Asset getDestinationAsset() {
if (destinationAssetType.equals("native")) {
return new AssetTypeNative();
} else {
KeyPair issuer = KeyPair.fromAccountId(destinationAssetIssuer);
return Asset.createNonNativeAsset(destinationAssetCode, issuer);
}
}
public Asset getSourceAsset() {
if (sourceAssetType.equals("native")) {
return new AssetTypeNative();
} else {
KeyPair issuer = KeyPair.fromAccountId(sourceAssetIssuer);
return Asset.createNonNativeAsset(sourceAssetCode, issuer);
}
}
public Links getLinks() {
return links;
}
/**
* Links connected to path.
*/
public static class Links {
@SerializedName("self")
private final Link self;
Links(Link self) {
this.self = self;
}
public Link getSelf() {
return self;
}
}
}

View file

@ -0,0 +1,53 @@
package org.stellar.sdk.responses;
import okhttp3.Headers;
public abstract class Response {
protected int rateLimitLimit;
protected int rateLimitRemaining;
protected int rateLimitReset;
public void setHeaders(Headers headers) {
if (headers.get("X-Ratelimit-Limit") != null) {
this.rateLimitLimit = Integer.parseInt(headers.get("X-Ratelimit-Limit"));
}
if (headers.get("X-Ratelimit-Remaining") != null) {
this.rateLimitRemaining = Integer.parseInt(headers.get("X-Ratelimit-Remaining"));
}
if (headers.get("X-Ratelimit-Reset") != null) {
this.rateLimitReset = Integer.parseInt(headers.get("X-Ratelimit-Reset"));
}
}
/**
* Returns X-RateLimit-Limit header from the response.
* This number represents the he maximum number of requests that the current client can
* make in one hour.
* @see <a href="https://www.stellar.org/developers/horizon/learn/rate-limiting.html" target="_blank">Rate Limiting</a>
*/
public int getRateLimitLimit() {
return rateLimitLimit;
}
public String getPagingToken() {
throw new UnsupportedOperationException("this response does not have a paging token");
}
/**
* Returns X-RateLimit-Remaining header from the response.
* The number of remaining requests for the current window.
* @see <a href="https://www.stellar.org/developers/horizon/learn/rate-limiting.html" target="_blank">Rate Limiting</a>
*/
public int getRateLimitRemaining() {
return rateLimitRemaining;
}
/**
* Returns X-RateLimit-Reset header from the response. Seconds until a new window starts.
* @see <a href="https://www.stellar.org/developers/horizon/learn/rate-limiting.html" target="_blank">Rate Limiting</a>
*/
public int getRateLimitReset() {
return rateLimitReset;
}
}

View file

@ -0,0 +1,62 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
/**
* Represents root endpoint response.
* @see org.stellar.sdk.Server#root()
*/
public class RootResponse extends Response {
@SerializedName("horizon_version")
private final String horizonVersion;
@SerializedName("core_version")
private final String stellarCoreVersion;
@SerializedName("history_latest_ledger")
private final int historyLatestLedger;
@SerializedName("history_elder_ledger")
private final int historyElderLedger;
@SerializedName("core_latest_ledger")
private final int coreLatestLedger;
@SerializedName("network_passphrase")
private final String networkPassphrase;
@SerializedName("protocol_version")
private final int protocolVersion;
public String getHorizonVersion() {
return horizonVersion;
}
public String getStellarCoreVersion() {
return stellarCoreVersion;
}
public int getHistoryLatestLedger() {
return historyLatestLedger;
}
public int getHistoryElderLedger() {
return historyElderLedger;
}
public int getCoreLatestLedger() {
return coreLatestLedger;
}
public String getNetworkPassphrase() {
return networkPassphrase;
}
public int getProtocolVersion() {
return protocolVersion;
}
public RootResponse(String horizonVersion, String stellarCoreVersion, int historyLatestLedger, int historyElderLedger, int coreLatestLedger, String networkPassphrase, int protocolVersion) {
this.horizonVersion = horizonVersion;
this.stellarCoreVersion = stellarCoreVersion;
this.historyLatestLedger = historyLatestLedger;
this.historyElderLedger = historyElderLedger;
this.coreLatestLedger = coreLatestLedger;
this.networkPassphrase = networkPassphrase;
this.protocolVersion = protocolVersion;
}
}

View file

@ -0,0 +1,182 @@
package org.stellar.sdk.responses;
import com.google.common.io.BaseEncoding;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Server;
import org.stellar.sdk.xdr.OperationType;
import org.stellar.sdk.xdr.TransactionResult;
import org.stellar.sdk.xdr.XdrDataInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
/**
* Represents server response after submitting transaction.
* @see Server#submitTransaction(org.stellar.sdk.Transaction)
*/
public class SubmitTransactionResponse extends Response {
@SerializedName("hash")
private final String hash;
@SerializedName("ledger")
private final Long ledger;
@SerializedName("envelope_xdr")
private final String envelopeXdr;
@SerializedName("result_xdr")
private final String resultXdr;
@SerializedName("extras")
private final Extras extras;
SubmitTransactionResponse(Extras extras, Long ledger, String hash, String envelopeXdr, String resultXdr) {
this.extras = extras;
this.ledger = ledger;
this.hash = hash;
this.envelopeXdr = envelopeXdr;
this.resultXdr = resultXdr;
}
public boolean isSuccess() {
return ledger != null;
}
public String getHash() {
return hash;
}
public Long getLedger() {
return ledger;
}
public String getEnvelopeXdr() {
if (this.isSuccess()) {
return this.envelopeXdr;
} else {
if (this.getExtras() != null) {
return this.getExtras().getEnvelopeXdr();
}
return null;
}
}
public String getResultXdr() {
if (this.isSuccess()) {
return this.resultXdr;
} else {
if (this.getExtras() != null) {
return this.getExtras().getResultXdr();
}
return null;
}
}
/**
* Helper method that returns Offer ID for ManageOffer from TransactionResult Xdr.
* This is helpful when you need ID of an offer to update it later.
* @param position Position of ManageOffer operation. If ManageOffer is second operation in this transaction this should be equal <code>1</code>.
* @return Offer ID or <code>null</code> when operation at <code>position</code> is not a ManageOffer operation or error has occurred.
*/
public Long getOfferIdFromResult(int position) {
if (!this.isSuccess()) {
return null;
}
BaseEncoding base64Encoding = BaseEncoding.base64();
byte[] bytes = base64Encoding.decode(this.getResultXdr());
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
XdrDataInputStream xdrInputStream = new XdrDataInputStream(inputStream);
TransactionResult result;
try {
result = TransactionResult.decode(xdrInputStream);
} catch (IOException e) {
return null;
}
if (result.getResult().getResults()[position] == null) {
return null;
}
if (result.getResult().getResults()[position].getTr().getDiscriminant() != OperationType.MANAGE_OFFER) {
return null;
}
if (result.getResult().getResults()[0].getTr().getManageOfferResult().getSuccess().getOffer().getOffer() == null) {
return null;
}
return result.getResult().getResults()[0].getTr().getManageOfferResult().getSuccess().getOffer().getOffer().getOfferID().getUint64();
}
/**
* Additional information returned by a server. This will be <code>null</code> if transaction succeeded.
*/
public Extras getExtras() {
return extras;
}
/**
* Additional information returned by a server.
*/
public static class Extras {
@SerializedName("envelope_xdr")
private final String envelopeXdr;
@SerializedName("result_xdr")
private final String resultXdr;
@SerializedName("result_codes")
private final ResultCodes resultCodes;
Extras(String envelopeXdr, String resultXdr, ResultCodes resultCodes) {
this.envelopeXdr = envelopeXdr;
this.resultXdr = resultXdr;
this.resultCodes = resultCodes;
}
/**
* Returns XDR TransactionEnvelope base64-encoded string.
* Use <a href="http://stellar.github.io/xdr-viewer/">xdr-viewer</a> to debug.
*/
public String getEnvelopeXdr() {
return envelopeXdr;
}
/**
* Returns XDR TransactionResult base64-encoded string
* Use <a href="http://stellar.github.io/xdr-viewer/">xdr-viewer</a> to debug.
*/
public String getResultXdr() {
return resultXdr;
}
/**
* Returns ResultCodes object that contains result codes for transaction.
*/
public ResultCodes getResultCodes() {
return resultCodes;
}
/**
* Contains result codes for this transaction.
* @see <a href="https://github.com/stellar/horizon/blob/master/src/github.com/stellar/horizon/codes/main.go" target="_blank">Possible values</a>
*/
public static class ResultCodes {
@SerializedName("transaction")
private final String transactionResultCode;
@SerializedName("operations")
private final ArrayList<String> operationsResultCodes;
public ResultCodes(String transactionResultCode, ArrayList<String> operationsResultCodes) {
this.transactionResultCode = transactionResultCode;
this.operationsResultCodes = operationsResultCodes;
}
public String getTransactionResultCode() {
return transactionResultCode;
}
public ArrayList<String> getOperationsResultCodes() {
return operationsResultCodes;
}
}
}
}

View file

@ -0,0 +1,8 @@
package org.stellar.sdk.responses;
public class SubmitTransactionTimeoutResponseException extends RuntimeException {
@Override
public String getMessage() {
return "Timeout. Please resubmit your transaction to receive submission status. More info: https://www.stellar.org/developers/horizon/reference/errors/timeout.html";
}
}

View file

@ -0,0 +1,24 @@
package org.stellar.sdk.responses;
public class SubmitTransactionUnknownResponseException extends RuntimeException {
private int code;
private String body;
public SubmitTransactionUnknownResponseException(int code, String body) {
this.code = code;
this.body = body;
}
@Override
public String getMessage() {
return "Unknown response from Horizon";
}
public int getCode() {
return code;
}
public String getBody() {
return body;
}
}

View file

@ -0,0 +1,78 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
public class TradeAggregationResponse extends Response {
@SerializedName("timestamp")
private final long timestamp;
@SerializedName("trade_count")
private final int tradeCount;
@SerializedName("base_volume")
private final String baseVolume;
@SerializedName("counter_volume")
private final String counterVolume;
@SerializedName("avg")
private final String avg;
@SerializedName("high")
private final String high;
@SerializedName("low")
private final String low;
@SerializedName("open")
private final String open;
@SerializedName("close")
private final String close;
public TradeAggregationResponse(long timestamp, int tradeCount, String baseVolume, String counterVolume, String avg, String high, String low, String open, String close) {
this.timestamp = timestamp;
this.tradeCount = tradeCount;
this.baseVolume = baseVolume;
this.counterVolume = counterVolume;
this.avg = avg;
this.high = high;
this.low = low;
this.open = open;
this.close = close;
}
public long getTimestamp() {
return timestamp;
}
public Date getDate() {
return new Date(Long.valueOf(this.timestamp));
}
public int getTradeCount() {
return tradeCount;
}
public String getBaseVolume() {
return baseVolume;
}
public String getCounterVolume() {
return counterVolume;
}
public String getAvg() {
return avg;
}
public String getHigh() {
return high;
}
public String getLow() {
return low;
}
public String getOpen() {
return open;
}
public String getClose() {
return close;
}
}

View file

@ -0,0 +1,195 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.Price;
;
/**
* Represents trades response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/endpoints/trades.html" target="_blank">Trades documentation</a>
* @see org.stellar.sdk.requests.TradesRequestBuilder
* @see org.stellar.sdk.Server#trades()
*/
public class TradeResponse extends Response {
@SerializedName("id")
private final String id;
@SerializedName("paging_token")
private final String pagingToken;
@SerializedName("ledger_close_time")
private final String ledgerCloseTime;
@SerializedName("offer_id")
private final String offerId;
@SerializedName("base_is_seller")
protected final boolean baseIsSeller;
@SerializedName("base_account")
protected final KeyPair baseAccount;
@SerializedName("base_offer_id")
private final String baseOfferId;
@SerializedName("base_amount")
protected final String baseAmount;
@SerializedName("base_asset_type")
protected final String baseAssetType;
@SerializedName("base_asset_code")
protected final String baseAssetCode;
@SerializedName("base_asset_issuer")
protected final String baseAssetIssuer;
@SerializedName("counter_account")
protected final KeyPair counterAccount;
@SerializedName("counter_offer_id")
private final String counterOfferId;
@SerializedName("counter_amount")
protected final String counterAmount;
@SerializedName("counter_asset_type")
protected final String counterAssetType;
@SerializedName("counter_asset_code")
protected final String counterAssetCode;
@SerializedName("counter_asset_issuer")
protected final String counterAssetIssuer;
@SerializedName("price")
protected final Price price;
@SerializedName("_links")
private TradeResponse.Links links;
public TradeResponse(String id, String pagingToken, String ledgerCloseTime, String offerId, boolean baseIsSeller, KeyPair baseAccount, String baseOfferId, String baseAmount, String baseAssetType, String baseAssetCode, String baseAssetIssuer, KeyPair counterAccount, String counterOfferId, String counterAmount, String counterAssetType, String counterAssetCode, String counterAssetIssuer, Price price) {
this.id = id;
this.pagingToken = pagingToken;
this.ledgerCloseTime = ledgerCloseTime;
this.offerId = offerId;
this.baseIsSeller = baseIsSeller;
this.baseAccount = baseAccount;
this.baseOfferId = baseOfferId;
this.baseAmount = baseAmount;
this.baseAssetType = baseAssetType;
this.baseAssetCode = baseAssetCode;
this.baseAssetIssuer = baseAssetIssuer;
this.counterAccount = counterAccount;
this.counterOfferId = counterOfferId;
this.counterAmount = counterAmount;
this.counterAssetType = counterAssetType;
this.counterAssetCode = counterAssetCode;
this.counterAssetIssuer = counterAssetIssuer;
this.price = price;
}
public String getId() {
return id;
}
public String getPagingToken() {
return pagingToken;
}
public String getLedgerCloseTime() {
return ledgerCloseTime;
}
public String getOfferId() {
return offerId;
}
public boolean isBaseSeller() {
return baseIsSeller;
}
public String getBaseOfferId() {
return baseOfferId;
}
public KeyPair getBaseAccount() {
return baseAccount;
}
public String getBaseAmount() {
return baseAmount;
}
public Asset getBaseAsset() {
return Asset.create(this.baseAssetType, this.baseAssetCode, this.baseAssetIssuer);
}
public String getBaseAssetType() {
return baseAssetType;
}
public String getBaseAssetCode() {
return baseAssetCode;
}
public String getBaseAssetIssuer() {
return baseAssetIssuer;
}
public KeyPair getCounterAccount() {
return counterAccount;
}
public String getCounterOfferId() {
return counterOfferId;
}
public Asset getCounterAsset() {
return Asset.create(this.counterAssetType, this.counterAssetCode, this.counterAssetIssuer);
}
public String getCounterAmount() {
return counterAmount;
}
public String getCounterAssetType() {
return counterAssetType;
}
public String getCounterAssetCode() {
return counterAssetCode;
}
public String getCounterAssetIssuer() {
return counterAssetIssuer;
}
public Price getPrice() {
return price;
}
public Links getLinks() {
return links;
}
/**
* Links connected to a trade.
*/
public static class Links {
@SerializedName("base")
private final Link base;
@SerializedName("counter")
private final Link counter;
@SerializedName("operation")
private final Link operation;
public Links(Link base, Link counter, Link operation) {
this.base = base;
this.counter = counter;
this.operation = operation;
}
public Link getBase() {
return base;
}
public Link getCounter() {
return counter;
}
public Link getOperation() {
return operation;
}
}
}

View file

@ -0,0 +1,60 @@
package org.stellar.sdk.responses;
import com.google.common.io.BaseEncoding;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.Memo;
import java.lang.reflect.Type;
public class TransactionDeserializer implements JsonDeserializer<TransactionResponse> {
@Override
public TransactionResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
// Create new Gson object with adapters needed in Transaction
Gson gson = new GsonBuilder()
.registerTypeAdapter(KeyPair.class, new KeyPairTypeAdapter().nullSafe())
.create();
TransactionResponse transaction = gson.fromJson(json, TransactionResponse.class);
String memoType = json.getAsJsonObject().get("memo_type").getAsString();
Memo memo;
if (memoType.equals("none")) {
memo = Memo.none();
} else {
// Because of the way "encoding/json" works on structs in Go, if transaction
// has an empty `memo_text` value, the `memo` field won't be present in a JSON
// representation of a transaction. That's why we need to handle a special case
// here.
if (memoType.equals("text")) {
JsonElement memoField = json.getAsJsonObject().get("memo");
if (memoField != null) {
memo = Memo.text(memoField.getAsString());
} else {
memo = Memo.text("");
}
} else {
String memoValue = json.getAsJsonObject().get("memo").getAsString();
BaseEncoding base64Encoding = BaseEncoding.base64();
if (memoType.equals("id")) {
memo = Memo.id(Long.parseUnsignedLong(memoValue));
} else if (memoType.equals("hash")) {
memo = Memo.hash(base64Encoding.decode(memoValue));
} else if (memoType.equals("return")) {
memo = Memo.returnHash(base64Encoding.decode(memoValue));
} else {
throw new JsonParseException("Unknown memo type.");
}
}
}
transaction.setMemo(memo);
return transaction;
}
}

View file

@ -0,0 +1,179 @@
package org.stellar.sdk.responses;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.Memo;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Represents transaction response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/transaction.html" target="_blank">Transaction documentation</a>
* @see org.stellar.sdk.requests.TransactionsRequestBuilder
* @see org.stellar.sdk.Server#transactions()
*/
public class TransactionResponse extends Response {
@SerializedName("hash")
private final String hash;
@SerializedName("ledger")
private final Long ledger;
@SerializedName("created_at")
private final String createdAt;
@SerializedName("source_account")
private final KeyPair sourceAccount;
@SerializedName("paging_token")
private final String pagingToken;
@SerializedName("source_account_sequence")
private final Long sourceAccountSequence;
@SerializedName("fee_paid")
private final Long feePaid;
@SerializedName("operation_count")
private final Integer operationCount;
@SerializedName("envelope_xdr")
private final String envelopeXdr;
@SerializedName("result_xdr")
private final String resultXdr;
@SerializedName("result_meta_xdr")
private final String resultMetaXdr;
@SerializedName("_links")
private final Links links;
// GSON won't serialize `transient` variables automatically. We need this behaviour
// because Memo is an abstract class and GSON tries to instantiate it.
private transient Memo memo;
TransactionResponse(String hash, Long ledger, String createdAt, KeyPair sourceAccount, String pagingToken, Long sourceAccountSequence, Long feePaid, Integer operationCount, String envelopeXdr, String resultXdr, String resultMetaXdr, Memo memo, Links links) {
this.hash = hash;
this.ledger = ledger;
this.createdAt = createdAt;
this.sourceAccount = sourceAccount;
this.pagingToken = pagingToken;
this.sourceAccountSequence = sourceAccountSequence;
this.feePaid = feePaid;
this.operationCount = operationCount;
this.envelopeXdr = envelopeXdr;
this.resultXdr = resultXdr;
this.resultMetaXdr = resultMetaXdr;
this.memo = memo;
this.links = links;
}
public String getHash() {
return hash;
}
public Long getLedger() {
return ledger;
}
public String getCreatedAt() {
return createdAt;
}
public KeyPair getSourceAccount() {
return sourceAccount;
}
public String getPagingToken() {
return pagingToken;
}
public Long getSourceAccountSequence() {
return sourceAccountSequence;
}
public Long getFeePaid() {
return feePaid;
}
public Integer getOperationCount() {
return operationCount;
}
public String getEnvelopeXdr() {
return envelopeXdr;
}
public String getResultXdr() {
return resultXdr;
}
public String getResultMetaXdr() {
return resultMetaXdr;
}
public Memo getMemo() {
return memo;
}
public void setMemo(Memo memo) {
memo = checkNotNull(memo, "memo cannot be null");
if (this.memo != null) {
throw new RuntimeException("Memo has been already set.");
}
this.memo = memo;
}
public Links getLinks() {
return links;
}
/**
* Links connected to transaction.
*/
public static class Links {
@SerializedName("account")
private final Link account;
@SerializedName("effects")
private final Link effects;
@SerializedName("ledger")
private final Link ledger;
@SerializedName("operations")
private final Link operations;
@SerializedName("precedes")
private final Link precedes;
@SerializedName("self")
private final Link self;
@SerializedName("succeeds")
private final Link succeeds;
Links(Link account, Link effects, Link ledger, Link operations, Link self, Link precedes, Link succeeds) {
this.account = account;
this.effects = effects;
this.ledger = ledger;
this.operations = operations;
this.self = self;
this.precedes = precedes;
this.succeeds = succeeds;
}
public Link getAccount() {
return account;
}
public Link getEffects() {
return effects;
}
public Link getLedger() {
return ledger;
}
public Link getOperations() {
return operations;
}
public Link getPrecedes() {
return precedes;
}
public Link getSelf() {
return self;
}
public Link getSucceeds() {
return succeeds;
}
}
}

View file

@ -0,0 +1,14 @@
package org.stellar.sdk.responses;
import com.google.gson.reflect.TypeToken;
/**
* Indicates a generic container that requires type information to be provided after initialisation.
*
* @param <T> the type of the objects in this response container.
*/
public interface TypedResponse<T> {
void setType(TypeToken<T> type);
}

View file

@ -0,0 +1,22 @@
package org.stellar.sdk.responses.effects;
import com.google.gson.annotations.SerializedName;
/**
* Represents account_created effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountCreatedEffectResponse extends EffectResponse {
@SerializedName("starting_balance")
protected final String startingBalance;
AccountCreatedEffectResponse(String startingBalance) {
this.startingBalance = startingBalance;
}
public String getStartingBalance() {
return startingBalance;
}
}

View file

@ -0,0 +1,45 @@
package org.stellar.sdk.responses.effects;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeNative;
import org.stellar.sdk.KeyPair;
/**
* Represents account_credited effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountCreditedEffectResponse extends EffectResponse {
@SerializedName("amount")
protected final String amount;
@SerializedName("asset_type")
protected final String assetType;
@SerializedName("asset_code")
protected final String assetCode;
@SerializedName("asset_issuer")
protected final String assetIssuer;
AccountCreditedEffectResponse(String amount, String assetType, String assetCode, String assetIssuer) {
this.amount = amount;
this.assetType = assetType;
this.assetCode = assetCode;
this.assetIssuer = assetIssuer;
}
public String getAmount() {
return amount;
}
public Asset getAsset() {
if (assetType.equals("native")) {
return new AssetTypeNative();
} else {
KeyPair issuer = KeyPair.fromAccountId(assetIssuer);
return Asset.createNonNativeAsset(assetCode, issuer);
}
}
}

View file

@ -0,0 +1,44 @@
package org.stellar.sdk.responses.effects;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.Asset;
import org.stellar.sdk.AssetTypeNative;
import org.stellar.sdk.KeyPair;
/**
* Represents account_debited effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountDebitedEffectResponse extends EffectResponse {
@SerializedName("amount")
protected final String amount;
@SerializedName("asset_type")
protected final String assetType;
@SerializedName("asset_code")
protected final String assetCode;
@SerializedName("asset_issuer")
protected final String assetIssuer;
AccountDebitedEffectResponse(String amount, String assetType, String assetCode, String assetIssuer) {
this.amount = amount;
this.assetType = assetType;
this.assetCode = assetCode;
this.assetIssuer = assetIssuer;
}
public String getAmount() {
return amount;
}
public Asset getAsset() {
if (assetType.equals("native")) {
return new AssetTypeNative();
} else {
KeyPair issuer = KeyPair.fromAccountId(assetIssuer);
return Asset.createNonNativeAsset(assetCode, issuer);
}
}
}

View file

@ -0,0 +1,29 @@
package org.stellar.sdk.responses.effects;
import com.google.gson.annotations.SerializedName;
/**
* Represents account_flags_updated effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountFlagsUpdatedEffectResponse extends EffectResponse {
@SerializedName("auth_required_flag")
protected final Boolean authRequiredFlag;
@SerializedName("auth_revokable_flag")
protected final Boolean authRevokableFlag;
AccountFlagsUpdatedEffectResponse(Boolean authRequiredFlag, Boolean authRevokableFlag) {
this.authRequiredFlag = authRequiredFlag;
this.authRevokableFlag = authRevokableFlag;
}
public Boolean getAuthRequiredFlag() {
return authRequiredFlag;
}
public Boolean getAuthRevokableFlag() {
return authRevokableFlag;
}
}

View file

@ -0,0 +1,22 @@
package org.stellar.sdk.responses.effects;
import com.google.gson.annotations.SerializedName;
/**
* Represents account_home_domain_updated effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountHomeDomainUpdatedEffectResponse extends EffectResponse {
@SerializedName("home_domain")
protected final String homeDomain;
AccountHomeDomainUpdatedEffectResponse(String homeDomain) {
this.homeDomain = homeDomain;
}
public String getHomeDomain() {
return homeDomain;
}
}

View file

@ -0,0 +1,11 @@
package org.stellar.sdk.responses.effects;
/**
* Represents account_inflation_destination_updated effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountInflationDestinationUpdatedEffectResponse extends EffectResponse {
//
}

View file

@ -0,0 +1,9 @@
package org.stellar.sdk.responses.effects;
/**
* Represents account_removed effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountRemovedEffectResponse extends EffectResponse {}

View file

@ -0,0 +1,36 @@
package org.stellar.sdk.responses.effects;
import com.google.gson.annotations.SerializedName;
/**
* Represents account_thresholds_updated effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class AccountThresholdsUpdatedEffectResponse extends EffectResponse {
@SerializedName("low_threshold")
protected final Integer lowThreshold;
@SerializedName("med_threshold")
protected final Integer medThreshold;
@SerializedName("high_threshold")
protected final Integer highThreshold;
AccountThresholdsUpdatedEffectResponse(Integer lowThreshold, Integer medThreshold, Integer highThreshold) {
this.lowThreshold = lowThreshold;
this.medThreshold = medThreshold;
this.highThreshold = highThreshold;
}
public Integer getLowThreshold() {
return lowThreshold;
}
public Integer getMedThreshold() {
return medThreshold;
}
public Integer getHighThreshold() {
return highThreshold;
}
}

View file

@ -0,0 +1,11 @@
package org.stellar.sdk.responses.effects;
/**
* Represents data_created effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class DataCreatedEffectResponse extends EffectResponse {
//
}

View file

@ -0,0 +1,11 @@
package org.stellar.sdk.responses.effects;
/**
* Represents data_removed effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class DataRemovedEffectResponse extends EffectResponse {
//
}

View file

@ -0,0 +1,11 @@
package org.stellar.sdk.responses.effects;
/**
* Represents data_updated effect response.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public class DataUpdatedEffectResponse extends EffectResponse {
//
}

View file

@ -0,0 +1,111 @@
package org.stellar.sdk.responses.effects;
import com.google.gson.annotations.SerializedName;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.responses.Link;
import org.stellar.sdk.responses.Response;
/**
* Abstract class for effect responses.
* @see <a href="https://www.stellar.org/developers/horizon/reference/resources/effect.html" target="_blank">Effect documentation</a>
* @see org.stellar.sdk.requests.EffectsRequestBuilder
* @see org.stellar.sdk.Server#effects()
*/
public abstract class EffectResponse extends Response {
@SerializedName("id")
protected String id;
@SerializedName("account")
protected KeyPair account;
@SerializedName("type")
protected String type;
@SerializedName("created_at")
protected String createdAt;
@SerializedName("paging_token")
protected String pagingToken;
@SerializedName("_links")
private Links links;
public String getId() {
return id;
}
public KeyPair getAccount() {
return account;
}
/**
* <p>Returns effect type. Possible types:</p>
* <ul>
* <li>account_created</li>
* <li>account_removed</li>
* <li>account_credited</li>
* <li>account_debited</li>
* <li>account_thresholds_updated</li>
* <li>account_home_domain_updated</li>
* <li>account_flags_updated</li>
* <li>account_inflation_destination_updated</li>
* <li>signer_created</li>
* <li>signer_removed</li>
* <li>signer_updated</li>
* <li>trustline_created</li>
* <li>trustline_removed</li>
* <li>trustline_updated</li>
* <li>trustline_authorized</li>
* <li>trustline_deauthorized</li>
* <li>offer_created</li>
* <li>offer_removed</li>
* <li>offer_updated</li>
* <li>trade</li>
* <li>data_created</li>
* <li>data_removed</li>
* <li>data_updated</li>
* <li>sequence_bumped</li>
* </ul>
*/
public String getType() {
return type;
}
public String getPagingToken() {
return pagingToken;
}
public String getCreatedAt() {
return createdAt;
}
public Links getLinks() {
return links;
}
/**
* Represents effect links.
*/
public static class Links {
@SerializedName("operation")
private final Link operation;
@SerializedName("precedes")
private final Link precedes;
@SerializedName("succeeds")
private final Link succeeds;
public Links(Link operation, Link precedes, Link succeeds) {
this.operation = operation;
this.precedes = precedes;
this.succeeds = succeeds;
}
public Link getOperation() {
return operation;
}
public Link getPrecedes() {
return precedes;
}
public Link getSucceeds() {
return succeeds;
}
}
}

Some files were not shown because too many files have changed in this diff Show more