Skip to content

Commit

Permalink
Add support for InvokeHostFunctionOperation.
Browse files Browse the repository at this point in the history
  • Loading branch information
overcat committed Jul 20, 2023
1 parent 3bd0632 commit c7e54fb
Show file tree
Hide file tree
Showing 9 changed files with 680 additions and 14 deletions.
153 changes: 153 additions & 0 deletions src/main/java/org/stellar/sdk/InvokeHostFunctionOperation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package org.stellar.sdk;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Objects;
import java.util.Arrays;
import org.stellar.sdk.xdr.*;

/**
* Represents <a
* href="https://developers.stellar.org/docs/fundamentals-and-concepts/list-of-operations#invoke-host-function"
* target="_blank">InvokeHostFunction</a> operation.
*
* @see <a href="https://developers.stellar.org/docs/fundamentals-and-concepts/list-of-operations"
* target="_blank">List of Operations</a>
*/
public class InvokeHostFunctionOperation extends Operation {

private final HostFunction hostFunction;
private SorobanAuthorizationEntry[] auth;

/**
* Constructs a new InvokeHostFunctionOperation object using the specified parameters.
*
* @param hostFunction host function to execute (with its wrapped parameters)
* @param auth the authorizations required to execute the host function
*/
private InvokeHostFunctionOperation(HostFunction hostFunction, SorobanAuthorizationEntry[] auth) {
this.hostFunction = hostFunction;
this.auth = auth;
}

/**
* Returns host function to execute (with its wrapped parameters).
*
* @return The host function to execute.
*/
public HostFunction getHostFunction() {
return hostFunction;
}

/**
* Returns the authorizations required to execute the host function.
*
* @return The authorizations.
*/
public SorobanAuthorizationEntry[] getAuth() {
return auth;
}

/**
* Sets the authorizations required to execute the host function.
*
* @param auth The authorizations.
*/
public void setAuth(SorobanAuthorizationEntry[] auth) {
this.auth = auth;
}

@Override
org.stellar.sdk.xdr.Operation.OperationBody toOperationBody(AccountConverter accountConverter) {
InvokeHostFunctionOp op = new InvokeHostFunctionOp();
op.setHostFunction(this.hostFunction);
op.setAuth(this.auth);

org.stellar.sdk.xdr.Operation.OperationBody body =
new org.stellar.sdk.xdr.Operation.OperationBody();
body.setDiscriminant(OperationType.INVOKE_HOST_FUNCTION);
body.setInvokeHostFunctionOp(op);

return body;
}

/**
* Builds InvokeHostFunction operation.
*
* @see InvokeHostFunctionOperation
*/
public static class Builder {
private final HostFunction hostFunction;
private final SorobanAuthorizationEntry[] auth;

private String mSourceAccount;

/**
* Creates a new InvokeHostFunction builder using the specified parameters.
*
* @param op the XDR representation of the {@link InvokeHostFunctionOperation}.
*/
Builder(InvokeHostFunctionOp op) {
this.hostFunction = op.getHostFunction();
this.auth = op.getAuth();
}

/**
* Creates a new InvokeHostFunction builder using the specified parameters.
*
* @param hostFunction host function to execute (with its wrapped parameters)
* @param auth the authorizations required to execute the host function
*/
public Builder(HostFunction hostFunction, SorobanAuthorizationEntry[] auth) {
this.hostFunction = checkNotNull(hostFunction, "hostFunction cannot be null");
this.auth = checkNotNull(auth, "auth cannot be null");
}

/**
* Creates a new InvokeHostFunction builder using the specified parameters.
*
* @param hostFunction host function to execute (with its wrapped parameters)
*/
public Builder(HostFunction hostFunction) {
this.hostFunction = checkNotNull(hostFunction, "hostFunction cannot be null");
this.auth = new SorobanAuthorizationEntry[] {};
}

/**
* Set source account of this operation
*
* @param sourceAccount Source account
* @return Builder object so you can chain methods.
*/
public Builder setSourceAccount(String sourceAccount) {
mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
return this;
}

/** Builds an operation */
public InvokeHostFunctionOperation build() {
InvokeHostFunctionOperation operation =
new InvokeHostFunctionOperation(this.hostFunction, this.auth);
if (mSourceAccount != null) {
operation.setSourceAccount(mSourceAccount);
}
return operation;
}
}

public int hashCode() {
return Objects.hashCode(this.getSourceAccount(), hostFunction, Arrays.hashCode(auth));
}

@Override
public boolean equals(Object object) {
if (!(object instanceof InvokeHostFunctionOperation)) {
return false;
}

InvokeHostFunctionOperation o = (InvokeHostFunctionOperation) object;
return Objects.equal(this.getHostFunction(), o.getHostFunction())
&& Arrays.equals(this.getAuth(), o.getAuth())
&& Objects.equal(this.getSourceAccount(), o.getSourceAccount());
}
}
3 changes: 3 additions & 0 deletions src/main/java/org/stellar/sdk/Operation.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ public static Operation fromXdr(
case LIQUIDITY_POOL_WITHDRAW:
operation = new LiquidityPoolWithdrawOperation(body.getLiquidityPoolWithdrawOp());
break;
case INVOKE_HOST_FUNCTION:
operation = new InvokeHostFunctionOperation.Builder(body.getInvokeHostFunctionOp()).build();
break;
default:
throw new RuntimeException("Unknown operation body " + body.getDiscriminant());
}
Expand Down
54 changes: 51 additions & 3 deletions src/main/java/org/stellar/sdk/Transaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Objects;
import com.google.common.base.Optional;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
Expand All @@ -20,6 +21,7 @@ public class Transaction extends AbstractTransaction {
private final Operation[] mOperations;
private final Memo mMemo;
private final TransactionPreconditions mPreconditions;
private Optional<SorobanTransactionData> mSorobanData;
private EnvelopeType envelopeType = EnvelopeType.ENVELOPE_TYPE_TX;

Transaction(
Expand All @@ -30,6 +32,7 @@ public class Transaction extends AbstractTransaction {
Operation[] operations,
Memo memo,
TransactionPreconditions preconditions,
Optional<SorobanTransactionData> sorobanData,
Network network) {
super(accountConverter, network);
this.mSourceAccount = checkNotNull(sourceAccount, "sourceAccount cannot be null");
Expand All @@ -39,6 +42,7 @@ public class Transaction extends AbstractTransaction {
this.mPreconditions = preconditions;
this.mFee = fee;
this.mMemo = memo != null ? memo : Memo.none();
this.mSorobanData = sorobanData != null ? sorobanData : Optional.absent();
}

// setEnvelopeType is only used in tests which is why this method is package protected
Expand Down Expand Up @@ -67,6 +71,36 @@ public Memo getMemo() {
return mMemo;
}

/**
* Get the Soroban data for the transaction
*
* @return SorobanTransactionData
*/
public Optional<SorobanTransactionData> getSorobanData() {
return mSorobanData;
}

/**
* Sets Soroban data to the transaction. TODO: After adding SorobanServer, add more descriptions.
*
* @param sorobanData Soroban data to set
* @return Builder object so you can chain methods.
*/
public void setSorobanData(SorobanTransactionData sorobanData) {
this.mSorobanData = Optional.fromNullable(sorobanData);
}

/**
* Sets Soroban data to the transaction. TODO: After adding SorobanServer, add more descriptions.
*
* @param sorobanData Soroban data to set
* @return Builder object so you can chain methods.
*/
public void setSorobanData(String sorobanData) throws IOException {
SorobanTransactionData data = Util.sorobanTransactionDataToXDR(sorobanData);
this.mSorobanData = Optional.of(data);
}

/**
* Get the pre-conditions for the transaction
*
Expand Down Expand Up @@ -195,7 +229,12 @@ private org.stellar.sdk.xdr.Transaction toV1Xdr(AccountConverter accountConverte
// ext
org.stellar.sdk.xdr.Transaction.TransactionExt ext =
new org.stellar.sdk.xdr.Transaction.TransactionExt();
ext.setDiscriminant(0);
if (this.mSorobanData.isPresent()) {
ext.setDiscriminant(1);
ext.setSorobanData(this.mSorobanData.get());
} else {
ext.setDiscriminant(0);
}

org.stellar.sdk.xdr.Transaction v1Tx = new org.stellar.sdk.xdr.Transaction();
v1Tx.setFee(fee);
Expand Down Expand Up @@ -230,6 +269,7 @@ public static Transaction fromV0EnvelopeXdr(
mOperations,
mMemo,
TransactionPreconditions.builder().timeBounds(mTimeBounds).build(),
Optional.absent(),
network);
transaction.setEnvelopeType(EnvelopeType.ENVELOPE_TYPE_TX_V0);

Expand All @@ -253,6 +293,11 @@ public static Transaction fromV1EnvelopeXdr(
mOperations[i] = Operation.fromXdr(accountConverter, envelope.getTx().getOperations()[i]);
}

Optional<SorobanTransactionData> sorobanData = Optional.absent();
if (envelope.getTx().getExt().getDiscriminant() == 1) {
sorobanData = Optional.of(envelope.getTx().getExt().getSorobanData());
}

Transaction transaction =
new Transaction(
accountConverter,
Expand All @@ -262,6 +307,7 @@ public static Transaction fromV1EnvelopeXdr(
mOperations,
mMemo,
TransactionPreconditions.fromXdr(envelope.getTx().getCond()),
sorobanData,
network);

transaction.mSignatures.addAll(Arrays.asList(envelope.getSignatures()));
Expand Down Expand Up @@ -310,7 +356,8 @@ public int hashCode() {
this.mMemo,
this.mPreconditions,
this.mSignatures,
this.mNetwork);
this.mNetwork,
this.mSorobanData);
}

@Override
Expand All @@ -328,7 +375,8 @@ public boolean equals(Object object) {
&& Objects.equal(this.mMemo, other.mMemo)
&& Objects.equal(this.mPreconditions, other.mPreconditions)
&& Objects.equal(this.mNetwork, other.mNetwork)
&& Objects.equal(this.mSignatures, other.mSignatures);
&& Objects.equal(this.mSignatures, other.mSignatures)
&& Objects.equal(this.mSorobanData, other.mSorobanData);
}

/**
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/org/stellar/sdk/TransactionBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import static org.stellar.sdk.TransactionPreconditions.TIMEOUT_INFINITE;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import java.util.Collection;
import java.util.List;
import org.stellar.sdk.TransactionPreconditions.TransactionPreconditionsBuilder;
import org.stellar.sdk.xdr.SorobanTransactionData;
import org.stellar.sdk.xdr.TimePoint;
import org.stellar.sdk.xdr.Uint64;

Expand All @@ -20,6 +22,7 @@ public class TransactionBuilder {
private Integer mBaseFee;
private Network mNetwork;
private TransactionPreconditions mPreconditions;
private Optional<SorobanTransactionData> mSorobanData;

/**
* Construct a new transaction builder.
Expand All @@ -37,6 +40,7 @@ public TransactionBuilder(
mNetwork = checkNotNull(network, "Network cannot be null");
mOperations = newArrayList();
mPreconditions = TransactionPreconditions.builder().build();
mSorobanData = Optional.absent();
}

/**
Expand Down Expand Up @@ -220,6 +224,7 @@ public Transaction build() {
operations,
mMemo,
mPreconditions,
mSorobanData,
mNetwork);
mSourceAccount.setSequenceNumber(sequenceNumber);
return transaction;
Expand All @@ -243,4 +248,26 @@ public static org.stellar.sdk.xdr.TimeBounds buildTimeBounds(long minTime, long
.maxTime(new TimePoint(new Uint64(maxTime)))
.build();
}

/**
* Sets Soroban data to the transaction. TODO: After adding SorobanServer, add more descriptions.
*
* @param sorobanData Soroban data to set
* @return Builder object so you can chain methods.
*/
public TransactionBuilder setSorobanData(SorobanTransactionData sorobanData) {
this.mSorobanData = Optional.fromNullable(sorobanData);
return this;
}

/**
* Sets Soroban data to the transaction. TODO: After adding SorobanServer, add more descriptions.
*
* @param sorobanData Soroban data to set
* @return Builder object so you can chain methods.
*/
public TransactionBuilder setSorobanData(String sorobanData) {
SorobanTransactionData data = Util.sorobanTransactionDataToXDR(sorobanData);
return setSorobanData(data);
}
}
Loading

0 comments on commit c7e54fb

Please sign in to comment.