Skip to content

Commit

Permalink
Add abstractions for converting between SCVal and native types.
Browse files Browse the repository at this point in the history
  • Loading branch information
overcat committed Aug 5, 2023
1 parent 315413a commit d262d33
Show file tree
Hide file tree
Showing 52 changed files with 2,658 additions and 71 deletions.
11 changes: 6 additions & 5 deletions src/main/java/org/stellar/sdk/InvokeHostFunctionOperation.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import lombok.Singular;
import lombok.Value;
import lombok.experimental.SuperBuilder;
import org.stellar.sdk.scval.ScvAddress;
import org.stellar.sdk.xdr.ContractExecutable;
import org.stellar.sdk.xdr.ContractExecutableType;
import org.stellar.sdk.xdr.ContractIDPreimage;
Expand Down Expand Up @@ -93,7 +94,7 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) {
* @return {@link InvokeHostFunctionOperationBuilder}
*/
public static InvokeHostFunctionOperationBuilder<?, ?> createContractOperationBuilder(
String wasmId, Address address, @Nullable byte[] salt) {
String wasmId, ScvAddress address, @Nullable byte[] salt) {
byte[] wasmIdBytes = Util.hexToBytes(wasmId);
return createContractOperationBuilder(wasmIdBytes, address, salt);
}
Expand All @@ -110,7 +111,7 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) {
* @return {@link InvokeHostFunctionOperationBuilder}
*/
public static InvokeHostFunctionOperationBuilder<?, ?> createContractOperationBuilder(
byte[] wasmId, Address address, @Nullable byte[] salt) {
byte[] wasmId, ScvAddress address, @Nullable byte[] salt) {
if (salt == null) {
salt = new byte[32];
new SecureRandom().nextBytes(salt);
Expand Down Expand Up @@ -188,7 +189,7 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) {
* @return {@link InvokeHostFunctionOperationBuilder}
*/
public static InvokeHostFunctionOperationBuilder<?, ?> createTokenContractOperationBuilder(
Address address, @Nullable byte[] salt) {
ScvAddress address, @Nullable byte[] salt) {
if (salt == null) {
salt = new byte[32];
new SecureRandom().nextBytes(salt);
Expand Down Expand Up @@ -235,8 +236,8 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) {
*/
public static InvokeHostFunctionOperationBuilder<?, ?> invokeContractFunctionOperationBuilder(
String contractId, String functionName, @Nullable Collection<SCVal> parameters) {
Address address = new Address(contractId);
if (address.getType() != Address.AddressType.CONTRACT) {
ScvAddress address = new ScvAddress(contractId);
if (address.getAddressType() != ScvAddress.AddressType.CONTRACT) {
throw new IllegalArgumentException("\"contractId\" must be a contract address");
}
SCVal contractIdScVal = address.toSCVal();
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/stellar/sdk/SorobanServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.stellar.sdk.responses.sorobanrpc.SendTransactionResponse;
import org.stellar.sdk.responses.sorobanrpc.SimulateTransactionResponse;
import org.stellar.sdk.responses.sorobanrpc.SorobanRpcResponse;
import org.stellar.sdk.scval.ScvAddress;
import org.stellar.sdk.xdr.ContractDataDurability;
import org.stellar.sdk.xdr.ContractEntryBodyType;
import org.stellar.sdk.xdr.LedgerEntry;
Expand Down Expand Up @@ -175,7 +176,7 @@ public Optional<GetLedgerEntriesResponse.LedgerEntryResult> getContractData(
throw new IllegalArgumentException("Invalid durability: " + durability);
}

Address address = new Address(contractId);
ScvAddress address = new ScvAddress(contractId);
LedgerKey.LedgerKeyContractData ledgerKeyContractData =
new LedgerKey.LedgerKeyContractData.Builder()
.contract(address.toSCAddress())
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/stellar/sdk/StrKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import org.stellar.sdk.xdr.XdrDataOutputStream;
import org.stellar.sdk.xdr.XdrUnsignedHyperInteger;

class StrKey {
public class StrKey {

public static final int ACCOUNT_ID_ADDRESS_LENGTH = 56;
private static final byte[] b32Table = decodingTable();
Expand Down
94 changes: 94 additions & 0 deletions src/main/java/org/stellar/sdk/scval/Scv.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.stellar.sdk.scval;

import java.math.BigInteger;
import java.util.Arrays;
import org.stellar.sdk.xdr.SCVal;
import org.stellar.sdk.xdr.SCValType;

/** Abstract class for all SCV types. */
public abstract class Scv {

/**
* Convert this object to {@link SCVal}.
*
* @return {@link SCVal}
*/
public abstract SCVal toSCVal();

/**
* Get the {@link SCValType} of this object.
*
* @return {@link SCValType}
*/
public abstract SCValType getSCValType();

/**
* Convert from {@link SCVal} to {@link Scv}.
*
* @param scVal {@link SCVal} to convert
* @return {@link Scv}
*/
public static Scv fromSCVal(SCVal scVal) {
switch (scVal.getDiscriminant()) {
case SCV_BOOL:
return ScvBoolean.fromSCVal(scVal);
case SCV_VOID:
return ScvVoid.fromSCVal(scVal);
case SCV_ERROR:
return ScvError.fromSCVal(scVal);
case SCV_U32:
return ScvUint32.fromSCVal(scVal);
case SCV_I32:
return ScvInt32.fromSCVal(scVal);
case SCV_U64:
return ScvUint64.fromSCVal(scVal);
case SCV_I64:
return ScvInt64.fromSCVal(scVal);
case SCV_TIMEPOINT:
return ScvTimePoint.fromSCVal(scVal);
case SCV_DURATION:
return ScvDuration.fromSCVal(scVal);
case SCV_U128:
return ScvUint128.fromSCVal(scVal);
case SCV_I128:
return ScvInt128.fromSCVal(scVal);
case SCV_U256:
return ScvUint256.fromSCVal(scVal);
case SCV_I256:
return ScvInt256.fromSCVal(scVal);
case SCV_BYTES:
return ScvBytes.fromSCVal(scVal);
case SCV_STRING:
return ScvString.fromSCVal(scVal);
case SCV_SYMBOL:
return ScvSymbol.fromSCVal(scVal);
case SCV_VEC:
return ScvVec.fromSCVal(scVal);
case SCV_MAP:
return ScvMap.fromSCVal(scVal);
case SCV_ADDRESS:
return ScvAddress.fromSCVal(scVal);
case SCV_CONTRACT_INSTANCE:
return ScvContractInstance.fromSCVal(scVal);
case SCV_LEDGER_KEY_CONTRACT_INSTANCE:
return ScvLedgerKeyContractInstance.fromSCVal(scVal);
case SCV_LEDGER_KEY_NONCE:
return ScvLedgerKeyNonce.fromSCVal(scVal);
default:
throw new IllegalArgumentException(
String.format("unsupported scVal type: %s", scVal.getDiscriminant()));
}
}

static byte[] getBytes(BigInteger value) {
byte[] bytes = value.toByteArray();
if (bytes.length < 8) {
byte[] temp = new byte[8];
System.arraycopy(bytes, 0, temp, 8 - bytes.length, bytes.length);
bytes = temp;
} else if (bytes.length > 8) {
bytes = Arrays.copyOfRange(bytes, bytes.length - 8, bytes.length);
}
return bytes;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.stellar.sdk;
package org.stellar.sdk.scval;

import com.google.common.base.Objects;
import org.stellar.sdk.KeyPair;
import org.stellar.sdk.StrKey;
import org.stellar.sdk.xdr.Hash;
import org.stellar.sdk.xdr.SCAddress;
import org.stellar.sdk.xdr.SCVal;
Expand All @@ -10,18 +12,19 @@
* Represents a single address in the Stellar network. An address can represent an account or a
* contract.
*/
public class Address {
public class ScvAddress extends Scv {
private static final SCValType TYPE = SCValType.SCV_ADDRESS;

private final byte[] key;

private final AddressType type;

/**
* Creates a new {@link Address} from a Stellar public key or contract ID.
* Creates a new {@link ScvAddress} from a Stellar public key or contract ID.
*
* @param address the StrKey encoded format of Stellar public key or contract ID.
*/
public Address(String address) {
public ScvAddress(String address) {
if (StrKey.isValidStellarAccountId(address)) {
this.type = AddressType.ACCOUNT;
this.key = StrKey.decodeStellarAccountId(address);
Expand All @@ -34,53 +37,55 @@ public Address(String address) {
}

/**
* Creates a new {@link Address} from a Stellar public key.
* Creates a new {@link ScvAddress} from a Stellar public key.
*
* @param accountId the byte array of the Stellar public key.
* @return a new {@link Address} object from the given Stellar public key.
* @return a new {@link ScvAddress} object from the given Stellar public key.
*/
public static Address fromAccount(byte[] accountId) {
return new Address(StrKey.encodeStellarAccountId(accountId));
public static ScvAddress fromAccount(byte[] accountId) {
return new ScvAddress(StrKey.encodeStellarAccountId(accountId));
}

/**
* Creates a new {@link Address} from a Stellar Contract ID.
* Creates a new {@link ScvAddress} from a Stellar Contract ID.
*
* @param contractId the byte array of the Stellar Contract ID.
* @return a new {@link Address} object from the given Stellar Contract ID.
* @return a new {@link ScvAddress} object from the given Stellar Contract ID.
*/
public static Address fromContract(byte[] contractId) {
return new Address(StrKey.encodeContractId(contractId));
public static ScvAddress fromContract(byte[] contractId) {
return new ScvAddress(StrKey.encodeContractId(contractId));
}

/**
* Creates a new {@link Address} from a {@link SCAddress} XDR object.
* Creates a new {@link ScvAddress} from a {@link SCAddress} XDR object.
*
* @param scAddress the {@link SCAddress} object to convert
* @return a new {@link Address} object from the given XDR object
* @return a new {@link ScvAddress} object from the given XDR object
*/
public static Address fromSCAddress(SCAddress scAddress) {
public static ScvAddress fromSCAddress(SCAddress scAddress) {
switch (scAddress.getDiscriminant()) {
case SC_ADDRESS_TYPE_ACCOUNT:
return new Address(StrKey.encodeStellarAccountId(scAddress.getAccountId()));
return new ScvAddress(StrKey.encodeStellarAccountId(scAddress.getAccountId()));
case SC_ADDRESS_TYPE_CONTRACT:
return new Address(StrKey.encodeContractId(scAddress.getContractId().getHash()));
return new ScvAddress(StrKey.encodeContractId(scAddress.getContractId().getHash()));
default:
throw new IllegalArgumentException("Unsupported address type");
}
}

/**
* Creates a new {@link Address} from a {@link SCVal} XDR object.
* Creates a new {@link ScvAddress} from a {@link SCVal} XDR object.
*
* @param scVal the {@link SCVal} object to convert
* @return a new {@link Address} object from the given XDR object
* @return a new {@link ScvAddress} object from the given XDR object
*/
public static Address fromSCVal(SCVal scVal) {
if (!SCValType.SCV_ADDRESS.equals(scVal.getDiscriminant())) {
throw new IllegalArgumentException("SCVal is not of type SCV_ADDRESS");
public static ScvAddress fromSCVal(SCVal scVal) {
if (!TYPE.equals(scVal.getDiscriminant())) {
throw new IllegalArgumentException(
String.format(
"invalid scVal type, expected %s, but got %s", TYPE, scVal.getDiscriminant()));
}
return Address.fromSCAddress(scVal.getAddress());
return ScvAddress.fromSCAddress(scVal.getAddress());
}

/**
Expand Down Expand Up @@ -112,11 +117,16 @@ public SCAddress toSCAddress() {
*/
public SCVal toSCVal() {
SCVal scVal = new SCVal();
scVal.setDiscriminant(SCValType.SCV_ADDRESS);
scVal.setDiscriminant(TYPE);
scVal.setAddress(this.toSCAddress());
return scVal;
}

@Override
public SCValType getSCValType() {
return TYPE;
}

/**
* Returns the byte array of the Stellar public key or contract ID.
*
Expand All @@ -131,7 +141,7 @@ public byte[] getBytes() {
*
* @return the type of this address.
*/
public AddressType getType() {
public AddressType getAddressType() {
return type;
}

Expand All @@ -142,11 +152,11 @@ public int hashCode() {

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

Address other = (Address) object;
ScvAddress other = (ScvAddress) object;
return Objects.equal(this.key, other.key) && Objects.equal(this.type, other.type);
}

Expand Down
36 changes: 36 additions & 0 deletions src/main/java/org/stellar/sdk/scval/ScvBoolean.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.stellar.sdk.scval;

import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import org.stellar.sdk.xdr.SCVal;
import org.stellar.sdk.xdr.SCValType;

@Value
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class ScvBoolean extends Scv {
private static final SCValType TYPE = SCValType.SCV_BOOL;

Boolean value;

@Override
public SCVal toSCVal() {
return new SCVal.Builder().discriminant(TYPE).b(value).build();
}

@Override
public SCValType getSCValType() {
return TYPE;
}

public static ScvBoolean fromSCVal(SCVal scVal) {
if (scVal.getDiscriminant() != TYPE) {
throw new IllegalArgumentException(
String.format(
"invalid scVal type, expected %s, but got %s", TYPE, scVal.getDiscriminant()));
}

return new ScvBoolean(scVal.getB());
}
}
37 changes: 37 additions & 0 deletions src/main/java/org/stellar/sdk/scval/ScvBytes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.stellar.sdk.scval;

import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import org.stellar.sdk.xdr.SCBytes;
import org.stellar.sdk.xdr.SCVal;
import org.stellar.sdk.xdr.SCValType;

@Value
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class ScvBytes extends Scv {
private static final SCValType TYPE = SCValType.SCV_BYTES;

byte[] value;

@Override
public SCVal toSCVal() {
return new SCVal.Builder().discriminant(TYPE).bytes(new SCBytes(value)).build();
}

@Override
public SCValType getSCValType() {
return TYPE;
}

public static ScvBytes fromSCVal(SCVal scVal) {
if (scVal.getDiscriminant() != TYPE) {
throw new IllegalArgumentException(
String.format(
"invalid scVal type, expected %s, but got %s", TYPE, scVal.getDiscriminant()));
}

return new ScvBytes(scVal.getBytes().getSCBytes());
}
}
Loading

0 comments on commit d262d33

Please sign in to comment.