-
Notifications
You must be signed in to change notification settings - Fork 159
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add SorobanDataBuilder to prepare sorobanData easily. (#509)
- Loading branch information
Showing
5 changed files
with
363 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package org.stellar.sdk; | ||
|
||
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; | ||
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. | ||
* | ||
* <p>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 { | ||
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. | ||
* | ||
* <p>You should almost NEVER need this, as its often generated/provided to you by transaction | ||
* simulation/preflight from a Soroban RPC server. | ||
* | ||
* @param resources the resource metrics to set | ||
* @return this builder instance | ||
*/ | ||
public SorobanDataBuilder setResources(Resources resources) { | ||
data.getResources() | ||
.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; | ||
} | ||
|
||
/** | ||
* Sets the read-only portion of the storage access footprint to be a certain set of ledger keys. | ||
* | ||
* <p>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 | ||
*/ | ||
public SorobanDataBuilder setReadOnly(@Nullable Collection<LedgerKey> 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. | ||
* | ||
* <p>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 | ||
*/ | ||
public SorobanDataBuilder setReadWrite(@Nullable Collection<LedgerKey> readWrite) { | ||
if (readWrite != null) { | ||
data.getResources().getFootprint().setReadWrite(readWrite.toArray(new LedgerKey[0])); | ||
} | ||
return this; | ||
} | ||
|
||
/** | ||
* @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); | ||
} | ||
} | ||
|
||
/** 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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
src/test/java/org/stellar/sdk/SorobanDataBuilderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
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 actualData = | ||
new SorobanDataBuilder() | ||
.setReadOnly(of(readOnly)) | ||
.setReadWrite(of(readWrite)) | ||
.setRefundableFee(5) | ||
.setResources( | ||
new SorobanDataBuilder.Resources.ResourcesBuilder() | ||
.cpuInstructions(1L) | ||
.readBytes(2L) | ||
.writeBytes(3L) | ||
.metadataBytes(4L) | ||
.build()) | ||
.build(); | ||
assertEquals(presetSorobanData, actualData); | ||
} | ||
|
||
@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()); | ||
} | ||
|
||
@Test | ||
public void testBuildCopy() { | ||
SorobanTransactionData actualData = new SorobanDataBuilder(presetSorobanData).build(); | ||
assertEquals(presetSorobanData, actualData); | ||
assertNotSame(presetSorobanData, actualData); | ||
} | ||
} |
Oops, something went wrong.