From 894754f784ec381ad70706891412c0a3075830c7 Mon Sep 17 00:00:00 2001 From: overcat <4catcode@gmail.com> Date: Sun, 13 Aug 2023 11:26:28 +0800 Subject: [PATCH 1/6] Add SorobanDataBuilder to prepare sorobanData easily. --- .../org/stellar/sdk/SorobanDataBuilder.java | 173 ++++++++++++++++++ .../java/org/stellar/sdk/Transaction.java | 2 +- .../org/stellar/sdk/TransactionBuilder.java | 12 +- .../stellar/sdk/SorobanDataBuilderTest.java | 161 ++++++++++++++++ .../org/stellar/sdk/SorobanServerTest.java | 63 ++++--- 5 files changed, 372 insertions(+), 39 deletions(-) create mode 100644 src/main/java/org/stellar/sdk/SorobanDataBuilder.java create mode 100644 src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java diff --git a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java new file mode 100644 index 000000000..3368f7275 --- /dev/null +++ b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java @@ -0,0 +1,173 @@ +package org.stellar.sdk; + +import java.io.IOException; +import java.util.Collection; +import javax.annotation.Nullable; +import org.stellar.sdk.xdr.ExtensionPoint; +import org.stellar.sdk.xdr.Int64; +import org.stellar.sdk.xdr.LedgerFootprint; +import org.stellar.sdk.xdr.LedgerKey; +import org.stellar.sdk.xdr.SorobanResources; +import org.stellar.sdk.xdr.SorobanTransactionData; +import org.stellar.sdk.xdr.Uint32; +import org.stellar.sdk.xdr.XdrUnsignedInteger; + +/** + * Supports building {@link SorobanTransactionData} structures with various items set to specific + * values. + * + *

This is recommended for when you are building {@link BumpFootprintExpirationOperation} {@link + * RestoreFootprintOperation} operations to avoid (re)building the entire data structure from + * scratch. + */ +public class SorobanDataBuilder { + private final SorobanTransactionData data; + + /** Creates a new builder with an empty {@link SorobanTransactionData}. */ + public SorobanDataBuilder() { + data = + new SorobanTransactionData.Builder() + .resources( + new SorobanResources.Builder() + .footprint( + new LedgerFootprint.Builder() + .readOnly(new LedgerKey[] {}) + .readWrite(new LedgerKey[] {}) + .build()) + .instructions(new Uint32(new XdrUnsignedInteger(0))) + .readBytes(new Uint32(new XdrUnsignedInteger(0))) + .writeBytes(new Uint32(new XdrUnsignedInteger(0))) + .extendedMetaDataSizeBytes(new Uint32(new XdrUnsignedInteger(0))) + .build()) + .refundableFee(new Int64(0L)) + .ext(new ExtensionPoint.Builder().discriminant(0).build()) + .build(); + } + + /** + * Creates a new builder from a base64 representation of {@link SorobanTransactionData}. + * + * @param sorobanData base64 representation of {@link SorobanTransactionData} + */ + public SorobanDataBuilder(String sorobanData) { + try { + data = SorobanTransactionData.fromXdrBase64(sorobanData); + } catch (IOException e) { + throw new IllegalArgumentException("Invalid SorobanData: " + sorobanData, e); + } + } + + /** + * Creates a new builder from a {@link SorobanTransactionData}. + * + * @param sorobanData {@link SorobanTransactionData}. + */ + public SorobanDataBuilder(SorobanTransactionData sorobanData) { + try { + data = SorobanTransactionData.fromXdrByteArray(sorobanData.toXdrByteArray()); + } catch (IOException e) { + throw new IllegalArgumentException("Invalid SorobanData: " + sorobanData, e); + } + } + + /** + * Sets the "refundable" fee portion of the Soroban data. + * + * @param fee the refundable fee to set (int64) + * @return this builder instance + */ + public SorobanDataBuilder setRefundableFee(long fee) { + data.setRefundableFee(new Int64(fee)); + return this; + } + + /** + * Sets up the resource metrics. + * + *

You should almost NEVER need this, as its often generated / provided to you by transaction + * simulation/preflight from a Soroban RPC server. + * + * @param cpuInstructions number of CPU instructions (uint32) + * @param readBytes number of bytes being read (uint32) + * @param writeBytes number of bytes being written (uint32) + * @param metadataBytes number of extended metadata bytes (uint32) + * @return this builder instance + */ + public SorobanDataBuilder setResources( + long cpuInstructions, long readBytes, long writeBytes, long metadataBytes) { + data.getResources().setInstructions(new Uint32(new XdrUnsignedInteger(cpuInstructions))); + data.getResources().setReadBytes(new Uint32(new XdrUnsignedInteger(readBytes))); + data.getResources().setWriteBytes(new Uint32(new XdrUnsignedInteger(writeBytes))); + data.getResources() + .setExtendedMetaDataSizeBytes(new Uint32(new XdrUnsignedInteger(metadataBytes))); + return this; + } + + /** + * Sets the storage access footprint to be a certain set of ledger keys. + * + *

You can also set each field explicitly via {@link + * SorobanDataBuilder#setReadOnly(Collection)} and {@link + * SorobanDataBuilder#setReadWrite(Collection)}. + * + *

Passing {@code null} to either parameter will IGNORE the existing values. If you want to + * clear them, pass empty collection instead. + * + * @param readOnly the set of ledger keys to set in the read-only portion of the transaction's + * sorobanData + * @param readWrite the set of ledger keys to set in the read-write portion of the transaction's + * sorobanData + * @return this builder instance + */ + public SorobanDataBuilder setFootprint( + @Nullable Collection readOnly, @Nullable Collection readWrite) { + if (readOnly != null) { + data.getResources().getFootprint().setReadOnly(readOnly.toArray(new LedgerKey[0])); + } + if (readWrite != null) { + data.getResources().getFootprint().setReadWrite(readWrite.toArray(new LedgerKey[0])); + } + return this; + } + + /** + * Sets the read-only portion of the storage access footprint to be a certain set of ledger keys. + * + * @param readOnly the set of ledger keys to set in the read-only portion of the transaction's + * sorobanData + * @return this builder instance + */ + public SorobanDataBuilder setReadOnly(@Nullable Collection readOnly) { + if (readOnly != null) { + data.getResources().getFootprint().setReadOnly(readOnly.toArray(new LedgerKey[0])); + } + return this; + } + + /** + * Sets the read-write portion of the storage access footprint to be a certain set of ledger keys. + * + * @param readWrite the set of ledger keys to set in the read-write portion of the transaction's + * sorobanData + * @return this builder instance + */ + public SorobanDataBuilder setReadWrite(@Nullable Collection readWrite) { + if (readWrite != null) { + data.getResources().getFootprint().setReadWrite(readWrite.toArray(new LedgerKey[0])); + } + return this; + } + + /** + * Returns the copy of the final {@link SorobanTransactionData} built by this builder. + * + * @return the copy of the final {@link SorobanTransactionData}. + */ + public SorobanTransactionData build() { + try { + return SorobanTransactionData.fromXdrByteArray(data.toXdrByteArray()); + } catch (IOException e) { + throw new IllegalArgumentException("Copy SorobanData failed, please report this bug.", e); + } + } +} diff --git a/src/main/java/org/stellar/sdk/Transaction.java b/src/main/java/org/stellar/sdk/Transaction.java index 2bc6df2ad..316c88c93 100644 --- a/src/main/java/org/stellar/sdk/Transaction.java +++ b/src/main/java/org/stellar/sdk/Transaction.java @@ -58,7 +58,7 @@ public class Transaction extends AbstractTransaction { this.mPreconditions = preconditions; this.mFee = fee; this.mMemo = memo != null ? memo : Memo.none(); - this.mSorobanData = sorobanData; + this.mSorobanData = sorobanData != null ? new SorobanDataBuilder(sorobanData).build() : null; } // setEnvelopeType is only used in tests which is why this method is package protected diff --git a/src/main/java/org/stellar/sdk/TransactionBuilder.java b/src/main/java/org/stellar/sdk/TransactionBuilder.java index da85d14f5..8459dd934 100644 --- a/src/main/java/org/stellar/sdk/TransactionBuilder.java +++ b/src/main/java/org/stellar/sdk/TransactionBuilder.java @@ -5,7 +5,6 @@ import static org.stellar.sdk.TransactionPreconditions.TIMEOUT_INFINITE; import com.google.common.base.Function; -import java.io.IOException; import java.math.BigInteger; import java.util.Collection; import java.util.List; @@ -270,7 +269,7 @@ public static org.stellar.sdk.xdr.TimeBounds buildTimeBounds(long minTime, long * @return Builder object so you can chain methods. */ public TransactionBuilder setSorobanData(SorobanTransactionData sorobanData) { - this.mSorobanData = sorobanData; + this.mSorobanData = new SorobanDataBuilder(sorobanData).build(); return this; } @@ -281,12 +280,7 @@ public TransactionBuilder setSorobanData(SorobanTransactionData sorobanData) { * @return Builder object so you can chain methods. */ public TransactionBuilder setSorobanData(String sorobanData) { - SorobanTransactionData data; - try { - data = SorobanTransactionData.fromXdrBase64(sorobanData); - } catch (IOException e) { - throw new IllegalArgumentException("Invalid Soroban data: " + sorobanData, e); - } - return setSorobanData(data); + this.mSorobanData = new SorobanDataBuilder(sorobanData).build(); + return this; } } diff --git a/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java b/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java new file mode 100644 index 000000000..dc12f0e88 --- /dev/null +++ b/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java @@ -0,0 +1,161 @@ +package org.stellar.sdk; + +import static com.google.common.collect.ImmutableList.of; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +import java.io.IOException; +import java.util.ArrayList; +import org.junit.Test; +import org.stellar.sdk.xdr.ExtensionPoint; +import org.stellar.sdk.xdr.Int64; +import org.stellar.sdk.xdr.LedgerEntryType; +import org.stellar.sdk.xdr.LedgerFootprint; +import org.stellar.sdk.xdr.LedgerKey; +import org.stellar.sdk.xdr.SorobanResources; +import org.stellar.sdk.xdr.SorobanTransactionData; +import org.stellar.sdk.xdr.Uint32; +import org.stellar.sdk.xdr.XdrUnsignedInteger; + +public class SorobanDataBuilderTest { + LedgerKey readOnly = + new LedgerKey.Builder() + .discriminant(LedgerEntryType.ACCOUNT) + .account( + new LedgerKey.LedgerKeyAccount.Builder() + .accountID( + KeyPair.fromAccountId( + "GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO") + .getXdrAccountId()) + .build()) + .build(); + LedgerKey readWrite = + new LedgerKey.Builder() + .discriminant(LedgerEntryType.ACCOUNT) + .account( + new LedgerKey.LedgerKeyAccount.Builder() + .accountID( + KeyPair.fromAccountId( + "GAHJJJKMOKYE4RVPZEWZTKH5FVI4PA3VL7GK2LFNUBSGBV6OJP7TQSLX") + .getXdrAccountId()) + .build()) + .build(); + + SorobanTransactionData emptySorobanData = + new SorobanTransactionData.Builder() + .resources( + new SorobanResources.Builder() + .footprint( + new LedgerFootprint.Builder() + .readOnly(new LedgerKey[] {}) + .readWrite(new LedgerKey[] {}) + .build()) + .instructions(new Uint32(new XdrUnsignedInteger(0))) + .readBytes(new Uint32(new XdrUnsignedInteger(0))) + .writeBytes(new Uint32(new XdrUnsignedInteger(0))) + .extendedMetaDataSizeBytes(new Uint32(new XdrUnsignedInteger(0))) + .build()) + .refundableFee(new Int64(0L)) + .ext(new ExtensionPoint.Builder().discriminant(0).build()) + .build(); + + SorobanTransactionData presetSorobanData = + new SorobanTransactionData.Builder() + .resources( + new SorobanResources.Builder() + .footprint( + new LedgerFootprint.Builder() + .readOnly(new LedgerKey[] {readOnly}) + .readWrite(new LedgerKey[] {readWrite}) + .build()) + .instructions(new Uint32(new XdrUnsignedInteger(1))) + .readBytes(new Uint32(new XdrUnsignedInteger(2))) + .writeBytes(new Uint32(new XdrUnsignedInteger(3))) + .extendedMetaDataSizeBytes(new Uint32(new XdrUnsignedInteger(4))) + .build()) + .refundableFee(new Int64(5L)) + .ext(new ExtensionPoint.Builder().discriminant(0).build()) + .build(); + + @Test + public void testConstructorFromEmpty() { + SorobanTransactionData actualData = new SorobanDataBuilder().build(); + assertEquals(emptySorobanData, actualData); + } + + @Test + public void testConstructorFromBase64() throws IOException { + String base64 = presetSorobanData.toXdrBase64(); + SorobanTransactionData actualData = new SorobanDataBuilder(base64).build(); + assertEquals(presetSorobanData, actualData); + } + + @Test + public void testConstructorFromSorobanTransactionData() { + SorobanTransactionData actualData = new SorobanDataBuilder(presetSorobanData).build(); + assertEquals(presetSorobanData, actualData); + } + + @Test + public void testSetProperties() { + SorobanTransactionData actualData0 = + new SorobanDataBuilder() + .setReadOnly(of(readOnly)) + .setReadWrite(of(readWrite)) + .setRefundableFee(5) + .setResources(1, 2, 3, 4) + .build(); + assertEquals(presetSorobanData, actualData0); + + SorobanTransactionData actualData1 = + new SorobanDataBuilder() + .setFootprint(of(readOnly), of(readWrite)) + .setRefundableFee(5) + .setResources(1, 2, 3, 4) + .build(); + assertEquals(presetSorobanData, actualData1); + } + + @Test + public void testLeavesUntouchedFootprintsUntouched() { + SorobanTransactionData data0 = + new SorobanDataBuilder(presetSorobanData).setReadOnly(null).build(); + assertArrayEquals( + new LedgerKey[] {readOnly}, data0.getResources().getFootprint().getReadOnly()); + + SorobanTransactionData data1 = + new SorobanDataBuilder(presetSorobanData).setReadOnly(new ArrayList<>()).build(); + assertArrayEquals(new LedgerKey[] {}, data1.getResources().getFootprint().getReadOnly()); + + SorobanTransactionData data3 = + new SorobanDataBuilder(presetSorobanData).setReadWrite(null).build(); + assertArrayEquals( + new LedgerKey[] {readWrite}, data3.getResources().getFootprint().getReadWrite()); + + SorobanTransactionData data4 = + new SorobanDataBuilder(presetSorobanData).setReadWrite(new ArrayList<>()).build(); + assertArrayEquals(new LedgerKey[] {}, data4.getResources().getFootprint().getReadWrite()); + + SorobanTransactionData data5 = + new SorobanDataBuilder(presetSorobanData).setFootprint(null, null).build(); + assertArrayEquals( + new LedgerKey[] {readOnly}, data5.getResources().getFootprint().getReadOnly()); + assertArrayEquals( + new LedgerKey[] {readWrite}, data5.getResources().getFootprint().getReadWrite()); + + SorobanTransactionData data6 = + new SorobanDataBuilder(presetSorobanData) + .setFootprint(new ArrayList<>(), new ArrayList<>()) + .build(); + assertArrayEquals(new LedgerKey[] {}, data6.getResources().getFootprint().getReadOnly()); + assertArrayEquals(new LedgerKey[] {}, data6.getResources().getFootprint().getReadWrite()); + } + + @Test + public void testBuildCopy() { + SorobanTransactionData actualData = new SorobanDataBuilder(presetSorobanData).build(); + assertEquals(presetSorobanData, actualData); + assertNotSame(presetSorobanData, actualData); + } +} diff --git a/src/test/java/org/stellar/sdk/SorobanServerTest.java b/src/test/java/org/stellar/sdk/SorobanServerTest.java index 57bba78ee..58cf86bfd 100644 --- a/src/test/java/org/stellar/sdk/SorobanServerTest.java +++ b/src/test/java/org/stellar/sdk/SorobanServerTest.java @@ -1362,35 +1362,40 @@ private Transaction buildSorobanTransaction( auth = new ArrayList<>(); } - return new TransactionBuilder(AccountConverter.enableMuxed(), source, Network.STANDALONE) - .setBaseFee(50000) - .addPreconditions( - TransactionPreconditions.builder().timeBounds(new TimeBounds(0, 0)).build()) - .addOperation( - InvokeHostFunctionOperation.builder() - .sourceAccount(opInvokerKp.getAccountId()) - .hostFunction( - new HostFunction.Builder() - .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT) - .invokeContract( - new SCVec( - new SCVal[] { - new Address(contractId).toSCVal(), - new SCVal.Builder() - .discriminant(SCValType.SCV_SYMBOL) - .sym(new SCSymbol(new XdrString("increment"))) - .build(), - new Address(opInvokerKp.getAccountId()).toSCVal(), - new SCVal.Builder() - .discriminant(SCValType.SCV_U32) - .u32(new Uint32(new XdrUnsignedInteger(10))) - .build() - })) - .build()) - .auth(auth) - .build()) - .setSorobanData(sorobanData) - .build(); + TransactionBuilder transactionBuilder = + new TransactionBuilder(AccountConverter.enableMuxed(), source, Network.STANDALONE) + .setBaseFee(50000) + .addPreconditions( + TransactionPreconditions.builder().timeBounds(new TimeBounds(0, 0)).build()) + .addOperation( + InvokeHostFunctionOperation.builder() + .sourceAccount(opInvokerKp.getAccountId()) + .hostFunction( + new HostFunction.Builder() + .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_INVOKE_CONTRACT) + .invokeContract( + new SCVec( + new SCVal[] { + new Address(contractId).toSCVal(), + new SCVal.Builder() + .discriminant(SCValType.SCV_SYMBOL) + .sym(new SCSymbol(new XdrString("increment"))) + .build(), + new Address(opInvokerKp.getAccountId()).toSCVal(), + new SCVal.Builder() + .discriminant(SCValType.SCV_U32) + .u32(new Uint32(new XdrUnsignedInteger(10))) + .build() + })) + .build()) + .auth(auth) + .build()); + + if (sorobanData != null) { + transactionBuilder.setSorobanData(sorobanData); + } + + return transactionBuilder.build(); } private static SorobanAuthorizationEntry sorobanAuthorizationEntryFromXdrBase64( From 27134f51edea232ff9b4aec2e06c8230b2579be4 Mon Sep 17 00:00:00 2001 From: overcat <4catcode@gmail.com> Date: Sun, 13 Aug 2023 19:13:18 +0800 Subject: [PATCH 2/6] fix docs --- src/main/java/org/stellar/sdk/SorobanDataBuilder.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java index 3368f7275..e0942510c 100644 --- a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java +++ b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java @@ -16,8 +16,8 @@ * Supports building {@link SorobanTransactionData} structures with various items set to specific * values. * - *

This is recommended for when you are building {@link BumpFootprintExpirationOperation} {@link - * RestoreFootprintOperation} operations to avoid (re)building the entire data structure from + *

This is recommended for when you are building {@link BumpFootprintExpirationOperation} and + * {@link RestoreFootprintOperation} operations to avoid (re)building the entire data structure from * scratch. */ public class SorobanDataBuilder { @@ -84,7 +84,7 @@ public SorobanDataBuilder setRefundableFee(long fee) { /** * Sets up the resource metrics. * - *

You should almost NEVER need this, as its often generated / provided to you by transaction + *

You should almost NEVER need this, as its often generated/provided to you by transaction * simulation/preflight from a Soroban RPC server. * * @param cpuInstructions number of CPU instructions (uint32) @@ -159,8 +159,6 @@ public SorobanDataBuilder setReadWrite(@Nullable Collection readWrite } /** - * Returns the copy of the final {@link SorobanTransactionData} built by this builder. - * * @return the copy of the final {@link SorobanTransactionData}. */ public SorobanTransactionData build() { From f4213a10918772e2b124efea02c5a7b810367aa3 Mon Sep 17 00:00:00 2001 From: overcat <4catcode@gmail.com> Date: Mon, 14 Aug 2023 07:49:37 +0800 Subject: [PATCH 3/6] improve docs --- src/main/java/org/stellar/sdk/SorobanDataBuilder.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java index e0942510c..faf9b0cf2 100644 --- a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java +++ b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java @@ -110,8 +110,8 @@ public SorobanDataBuilder setResources( * SorobanDataBuilder#setReadOnly(Collection)} and {@link * SorobanDataBuilder#setReadWrite(Collection)}. * - *

Passing {@code null} to either parameter will IGNORE the existing values. If you want to - * clear them, pass empty collection instead. + *

Passing {@code null} to either parameter will leave that portion of the footprint untouched. + * If you want to clear a portion of the footprint, pass an empty collection. * * @param readOnly the set of ledger keys to set in the read-only portion of the transaction's * sorobanData @@ -133,6 +133,9 @@ public SorobanDataBuilder setFootprint( /** * Sets the read-only portion of the storage access footprint to be a certain set of ledger keys. * + *

Passing {@code null} will leave that portion of the footprint untouched. If you want to + * clear a portion of the footprint, pass an empty collection. + * * @param readOnly the set of ledger keys to set in the read-only portion of the transaction's * sorobanData * @return this builder instance @@ -147,6 +150,9 @@ public SorobanDataBuilder setReadOnly(@Nullable Collection readOnly) /** * Sets the read-write portion of the storage access footprint to be a certain set of ledger keys. * + *

Passing {@code null} will leave that portion of the footprint untouched. If you want to + * clear a portion of the footprint, pass an empty collection. + * * @param readWrite the set of ledger keys to set in the read-write portion of the transaction's * sorobanData * @return this builder instance From 9acb4552df8cfdf2a2b462ed5e6cc4c9d431a066 Mon Sep 17 00:00:00 2001 From: overcat <4catcode@gmail.com> Date: Mon, 14 Aug 2023 08:07:25 +0800 Subject: [PATCH 4/6] improve docs --- .../java/org/stellar/sdk/TransactionBuilder.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/stellar/sdk/TransactionBuilder.java b/src/main/java/org/stellar/sdk/TransactionBuilder.java index 8459dd934..c1b900697 100644 --- a/src/main/java/org/stellar/sdk/TransactionBuilder.java +++ b/src/main/java/org/stellar/sdk/TransactionBuilder.java @@ -263,7 +263,13 @@ public static org.stellar.sdk.xdr.TimeBounds buildTimeBounds(long minTime, long } /** - * Sets Soroban data to the transaction. TODO: After adding SorobanServer, add more descriptions. + * Sets the transaction's internal Soroban transaction data (resources, footprint, etc.). + * + *

For non-contract(non-Soroban) transactions, this setting has no effect. In the case of + * Soroban transactions, this is either an instance of {@link SorobanTransactionData} or a + * base64-encoded string of said structure. This is usually obtained from the simulation response + * based on a transaction with a Soroban operation (e.g. {@link InvokeHostFunctionOperation}, + * providing necessary resource and storage footprint estimations for contract invocation. * * @param sorobanData Soroban data to set * @return Builder object so you can chain methods. @@ -274,7 +280,13 @@ public TransactionBuilder setSorobanData(SorobanTransactionData sorobanData) { } /** - * Sets Soroban data to the transaction. TODO: After adding SorobanServer, add more descriptions. + * Sets the transaction's internal Soroban transaction data (resources, footprint, etc.). + * + *

For non-contract(non-Soroban) transactions, this setting has no effect. In the case of + * Soroban transactions, this is either an instance of {@link SorobanTransactionData} or a + * base64-encoded string of said structure. This is usually obtained from the simulation response + * based on a transaction with a Soroban operation (e.g. {@link InvokeHostFunctionOperation}, + * providing necessary resource and storage footprint estimations for contract invocation. * * @param sorobanData Soroban data to set * @return Builder object so you can chain methods. From 4da8f8120aed245180142d4a3c7a4873b14d9a46 Mon Sep 17 00:00:00 2001 From: overcat <4catcode@gmail.com> Date: Mon, 14 Aug 2023 23:20:33 +0800 Subject: [PATCH 5/6] remove setFootprint --- .../org/stellar/sdk/SorobanDataBuilder.java | 27 ------------------- .../stellar/sdk/SorobanDataBuilderTest.java | 26 ++---------------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java index faf9b0cf2..acfcc8a35 100644 --- a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java +++ b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java @@ -103,33 +103,6 @@ public SorobanDataBuilder setResources( return this; } - /** - * Sets the storage access footprint to be a certain set of ledger keys. - * - *

You can also set each field explicitly via {@link - * SorobanDataBuilder#setReadOnly(Collection)} and {@link - * SorobanDataBuilder#setReadWrite(Collection)}. - * - *

Passing {@code null} to either parameter will leave that portion of the footprint untouched. - * If you want to clear a portion of the footprint, pass an empty collection. - * - * @param readOnly the set of ledger keys to set in the read-only portion of the transaction's - * sorobanData - * @param readWrite the set of ledger keys to set in the read-write portion of the transaction's - * sorobanData - * @return this builder instance - */ - public SorobanDataBuilder setFootprint( - @Nullable Collection readOnly, @Nullable Collection readWrite) { - if (readOnly != null) { - data.getResources().getFootprint().setReadOnly(readOnly.toArray(new LedgerKey[0])); - } - if (readWrite != null) { - data.getResources().getFootprint().setReadWrite(readWrite.toArray(new LedgerKey[0])); - } - return this; - } - /** * Sets the read-only portion of the storage access footprint to be a certain set of ledger keys. * diff --git a/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java b/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java index dc12f0e88..6d616d78d 100644 --- a/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java +++ b/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java @@ -99,22 +99,14 @@ public void testConstructorFromSorobanTransactionData() { @Test public void testSetProperties() { - SorobanTransactionData actualData0 = + SorobanTransactionData actualData = new SorobanDataBuilder() .setReadOnly(of(readOnly)) .setReadWrite(of(readWrite)) .setRefundableFee(5) .setResources(1, 2, 3, 4) .build(); - assertEquals(presetSorobanData, actualData0); - - SorobanTransactionData actualData1 = - new SorobanDataBuilder() - .setFootprint(of(readOnly), of(readWrite)) - .setRefundableFee(5) - .setResources(1, 2, 3, 4) - .build(); - assertEquals(presetSorobanData, actualData1); + assertEquals(presetSorobanData, actualData); } @Test @@ -136,20 +128,6 @@ public void testLeavesUntouchedFootprintsUntouched() { SorobanTransactionData data4 = new SorobanDataBuilder(presetSorobanData).setReadWrite(new ArrayList<>()).build(); assertArrayEquals(new LedgerKey[] {}, data4.getResources().getFootprint().getReadWrite()); - - SorobanTransactionData data5 = - new SorobanDataBuilder(presetSorobanData).setFootprint(null, null).build(); - assertArrayEquals( - new LedgerKey[] {readOnly}, data5.getResources().getFootprint().getReadOnly()); - assertArrayEquals( - new LedgerKey[] {readWrite}, data5.getResources().getFootprint().getReadWrite()); - - SorobanTransactionData data6 = - new SorobanDataBuilder(presetSorobanData) - .setFootprint(new ArrayList<>(), new ArrayList<>()) - .build(); - assertArrayEquals(new LedgerKey[] {}, data6.getResources().getFootprint().getReadOnly()); - assertArrayEquals(new LedgerKey[] {}, data6.getResources().getFootprint().getReadWrite()); } @Test From 9f5f23145e0d1eb4d814d17b408019b45eee1bba Mon Sep 17 00:00:00 2001 From: overcat <4catcode@gmail.com> Date: Mon, 14 Aug 2023 23:32:01 +0800 Subject: [PATCH 6/6] add resource builder --- .../org/stellar/sdk/SorobanDataBuilder.java | 36 +++++++++++++------ .../stellar/sdk/SorobanDataBuilderTest.java | 8 ++++- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java index acfcc8a35..23480f4ca 100644 --- a/src/main/java/org/stellar/sdk/SorobanDataBuilder.java +++ b/src/main/java/org/stellar/sdk/SorobanDataBuilder.java @@ -3,6 +3,9 @@ import java.io.IOException; import java.util.Collection; import javax.annotation.Nullable; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; import org.stellar.sdk.xdr.ExtensionPoint; import org.stellar.sdk.xdr.Int64; import org.stellar.sdk.xdr.LedgerFootprint; @@ -87,19 +90,18 @@ public SorobanDataBuilder setRefundableFee(long fee) { *

You should almost NEVER need this, as its often generated/provided to you by transaction * simulation/preflight from a Soroban RPC server. * - * @param cpuInstructions number of CPU instructions (uint32) - * @param readBytes number of bytes being read (uint32) - * @param writeBytes number of bytes being written (uint32) - * @param metadataBytes number of extended metadata bytes (uint32) + * @param resources the resource metrics to set * @return this builder instance */ - public SorobanDataBuilder setResources( - long cpuInstructions, long readBytes, long writeBytes, long metadataBytes) { - data.getResources().setInstructions(new Uint32(new XdrUnsignedInteger(cpuInstructions))); - data.getResources().setReadBytes(new Uint32(new XdrUnsignedInteger(readBytes))); - data.getResources().setWriteBytes(new Uint32(new XdrUnsignedInteger(writeBytes))); + public SorobanDataBuilder setResources(Resources resources) { data.getResources() - .setExtendedMetaDataSizeBytes(new Uint32(new XdrUnsignedInteger(metadataBytes))); + .setInstructions(new Uint32(new XdrUnsignedInteger(resources.getCpuInstructions()))); + data.getResources().setReadBytes(new Uint32(new XdrUnsignedInteger(resources.getReadBytes()))); + data.getResources() + .setWriteBytes(new Uint32(new XdrUnsignedInteger(resources.getWriteBytes()))); + data.getResources() + .setExtendedMetaDataSizeBytes( + new Uint32(new XdrUnsignedInteger(resources.getMetadataBytes()))); return this; } @@ -147,4 +149,18 @@ public SorobanTransactionData build() { throw new IllegalArgumentException("Copy SorobanData failed, please report this bug.", e); } } + + /** Represents the resource metrics of the Soroban data. */ + @Builder(toBuilder = true) + @Value + public static class Resources { + // number of CPU instructions (uint32) + @NonNull Long cpuInstructions; + // number of bytes being read (uint32) + @NonNull Long readBytes; + // number of bytes being written (uint32) + @NonNull Long writeBytes; + // number of extended metadata bytes (uint32) + @NonNull Long metadataBytes; + } } diff --git a/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java b/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java index 6d616d78d..f55a4ae74 100644 --- a/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java +++ b/src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java @@ -104,7 +104,13 @@ public void testSetProperties() { .setReadOnly(of(readOnly)) .setReadWrite(of(readWrite)) .setRefundableFee(5) - .setResources(1, 2, 3, 4) + .setResources( + new SorobanDataBuilder.Resources.ResourcesBuilder() + .cpuInstructions(1L) + .readBytes(2L) + .writeBytes(3L) + .metadataBytes(4L) + .build()) .build(); assertEquals(presetSorobanData, actualData); }